- Tabela `profiles` + coluna `profile_id` em todas as entidades (categories, transactions, recurring_expenses, accounts, player_profile, xp_events, player_quests, player_achievements, player_cosmetics) - Dados existentes migrados para profile_id = 1 (Manoel) - CLI `./api create-user --name <n> --password <p>` cria perfil com seed de categorias e player_profile; faz upsert de senha se já existir - Auth substituída: cookie+APP_PASSWORD → JWT Bearer 24h (HS256) - Middleware RequireAuth injeta profile_id no context de todas as rotas - Todos os repositórios filtram por profile_id do context - Endpoints: POST /api/auth/login, GET /api/auth/me, POST /api/auth/change-password, POST /api/logout - Frontend: auth store usa localStorage (fc_token/fc_profile), api.ts envia Authorization header, LoginView usa campo name - SettingsView reescrita com troca de senha e logout - docker-compose.yml: remove APP_USERNAME/APP_PASSWORD, adiciona JWT_SECRET Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
81 lines
1.9 KiB
Go
81 lines
1.9 KiB
Go
package migration
|
|
|
|
import (
|
|
"context"
|
|
_ "embed"
|
|
"fmt"
|
|
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
)
|
|
|
|
//go:embed sql/001_initial.sql
|
|
var m001 string
|
|
|
|
//go:embed sql/002_add_external_id.sql
|
|
var m002 string
|
|
|
|
//go:embed sql/003_recurring_ignores.sql
|
|
var m003 string
|
|
|
|
//go:embed sql/004_accounts.sql
|
|
var m004 string
|
|
|
|
//go:embed sql/005_game_profile.sql
|
|
var m005 string
|
|
|
|
//go:embed sql/006_quests.sql
|
|
var m006 string
|
|
|
|
//go:embed sql/007_achievements.sql
|
|
var m007 string
|
|
|
|
//go:embed sql/008_cosmetics.sql
|
|
var m008 string
|
|
|
|
//go:embed sql/009_recurring_income.sql
|
|
var m009 string
|
|
|
|
//go:embed sql/010_cdi_yield.sql
|
|
var m010 string
|
|
|
|
//go:embed sql/011_credit_bills.sql
|
|
var m011 string
|
|
|
|
//go:embed sql/012_category_is_tax.sql
|
|
var m012 string
|
|
|
|
//go:embed sql/013_transaction_original_date.sql
|
|
var m013 string
|
|
|
|
//go:embed sql/014_profiles.sql
|
|
var m014 string
|
|
|
|
func Run(ctx context.Context, pool *pgxpool.Pool) error {
|
|
// Bootstrap: ensure schema_migrations table exists before checking versions.
|
|
if _, err := pool.Exec(ctx, `
|
|
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
version INTEGER PRIMARY KEY,
|
|
applied_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
|
)
|
|
`); err != nil {
|
|
return fmt.Errorf("bootstrap schema_migrations: %w", err)
|
|
}
|
|
|
|
migrations := []string{m001, m002, m003, m004, m005, m006, m007, m008, m009, m010, m011, m012, m013, m014}
|
|
for i, sql := range migrations {
|
|
version := i + 1
|
|
var applied bool
|
|
_ = pool.QueryRow(ctx, `SELECT EXISTS(SELECT 1 FROM schema_migrations WHERE version=$1)`, version).Scan(&applied)
|
|
if applied {
|
|
continue
|
|
}
|
|
if _, err := pool.Exec(ctx, sql); err != nil {
|
|
return fmt.Errorf("migration %03d: %w", version, err)
|
|
}
|
|
if _, err := pool.Exec(ctx, `INSERT INTO schema_migrations (version) VALUES ($1) ON CONFLICT DO NOTHING`, version); err != nil {
|
|
return fmt.Errorf("migration %03d mark applied: %w", version, err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|