feat: multi-usuário com autenticação JWT
- 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]>
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
|
||||
"financeiro-carvalho/internal/middleware"
|
||||
"financeiro-carvalho/internal/model"
|
||||
)
|
||||
|
||||
@@ -17,18 +18,20 @@ func NewDashboardRepository(pool *pgxpool.Pool) *DashboardRepository {
|
||||
}
|
||||
|
||||
func (r *DashboardRepository) MonthlySummary(ctx context.Context, month string) (income, expenses float64, err error) {
|
||||
pid := middleware.ProfileIDFromCtx(ctx)
|
||||
row := r.pool.QueryRow(ctx, `
|
||||
SELECT
|
||||
COALESCE(SUM(CASE WHEN type = 'income' THEN amount ELSE 0 END), 0),
|
||||
COALESCE(SUM(CASE WHEN type = 'expense' THEN amount ELSE 0 END), 0)
|
||||
FROM transactions
|
||||
WHERE to_char(date, 'YYYY-MM') = $1
|
||||
`, month)
|
||||
WHERE to_char(date, 'YYYY-MM') = $1 AND profile_id = $2
|
||||
`, month, pid)
|
||||
err = row.Scan(&income, &expenses)
|
||||
return
|
||||
}
|
||||
|
||||
func (r *DashboardRepository) ByCategory(ctx context.Context, month string) ([]model.CategoryTotal, error) {
|
||||
pid := middleware.ProfileIDFromCtx(ctx)
|
||||
rows, err := r.pool.Query(ctx, `
|
||||
SELECT
|
||||
t.category_id,
|
||||
@@ -39,9 +42,10 @@ func (r *DashboardRepository) ByCategory(ctx context.Context, month string) ([]m
|
||||
LEFT JOIN categories c ON c.id = t.category_id
|
||||
WHERE to_char(t.date, 'YYYY-MM') = $1
|
||||
AND t.type = 'expense'
|
||||
AND t.profile_id = $2
|
||||
GROUP BY t.category_id, c.name, c.color
|
||||
ORDER BY total DESC
|
||||
`, month)
|
||||
`, month, pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -59,6 +63,7 @@ func (r *DashboardRepository) ByCategory(ctx context.Context, month string) ([]m
|
||||
}
|
||||
|
||||
func (r *DashboardRepository) MonthlyEvolution(ctx context.Context, month string) ([]model.MonthEvolution, error) {
|
||||
pid := middleware.ProfileIDFromCtx(ctx)
|
||||
rows, err := r.pool.Query(ctx, `
|
||||
SELECT
|
||||
to_char(m.ms, 'YYYY-MM') AS month,
|
||||
@@ -69,10 +74,10 @@ func (r *DashboardRepository) MonthlyEvolution(ctx context.Context, month string
|
||||
date_trunc('month', ($1 || '-01')::date),
|
||||
'1 month'::interval
|
||||
) AS m(ms)
|
||||
LEFT JOIN transactions t ON date_trunc('month', t.date) = m.ms
|
||||
LEFT JOIN transactions t ON date_trunc('month', t.date) = m.ms AND t.profile_id = $2
|
||||
GROUP BY m.ms
|
||||
ORDER BY m.ms
|
||||
`, month)
|
||||
`, month, pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -91,6 +96,7 @@ func (r *DashboardRepository) MonthlyEvolution(ctx context.Context, month string
|
||||
}
|
||||
|
||||
func (r *DashboardRepository) TotalTaxes(ctx context.Context, month string) (float64, error) {
|
||||
pid := middleware.ProfileIDFromCtx(ctx)
|
||||
var total float64
|
||||
err := r.pool.QueryRow(ctx, `
|
||||
SELECT COALESCE(SUM(t.amount), 0)
|
||||
@@ -99,11 +105,13 @@ func (r *DashboardRepository) TotalTaxes(ctx context.Context, month string) (flo
|
||||
WHERE to_char(t.date, 'YYYY-MM') = $1
|
||||
AND t.type = 'expense'
|
||||
AND c.is_tax = TRUE
|
||||
`, month).Scan(&total)
|
||||
AND t.profile_id = $2
|
||||
`, month, pid).Scan(&total)
|
||||
return total, err
|
||||
}
|
||||
|
||||
func (r *DashboardRepository) RecentTransactions(ctx context.Context, month string) ([]model.RecentTransaction, error) {
|
||||
pid := middleware.ProfileIDFromCtx(ctx)
|
||||
rows, err := r.pool.Query(ctx, `
|
||||
SELECT
|
||||
t.id,
|
||||
@@ -115,9 +123,10 @@ func (r *DashboardRepository) RecentTransactions(ctx context.Context, month stri
|
||||
FROM transactions t
|
||||
LEFT JOIN categories c ON c.id = t.category_id
|
||||
WHERE to_char(t.date, 'YYYY-MM') = $1
|
||||
AND t.profile_id = $2
|
||||
ORDER BY t.date DESC, t.id DESC
|
||||
LIMIT 10
|
||||
`, month)
|
||||
`, month, pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user