245 lines
7.7 KiB
Go
245 lines
7.7 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/mish/dealspace/lib"
|
|
)
|
|
|
|
// seedSuperAdmins ensures the super admin accounts always exist.
|
|
// Called on every startup (not just when SEED_DEMO=true).
|
|
func seedSuperAdmins(db *lib.DB) {
|
|
type admin struct {
|
|
email string
|
|
name string
|
|
orgID string
|
|
orgName string
|
|
}
|
|
admins := []admin{
|
|
{"michael@muskepo.com", "Michael", "muskepo", "Muskepo"},
|
|
{"johan@jongsma.me", "Johan", "muskepo", "Muskepo"},
|
|
}
|
|
|
|
for _, a := range admins {
|
|
existing, err := lib.UserByEmail(db, a.email)
|
|
if err != nil {
|
|
log.Printf("seed: check %s: %v", a.email, err)
|
|
continue
|
|
}
|
|
if existing != nil {
|
|
// User exists, ensure they have super_admin access
|
|
ensureSuperAdmin(db, existing.UserID)
|
|
continue
|
|
}
|
|
|
|
now := time.Now().UnixMilli()
|
|
userID := uuid.New().String()
|
|
user := &lib.User{
|
|
UserID: userID,
|
|
Email: a.email,
|
|
Name: a.name,
|
|
Password: "", // passwordless auth — no password needed
|
|
OrgID: a.orgID,
|
|
OrgName: a.orgName,
|
|
Active: true,
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
if err := lib.UserCreate(db, user); err != nil {
|
|
log.Printf("seed: create %s: %v", a.email, err)
|
|
continue
|
|
}
|
|
log.Printf("seed: created super admin %s", a.email)
|
|
ensureSuperAdmin(db, userID)
|
|
}
|
|
}
|
|
|
|
// ensureSuperAdmin grants super_admin role on the special "*" project (platform-wide).
|
|
func ensureSuperAdmin(db *lib.DB, userID string) {
|
|
// Check if already has super_admin
|
|
isSuperAdmin, _ := lib.IsSuperAdmin(db, userID)
|
|
if isSuperAdmin {
|
|
return
|
|
}
|
|
|
|
now := time.Now().UnixMilli()
|
|
access := &lib.Access{
|
|
ID: uuid.New().String(),
|
|
ProjectID: "*", // platform-wide
|
|
UserID: userID,
|
|
Role: lib.RoleSuperAdmin,
|
|
Ops: "rwdm",
|
|
CanGrant: true,
|
|
GrantedBy: "system",
|
|
GrantedAt: now,
|
|
}
|
|
if err := lib.AccessGrant(db, access); err != nil {
|
|
log.Printf("seed: grant super_admin to %s: %v", userID, err)
|
|
return
|
|
}
|
|
log.Printf("seed: granted super_admin to %s", userID)
|
|
}
|
|
|
|
func seedDemoData(db *lib.DB, cfg *lib.Config) {
|
|
count, err := lib.UserCount(db)
|
|
if err != nil {
|
|
log.Printf("seed: cannot check user count: %v", err)
|
|
return
|
|
}
|
|
// Only seed demo data if no users exist (besides super admins)
|
|
if count > 2 {
|
|
log.Printf("seed: users already exist, skipping demo data")
|
|
return
|
|
}
|
|
|
|
log.Printf("seed: creating demo data...")
|
|
now := time.Now().UnixMilli()
|
|
|
|
// Create demo user: admin@demo.com (passwordless — no password needed)
|
|
adminID := uuid.New().String()
|
|
admin := &lib.User{
|
|
UserID: adminID,
|
|
Email: "admin@demo.com",
|
|
Name: "Demo Admin",
|
|
Password: "",
|
|
OrgID: "demo-org",
|
|
OrgName: "Demo Investment Bank",
|
|
Active: true,
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
if err := lib.UserCreate(db, admin); err != nil {
|
|
log.Printf("seed: create admin: %v", err)
|
|
return
|
|
}
|
|
log.Printf("seed: created admin user admin@demo.com")
|
|
|
|
// Create demo project: TechCorp Acquisition
|
|
projectID := uuid.New().String()
|
|
projectData := `{"name":"TechCorp Acquisition","deal_type":"sell_side","status":"active"}`
|
|
|
|
key, err := lib.DeriveProjectKey(cfg.MasterKey, projectID)
|
|
if err != nil {
|
|
log.Printf("seed: derive key: %v", err)
|
|
return
|
|
}
|
|
|
|
summaryPacked, _ := lib.Pack(key, "TechCorp Acquisition")
|
|
dataPacked, _ := lib.Pack(key, projectData)
|
|
|
|
_, err = db.Conn.Exec(
|
|
`INSERT INTO entries (entry_id, project_id, parent_id, type, depth,
|
|
search_key, search_key2, summary, data, stage,
|
|
assignee_id, return_to_id, origin_id,
|
|
version, key_version, created_at, updated_at, created_by)
|
|
VALUES (?,?,?,?,?, ?,?,?,?,?, ?,?,?, ?,?,?,?,?)`,
|
|
projectID, projectID, "", lib.TypeProject, 0,
|
|
nil, nil, summaryPacked, dataPacked, lib.StagePreDataroom,
|
|
"", "", "",
|
|
1, 1, now, now, adminID,
|
|
)
|
|
if err != nil {
|
|
log.Printf("seed: create project: %v", err)
|
|
return
|
|
}
|
|
|
|
// Grant ib_admin access
|
|
accessID := uuid.New().String()
|
|
_, err = db.Conn.Exec(
|
|
`INSERT INTO access (id, project_id, workstream_id, user_id, role, ops, can_grant, granted_by, granted_at)
|
|
VALUES (?,?,?,?,?,?,?,?,?)`,
|
|
accessID, projectID, nil, adminID, lib.RoleIBAdmin, "rwdm", 1, adminID, now,
|
|
)
|
|
if err != nil {
|
|
log.Printf("seed: grant access: %v", err)
|
|
return
|
|
}
|
|
|
|
// Also grant super admins access to the demo project
|
|
grantSuperAdminsToProject(db, projectID, now)
|
|
|
|
// Create workstreams: Finance, Legal, IT
|
|
workstreams := []string{"Finance", "Legal", "IT"}
|
|
wsIDs := make([]string, len(workstreams))
|
|
for i, name := range workstreams {
|
|
wsID := uuid.New().String()
|
|
wsIDs[i] = wsID
|
|
wsSummary, _ := lib.Pack(key, name)
|
|
wsData, _ := lib.Pack(key, fmt.Sprintf(`{"name":"%s"}`, name))
|
|
|
|
_, err = db.Conn.Exec(
|
|
`INSERT INTO entries (entry_id, project_id, parent_id, type, depth,
|
|
search_key, search_key2, summary, data, stage,
|
|
assignee_id, return_to_id, origin_id,
|
|
version, key_version, created_at, updated_at, created_by)
|
|
VALUES (?,?,?,?,?, ?,?,?,?,?, ?,?,?, ?,?,?,?,?)`,
|
|
wsID, projectID, projectID, lib.TypeWorkstream, 1,
|
|
nil, nil, wsSummary, wsData, lib.StagePreDataroom,
|
|
"", "", "",
|
|
1, 1, now, now, adminID,
|
|
)
|
|
if err != nil {
|
|
log.Printf("seed: create workstream %s: %v", name, err)
|
|
}
|
|
}
|
|
log.Printf("seed: created 3 workstreams")
|
|
|
|
// Create 5 sample requests in Finance workstream
|
|
type seedReq struct {
|
|
ref, title, body, priority, due string
|
|
}
|
|
requests := []seedReq{
|
|
{"FIN-001", "Audited financial statements FY2023-2025", "Provide complete audited financial statements for fiscal years 2023, 2024, and 2025.", "high", "2026-03-15"},
|
|
{"FIN-002", "Revenue breakdown by customer (top 20)", "Break down revenue by top 20 customers showing percentage of total revenue.", "high", "2026-03-15"},
|
|
{"FIN-003", "Cap table and option pool details", "Provide current capitalization table including all share classes and warrants.", "normal", "2026-03-20"},
|
|
{"FIN-004", "AR/AP aging reports", "Accounts receivable and accounts payable aging reports as of most recent month-end.", "normal", "2026-03-25"},
|
|
{"FIN-005", "Revenue recognition policy", "Document your revenue recognition policy including any changes in past 3 fiscal years.", "low", "2026-03-30"},
|
|
}
|
|
|
|
for _, req := range requests {
|
|
reqID := uuid.New().String()
|
|
reqData := fmt.Sprintf(`{"title":"%s","body":"%s","priority":"%s","due_date":"%s","status":"open","ref":"%s"}`,
|
|
req.title, req.body, req.priority, req.due, req.ref)
|
|
|
|
reqSummary, _ := lib.Pack(key, req.title)
|
|
reqDataPacked, _ := lib.Pack(key, reqData)
|
|
|
|
_, err = db.Conn.Exec(
|
|
`INSERT INTO entries (entry_id, project_id, parent_id, type, depth,
|
|
search_key, search_key2, summary, data, stage,
|
|
assignee_id, return_to_id, origin_id,
|
|
version, key_version, created_at, updated_at, created_by)
|
|
VALUES (?,?,?,?,?, ?,?,?,?,?, ?,?,?, ?,?,?,?,?)`,
|
|
reqID, projectID, wsIDs[0], lib.TypeRequest, 3,
|
|
nil, nil, reqSummary, reqDataPacked, lib.StagePreDataroom,
|
|
adminID, "", "",
|
|
1, 1, now, now, adminID,
|
|
)
|
|
if err != nil {
|
|
log.Printf("seed: create request %s: %v", req.ref, err)
|
|
}
|
|
}
|
|
log.Printf("seed: created 5 sample requests in Finance workstream")
|
|
log.Printf("seed: done! Login with admin@demo.com, michael@muskepo.com, or johan@jongsma.me")
|
|
}
|
|
|
|
// grantSuperAdminsToProject gives super admin users ib_admin access on a project.
|
|
func grantSuperAdminsToProject(db *lib.DB, projectID string, now int64) {
|
|
emails := []string{"michael@muskepo.com", "johan@jongsma.me"}
|
|
for _, email := range emails {
|
|
user, err := lib.UserByEmail(db, email)
|
|
if err != nil || user == nil {
|
|
continue
|
|
}
|
|
accessID := uuid.New().String()
|
|
_, _ = db.Conn.Exec(
|
|
`INSERT OR IGNORE INTO access (id, project_id, workstream_id, user_id, role, ops, can_grant, granted_by, granted_at)
|
|
VALUES (?,?,?,?,?,?,?,?,?)`,
|
|
accessID, projectID, nil, user.UserID, lib.RoleIBAdmin, "rwdm", 1, "system", now,
|
|
)
|
|
}
|
|
}
|