221 lines
5.4 KiB
Go
221 lines
5.4 KiB
Go
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
|
|
}
|