feat: Phase 0-2 complete — auth, machines, equipment parts with full CRUD
Multi-tenant factory inspection system (SpiFox, Enkid, Alpet): - FastAPI backend with JWT auth, PostgreSQL (asyncpg) - Next.js 16 frontend with App Router, SWR data fetching - Machines CRUD with equipment parts management - Part lifecycle tracking (hours/count/date) with counters - Partial unique index for soft-delete support - 24 pytest tests passing, E2E verified Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
This commit is contained in:
115
scripts/seed.py
Normal file
115
scripts/seed.py
Normal file
@@ -0,0 +1,115 @@
|
||||
import asyncio
|
||||
import os
|
||||
import sys
|
||||
import uuid
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
|
||||
|
||||
from src.database.models import Base, User, Tenant
|
||||
from src.auth.password import hash_password
|
||||
|
||||
DATABASE_URL = os.getenv(
|
||||
"DATABASE_URL",
|
||||
"postgresql+asyncpg://factoryops:factoryops@localhost:5432/factoryops_v2",
|
||||
)
|
||||
|
||||
TENANTS = [
|
||||
{
|
||||
"id": "spifox",
|
||||
"name": "SpiFox",
|
||||
"industry_type": "semiconductor",
|
||||
},
|
||||
{
|
||||
"id": "enkid",
|
||||
"name": "Enkid",
|
||||
"industry_type": "manufacturing",
|
||||
},
|
||||
{
|
||||
"id": "alpet",
|
||||
"name": "Alpet",
|
||||
"industry_type": "chemical",
|
||||
},
|
||||
]
|
||||
|
||||
SUPERADMIN = {
|
||||
"email": "admin@factoryops.com",
|
||||
"password": "admin1234",
|
||||
"name": "Super Admin",
|
||||
"role": "superadmin",
|
||||
"tenant_id": None,
|
||||
}
|
||||
|
||||
TENANT_ADMINS = [
|
||||
{
|
||||
"email": "admin@spifox.com",
|
||||
"password": "spifox1234",
|
||||
"name": "SpiFox Admin",
|
||||
"role": "tenant_admin",
|
||||
"tenant_id": "spifox",
|
||||
},
|
||||
{
|
||||
"email": "admin@enkid.com",
|
||||
"password": "enkid1234",
|
||||
"name": "Enkid Admin",
|
||||
"role": "tenant_admin",
|
||||
"tenant_id": "enkid",
|
||||
},
|
||||
{
|
||||
"email": "admin@alpet.com",
|
||||
"password": "alpet1234",
|
||||
"name": "Alpet Admin",
|
||||
"role": "tenant_admin",
|
||||
"tenant_id": "alpet",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
async def seed():
|
||||
engine = create_async_engine(DATABASE_URL, echo=False)
|
||||
session_factory = async_sessionmaker(
|
||||
engine, class_=AsyncSession, expire_on_commit=False
|
||||
)
|
||||
|
||||
async with session_factory() as db:
|
||||
for t in TENANTS:
|
||||
existing = await db.execute(select(Tenant).where(Tenant.id == t["id"]))
|
||||
if existing.scalar_one_or_none():
|
||||
print(f" Tenant '{t['id']}' already exists, skipping")
|
||||
continue
|
||||
db.add(Tenant(**t))
|
||||
print(f" + Tenant '{t['id']}' ({t['name']})")
|
||||
await db.commit()
|
||||
|
||||
all_users = [SUPERADMIN] + TENANT_ADMINS
|
||||
for u in all_users:
|
||||
existing = await db.execute(select(User).where(User.email == u["email"]))
|
||||
if existing.scalar_one_or_none():
|
||||
print(f" User '{u['email']}' already exists, skipping")
|
||||
continue
|
||||
db.add(
|
||||
User(
|
||||
id=uuid.uuid4(),
|
||||
email=u["email"],
|
||||
password_hash=hash_password(u["password"]),
|
||||
name=u["name"],
|
||||
role=u["role"],
|
||||
tenant_id=u["tenant_id"],
|
||||
)
|
||||
)
|
||||
print(f" + User '{u['email']}' (role={u['role']})")
|
||||
await db.commit()
|
||||
|
||||
await engine.dispose()
|
||||
print("\nSeed complete.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Seeding FactoryOps v2 database...\n")
|
||||
asyncio.run(seed())
|
||||
Reference in New Issue
Block a user