240 lines
8.9 KiB
Python
240 lines
8.9 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Master User Creation Script for Positeasy Canteen Automation System.
|
||
|
||
This script creates or updates a master administrator user in the database
|
||
with username 'admin' and password 'admin', and synchronizes the application configuration.
|
||
"""
|
||
|
||
import sys
|
||
import os
|
||
import re
|
||
import argparse
|
||
import bcrypt
|
||
import psycopg2
|
||
|
||
# Default Configuration
|
||
DEFAULT_USERNAME = "admin"
|
||
DEFAULT_PASSWORD = "admin"
|
||
DEFAULT_NAME = "Admin Master"
|
||
DEFAULT_ROLE = "MASTER"
|
||
|
||
DEFAULT_DB_HOST = "localhost"
|
||
DEFAULT_DB_PORT = 5432
|
||
DEFAULT_DB_NAME = "positeasy"
|
||
DEFAULT_DB_USER = "postgres"
|
||
DEFAULT_DB_PASS = "sidharth"
|
||
|
||
ALL_PERMISSIONS = [
|
||
"dashboard",
|
||
"sale",
|
||
"customers",
|
||
"purchases",
|
||
"inventory",
|
||
"expense",
|
||
"reports",
|
||
"stores",
|
||
"table",
|
||
"wallet",
|
||
"promotions",
|
||
"feedback"
|
||
]
|
||
|
||
def hash_password(password: str) -> str:
|
||
"""Hash password using bcrypt compatible with Spring Security BCryptPasswordEncoder."""
|
||
salt = bcrypt.gensalt(rounds=10)
|
||
hashed = bcrypt.hashpw(password.encode('utf-8'), salt)
|
||
return hashed.decode('utf-8')
|
||
|
||
def parse_application_properties(prop_path: str) -> dict:
|
||
"""Parse db credentials from backend application.properties if available."""
|
||
config = {}
|
||
if not os.path.exists(prop_path):
|
||
return config
|
||
|
||
with open(prop_path, 'r', encoding='utf-8') as f:
|
||
for line in f:
|
||
line = line.strip()
|
||
if line and not line.startswith('#') and '=' in line:
|
||
key, value = line.split('=', 1)
|
||
config[key.strip()] = value.strip()
|
||
return config
|
||
|
||
def update_application_properties(prop_path: str, username: str, password: str) -> bool:
|
||
"""Update app.master.username and app.master.password in application.properties."""
|
||
if not os.path.exists(prop_path):
|
||
print(f"⚠️ [Config] File not found: {prop_path}")
|
||
return False
|
||
|
||
with open(prop_path, 'r', encoding='utf-8') as f:
|
||
content = f.read()
|
||
|
||
# Update or add app.master.username and app.master.password
|
||
new_user_line = f"app.master.username=${{MASTER_USER:{username}}}"
|
||
new_pass_line = f"app.master.password=${{MASTER_PASSWORD:{password}}}"
|
||
|
||
if re.search(r'^app\.master\.username=.*$', content, flags=re.MULTILINE):
|
||
content = re.sub(r'^app\.master\.username=.*$', new_user_line, content, flags=re.MULTILINE)
|
||
else:
|
||
content += f"\n{new_user_line}"
|
||
|
||
if re.search(r'^app\.master\.password=.*$', content, flags=re.MULTILINE):
|
||
content = re.sub(r'^app\.master\.password=.*$', new_pass_line, content, flags=re.MULTILINE)
|
||
else:
|
||
content += f"\n{new_pass_line}"
|
||
|
||
with open(prop_path, 'w', encoding='utf-8') as f:
|
||
f.write(content)
|
||
|
||
print(f"✅ [Config] Updated {prop_path} with default master credentials ({username}/{password}).")
|
||
return True
|
||
|
||
def update_frontend_demo_credentials(app_tsx_path: str, username: str, password: str) -> bool:
|
||
"""Update UI demo credentials displayed in api-dashboard if file exists."""
|
||
if not os.path.exists(app_tsx_path):
|
||
return False
|
||
|
||
with open(app_tsx_path, 'r', encoding='utf-8') as f:
|
||
content = f.read()
|
||
|
||
old_pattern = r'Default Admin:\s*<strong>[^<]*</strong>\s*/\s*Password:\s*<strong>[^<]*</strong>'
|
||
new_text = f'Default Admin: <strong>{username}</strong> / Password: <strong>{password}</strong>'
|
||
|
||
if re.search(old_pattern, content):
|
||
content = re.sub(old_pattern, new_text, content)
|
||
with open(app_tsx_path, 'w', encoding='utf-8') as f:
|
||
f.write(content)
|
||
print(f"✅ [UI] Updated demo credentials in {app_tsx_path}.")
|
||
return True
|
||
return False
|
||
|
||
def create_or_update_master_user(db_params: dict, username: str, password: str, name: str) -> bool:
|
||
"""Insert or update master user in PostgreSQL database."""
|
||
print(f"\n🔌 Connecting to PostgreSQL database '{db_params['dbname']}' on {db_params['host']}:{db_params['port']}...")
|
||
|
||
try:
|
||
conn = psycopg2.connect(**db_params)
|
||
conn.autocommit = False
|
||
cur = conn.cursor()
|
||
|
||
# Check if system_users table exists
|
||
cur.execute("""
|
||
SELECT EXISTS (
|
||
SELECT FROM information_schema.tables
|
||
WHERE table_name = 'system_users'
|
||
);
|
||
""")
|
||
table_exists = cur.fetchone()[0]
|
||
|
||
if not table_exists:
|
||
print("❌ Table 'system_users' does not exist in the database. Has backend schema initialized?")
|
||
conn.close()
|
||
return False
|
||
|
||
# Check if master user with this username (stored in email column) exists
|
||
cur.execute("SELECT id, name, role FROM system_users WHERE email = %s;", (username,))
|
||
row = cur.fetchone()
|
||
|
||
hashed_pass = hash_password(password)
|
||
|
||
if row:
|
||
user_id = row[0]
|
||
print(f"🔄 Existing master user found (ID: {user_id}). Updating credentials...")
|
||
cur.execute("""
|
||
UPDATE system_users
|
||
SET name = %s, password = %s, role = %s, view_only = false
|
||
WHERE id = %s;
|
||
""", (name, hashed_pass, DEFAULT_ROLE, user_id))
|
||
else:
|
||
print(f"➕ Creating new master user '{username}'...")
|
||
cur.execute("""
|
||
INSERT INTO system_users (name, email, password, role, view_only)
|
||
VALUES (%s, %s, %s, %s, false)
|
||
RETURNING id;
|
||
""", (name, username, hashed_pass, DEFAULT_ROLE))
|
||
user_id = cur.fetchone()[0]
|
||
|
||
# Reset permissions for this user
|
||
cur.execute("DELETE FROM system_user_permissions WHERE user_id = %s;", (user_id,))
|
||
for perm in ALL_PERMISSIONS:
|
||
cur.execute("""
|
||
INSERT INTO system_user_permissions (user_id, permission)
|
||
VALUES (%s, %s);
|
||
""", (user_id, perm))
|
||
|
||
conn.commit()
|
||
|
||
# Verification query
|
||
cur.execute("""
|
||
SELECT u.id, u.name, u.email, u.role, count(p.permission)
|
||
FROM system_users u
|
||
LEFT JOIN system_user_permissions p ON u.id = p.user_id
|
||
WHERE u.id = %s
|
||
GROUP BY u.id, u.name, u.email, u.role;
|
||
""", (user_id,))
|
||
ver = cur.fetchone()
|
||
cur.close()
|
||
conn.close()
|
||
|
||
print("\n" + "=" * 50)
|
||
print("🎉 MASTER USER CREATED / UPDATED SUCCESSFULLY")
|
||
print("=" * 50)
|
||
print(f" User ID: {ver[0]}")
|
||
print(f" Name: {ver[1]}")
|
||
print(f" Username: {ver[2]}")
|
||
print(f" Password: {password}")
|
||
print(f" Role: {ver[3]}")
|
||
print(f" Permissions: {ver[4]} granted ({', '.join(ALL_PERMISSIONS[:4])}...)")
|
||
print("=" * 50 + "\n")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"❌ Database error: {e}")
|
||
return False
|
||
|
||
def main():
|
||
parser = argparse.ArgumentParser(description="Create or update master user for Positeasy Canteen Automation.")
|
||
parser.add_argument("-u", "--username", default=DEFAULT_USERNAME, help="Master username (default: admin)")
|
||
parser.add_argument("-p", "--password", default=DEFAULT_PASSWORD, help="Master password (default: admin)")
|
||
parser.add_argument("-n", "--name", default=DEFAULT_NAME, help="Display name for master user")
|
||
parser.add_argument("--host", default=DEFAULT_DB_HOST, help="PostgreSQL host (default: localhost)")
|
||
parser.add_argument("--port", type=int, default=DEFAULT_DB_PORT, help="PostgreSQL port (default: 5432)")
|
||
parser.add_argument("--dbname", default=DEFAULT_DB_NAME, help="PostgreSQL database name (default: positeasy)")
|
||
parser.add_argument("--dbuser", default=DEFAULT_DB_USER, help="PostgreSQL database user (default: postgres)")
|
||
parser.add_argument("--dbpass", default=DEFAULT_DB_PASS, help="PostgreSQL database password")
|
||
|
||
args = parser.parse_args()
|
||
|
||
project_root = os.path.dirname(os.path.abspath(__file__))
|
||
prop_path = os.path.join(project_root, "backend", "src", "main", "resources", "application.properties")
|
||
app_tsx_path = os.path.join(project_root, "api-dashboard", "src", "App.tsx")
|
||
|
||
db_params = {
|
||
"host": args.host,
|
||
"port": args.port,
|
||
"dbname": args.dbname,
|
||
"user": args.dbuser,
|
||
"password": args.dbpass
|
||
}
|
||
|
||
print("🚀 Starting Master User setup script...")
|
||
|
||
# 1. Database creation / update
|
||
db_success = create_or_update_master_user(db_params, args.username, args.password, args.name)
|
||
|
||
# 2. Sync application.properties
|
||
update_application_properties(prop_path, args.username, args.password)
|
||
|
||
# 3. Sync frontend dashboard UI
|
||
update_frontend_demo_credentials(app_tsx_path, args.username, args.password)
|
||
|
||
if db_success:
|
||
print("✨ All steps completed successfully! You can now log in with:")
|
||
print(f" Username: {args.username}")
|
||
print(f" Password: {args.password}")
|
||
else:
|
||
print("⚠️ Database operation failed or incomplete. Check database connectivity.")
|
||
|
||
if __name__ == "__main__":
|
||
main()
|