134 lines
5.3 KiB
Go
134 lines
5.3 KiB
Go
package database
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"strings"
|
|
"time"
|
|
|
|
"juhe-factory/api/internal/config"
|
|
|
|
"gorm.io/driver/postgres"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/logger"
|
|
)
|
|
|
|
func Open(cfg config.Config) (*gorm.DB, *sql.DB, error) {
|
|
logLevel := logger.Warn
|
|
if cfg.Debug {
|
|
logLevel = logger.Info
|
|
}
|
|
|
|
databaseURL := cfg.DatabaseURL
|
|
if !strings.Contains(strings.ToLower(databaseURL), "timezone=") {
|
|
separator := "?"
|
|
if strings.Contains(databaseURL, " ") {
|
|
separator = " "
|
|
}
|
|
if strings.Contains(databaseURL, "?") {
|
|
separator = "&"
|
|
}
|
|
databaseURL += separator + "TimeZone=Asia%2FShanghai"
|
|
}
|
|
db, err := gorm.Open(postgres.Open(databaseURL), &gorm.Config{
|
|
Logger: logger.Default.LogMode(logLevel),
|
|
})
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
sqlDB, err := db.DB()
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
sqlDB.SetMaxOpenConns(20)
|
|
sqlDB.SetMaxIdleConns(5)
|
|
sqlDB.SetConnMaxLifetime(30 * time.Minute)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
if err := sqlDB.PingContext(ctx); err != nil {
|
|
sqlDB.Close()
|
|
return nil, nil, err
|
|
}
|
|
if err := db.Exec(`SET TIME ZONE 'Asia/Shanghai'`).Error; err != nil {
|
|
sqlDB.Close()
|
|
return nil, nil, err
|
|
}
|
|
if err := bootstrapEmptyDatabase(db); err != nil {
|
|
sqlDB.Close()
|
|
return nil, nil, err
|
|
}
|
|
// Keep older installations compatible when migrations are applied manually.
|
|
if err := db.Exec(`ALTER TABLE project_model_configs ADD COLUMN IF NOT EXISTS settings jsonb NOT NULL DEFAULT '{}'::jsonb`).Error; err != nil {
|
|
sqlDB.Close()
|
|
return nil, nil, err
|
|
}
|
|
if err := db.Exec(`ALTER TABLE creative_projects ADD COLUMN IF NOT EXISTS video_resolution varchar(8) NOT NULL DEFAULT '720p'`).Error; err != nil {
|
|
sqlDB.Close()
|
|
return nil, nil, err
|
|
}
|
|
if err := db.Exec(`ALTER TABLE episode_storyboards ADD COLUMN IF NOT EXISTS image_prompt text`).Error; err != nil {
|
|
sqlDB.Close()
|
|
return nil, nil, err
|
|
}
|
|
if err := db.Exec(`ALTER TABLE prompts ADD COLUMN IF NOT EXISTS type varchar(50); ALTER TABLE prompts ADD COLUMN IF NOT EXISTS content text; ALTER TABLE prompts ADD COLUMN IF NOT EXISTS scope varchar(20) NOT NULL DEFAULT 'system'; ALTER TABLE prompts ADD COLUMN IF NOT EXISTS owner_user_id uuid REFERENCES web_users(id);`).Error; err != nil {
|
|
sqlDB.Close()
|
|
return nil, nil, err
|
|
}
|
|
if err := db.Exec(`ALTER TABLE web_users ADD COLUMN IF NOT EXISTS account citext; UPDATE web_users SET account=username WHERE account IS NULL; CREATE UNIQUE INDEX IF NOT EXISTS idx_web_users_account ON web_users(account);`).Error; err != nil {
|
|
sqlDB.Close()
|
|
return nil, nil, err
|
|
}
|
|
if err := db.Exec(`ALTER TABLE redemption_codes ADD COLUMN IF NOT EXISTS code character varying(40);`).Error; err != nil {
|
|
sqlDB.Close()
|
|
return nil, nil, err
|
|
}
|
|
if err := db.Exec(`CREATE TABLE IF NOT EXISTS user_model_configs (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id uuid NOT NULL REFERENCES web_users(id),
|
|
purpose varchar(32) NOT NULL CHECK (purpose IN ('prompt_reverse','image_generation','video_generation')),
|
|
model_id uuid NOT NULL REFERENCES models(id),
|
|
prompt_id uuid REFERENCES prompts(id),
|
|
settings jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE(user_id, purpose)
|
|
); CREATE INDEX IF NOT EXISTS idx_user_model_configs_user ON user_model_configs(user_id);
|
|
DO $$
|
|
BEGIN
|
|
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='user_model_configs' AND column_name='model_type') THEN
|
|
INSERT INTO user_model_configs(id,user_id,project_type,model_type,purpose,model_id,prompt_id,settings)
|
|
SELECT DISTINCT ON (project.user_id, config.model_type)
|
|
gen_random_uuid(), project.user_id, 'video_redraw', config.model_type, config.purpose, config.model_id, config.prompt_id, config.settings
|
|
FROM project_model_configs config
|
|
JOIN creative_projects project ON project.id=config.project_id
|
|
WHERE project.deleted_at IS NULL AND project.project_type='video_redraw'
|
|
ORDER BY project.user_id, config.model_type, project.updated_at DESC, config.updated_at DESC
|
|
ON CONFLICT DO NOTHING;
|
|
ELSE
|
|
INSERT INTO user_model_configs(id,user_id,purpose,model_id,prompt_id,settings)
|
|
SELECT DISTINCT ON (project.user_id, config.purpose)
|
|
gen_random_uuid(), project.user_id, config.purpose, config.model_id, config.prompt_id, config.settings
|
|
FROM project_model_configs config
|
|
JOIN creative_projects project ON project.id=config.project_id
|
|
WHERE project.deleted_at IS NULL
|
|
ORDER BY project.user_id, config.purpose, project.updated_at DESC, config.updated_at DESC
|
|
ON CONFLICT DO NOTHING;
|
|
END IF;
|
|
END $$;`).Error; err != nil {
|
|
sqlDB.Close()
|
|
return nil, nil, err
|
|
}
|
|
if err := db.Exec(`CREATE TABLE IF NOT EXISTS user_prompt_preferences (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), user_id uuid NOT NULL REFERENCES web_users(id) ON DELETE CASCADE,
|
|
prompt_id uuid NOT NULL REFERENCES prompts(id) ON DELETE CASCADE, prompt_type varchar(64) NOT NULL,
|
|
created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE(user_id,prompt_type)
|
|
); CREATE INDEX IF NOT EXISTS idx_user_prompt_preferences_user ON user_prompt_preferences(user_id);`).Error; err != nil {
|
|
sqlDB.Close()
|
|
return nil, nil, err
|
|
}
|
|
return db, sqlDB, nil
|
|
}
|