package database import ( "context" "database/sql" "time" "github.com/google/uuid" ) type DB struct { *sql.DB } func New(db *sql.DB) *DB { return &DB{DB: db} } type User struct { ID uuid.UUID Email string DisplayName string CreatedAt time.Time LastLoginAt *time.Time } type Session struct { ID uuid.UUID UserID uuid.UUID ExpiresAt time.Time RevokedAt *time.Time } type Organization struct { ID uuid.UUID Name string Slug string CreatedAt time.Time } type Membership struct { UserID uuid.UUID OrgID uuid.UUID Role string CreatedAt time.Time } type Activity struct { ID uuid.UUID UserID uuid.UUID OrgID uuid.UUID FileID *string Action string Metadata map[string]interface{} Timestamp time.Time } func (db *DB) GetOrCreateUser(ctx context.Context, sub, email, name string) (*User, error) { var user User err := db.QueryRowContext(ctx, ` INSERT INTO users (id, email, display_name) VALUES (gen_random_uuid(), $1, $2) ON CONFLICT (email) DO UPDATE SET display_name = EXCLUDED.display_name, last_login_at = NOW() RETURNING id, email, display_name, created_at, last_login_at `, email, name).Scan(&user.ID, &user.Email, &user.DisplayName, &user.CreatedAt, &user.LastLoginAt) if err != nil { return nil, err } return &user, nil } func (db *DB) CreateSession(ctx context.Context, userID uuid.UUID, expiresAt time.Time) (*Session, error) { var session Session err := db.QueryRowContext(ctx, ` INSERT INTO sessions (user_id, expires_at) VALUES ($1, $2) RETURNING id, user_id, expires_at, revoked_at `, userID, expiresAt).Scan(&session.ID, &session.UserID, &session.ExpiresAt, &session.RevokedAt) if err != nil { return nil, err } return &session, nil } func (db *DB) GetSession(ctx context.Context, sessionID uuid.UUID) (*Session, error) { var session Session err := db.QueryRowContext(ctx, ` SELECT id, user_id, expires_at, revoked_at FROM sessions WHERE id = $1 `, sessionID).Scan(&session.ID, &session.UserID, &session.ExpiresAt, &session.RevokedAt) if err != nil { return nil, err } return &session, nil } func (db *DB) GetUserOrganizations(ctx context.Context, userID uuid.UUID) ([]Organization, error) { rows, err := db.QueryContext(ctx, ` SELECT o.id, o.name, o.slug, o.created_at FROM organizations o JOIN memberships m ON o.id = m.org_id WHERE m.user_id = $1 `, userID) if err != nil { return nil, err } defer rows.Close() var orgs []Organization for rows.Next() { var org Organization if err := rows.Scan(&org.ID, &org.Name, &org.Slug, &org.CreatedAt); err != nil { return nil, err } orgs = append(orgs, org) } return orgs, rows.Err() } func (db *DB) GetUserMembership(ctx context.Context, userID, orgID uuid.UUID) (*Membership, error) { var membership Membership err := db.QueryRowContext(ctx, ` SELECT user_id, org_id, role, created_at FROM memberships WHERE user_id = $1 AND org_id = $2 `, userID, orgID).Scan(&membership.UserID, &membership.OrgID, &membership.Role, &membership.CreatedAt) if err != nil { return nil, err } return &membership, nil } func (db *DB) CreateOrg(ctx context.Context, name, slug string) (*Organization, error) { var org Organization err := db.QueryRowContext(ctx, ` INSERT INTO organizations (name, slug) VALUES ($1, $2) RETURNING id, name, slug, created_at `, name, slug).Scan(&org.ID, &org.Name, &org.Slug, &org.CreatedAt) if err != nil { return nil, err } return &org, nil } func (db *DB) AddMembership(ctx context.Context, userID, orgID uuid.UUID, role string) error { _, err := db.ExecContext(ctx, ` INSERT INTO memberships (user_id, org_id, role) VALUES ($1, $2, $3) `, userID, orgID, role) return err } func (db *DB) LogActivity(ctx context.Context, userID, orgID uuid.UUID, fileID *string, action string, metadata map[string]interface{}) error { _, err := db.ExecContext(ctx, ` INSERT INTO activities (user_id, org_id, file_id, action, metadata) VALUES ($1, $2, $3, $4, $5) `, userID, orgID, fileID, action, metadata) return err } func (db *DB) GetOrgActivities(ctx context.Context, orgID uuid.UUID, limit int) ([]Activity, error) { rows, err := db.QueryContext(ctx, ` SELECT id, user_id, org_id, file_id, action, metadata, timestamp FROM activities WHERE org_id = $1 ORDER BY timestamp DESC LIMIT $2 `, orgID, limit) if err != nil { return nil, err } defer rows.Close() var activities []Activity for rows.Next() { var a Activity err := rows.Scan(&a.ID, &a.UserID, &a.OrgID, &a.FileID, &a.Action, &a.Metadata, &a.Timestamp) if err != nil { return nil, err } activities = append(activities, a) } return activities, rows.Err() } func (db *DB) GetOrgMembers(ctx context.Context, orgID uuid.UUID) ([]Membership, error) { rows, err := db.QueryContext(ctx, ` SELECT user_id, org_id, role, created_at FROM memberships WHERE org_id = $1 `, orgID) if err != nil { return nil, err } defer rows.Close() var memberships []Membership for rows.Next() { var m Membership err := rows.Scan(&m.UserID, &m.OrgID, &m.Role, &m.CreatedAt) if err != nil { return nil, err } memberships = append(memberships, m) } return memberships, rows.Err() } func (db *DB) UpdateMemberRole(ctx context.Context, orgID, userID uuid.UUID, role string) error { _, err := db.ExecContext(ctx, ` UPDATE memberships SET role = $1 WHERE org_id = $2 AND user_id = $3 `, role, orgID, userID) return err }