- Add is_blocked to core.users (002_core_tables.sql) - Add is_active to activity.sessions for soft deletes (005_activity_tables.sql) - Implement UserRepo: List, GetByID, GetByName, Create, Update, Delete - Implement MimeRepo: List, GetByID, GetByName - Implement SessionRepo: Create, GetByTokenHash, ListByUser, UpdateLastActivity, Delete, DeleteByUserID - Session deletes are soft (SET is_active = false); is_active is a SQL-only filter, not mapped to the domain type Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
98 lines
2.4 KiB
Go
98 lines
2.4 KiB
Go
package postgres
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
|
|
"tanabata/backend/internal/domain"
|
|
"tanabata/backend/internal/port"
|
|
)
|
|
|
|
type mimeRow struct {
|
|
ID int16 `db:"id"`
|
|
Name string `db:"name"`
|
|
Extension string `db:"extension"`
|
|
}
|
|
|
|
func toMIMEType(r mimeRow) domain.MIMEType {
|
|
return domain.MIMEType{
|
|
ID: r.ID,
|
|
Name: r.Name,
|
|
Extension: r.Extension,
|
|
}
|
|
}
|
|
|
|
// MimeRepo implements port.MimeRepo using PostgreSQL.
|
|
type MimeRepo struct {
|
|
pool *pgxpool.Pool
|
|
}
|
|
|
|
// NewMimeRepo creates a MimeRepo backed by pool.
|
|
func NewMimeRepo(pool *pgxpool.Pool) *MimeRepo {
|
|
return &MimeRepo{pool: pool}
|
|
}
|
|
|
|
var _ port.MimeRepo = (*MimeRepo)(nil)
|
|
|
|
func (r *MimeRepo) List(ctx context.Context) ([]domain.MIMEType, error) {
|
|
const sql = `SELECT id, name, extension FROM core.mime_types ORDER BY name`
|
|
|
|
q := connOrTx(ctx, r.pool)
|
|
rows, err := q.Query(ctx, sql)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("MimeRepo.List: %w", err)
|
|
}
|
|
collected, err := pgx.CollectRows(rows, pgx.RowToStructByName[mimeRow])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("MimeRepo.List scan: %w", err)
|
|
}
|
|
|
|
result := make([]domain.MIMEType, len(collected))
|
|
for i, row := range collected {
|
|
result[i] = toMIMEType(row)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (r *MimeRepo) GetByID(ctx context.Context, id int16) (*domain.MIMEType, error) {
|
|
const sql = `SELECT id, name, extension FROM core.mime_types WHERE id = $1`
|
|
|
|
q := connOrTx(ctx, r.pool)
|
|
rows, err := q.Query(ctx, sql, id)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("MimeRepo.GetByID: %w", err)
|
|
}
|
|
row, err := pgx.CollectOneRow(rows, pgx.RowToStructByName[mimeRow])
|
|
if err != nil {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return nil, domain.ErrNotFound
|
|
}
|
|
return nil, fmt.Errorf("MimeRepo.GetByID scan: %w", err)
|
|
}
|
|
m := toMIMEType(row)
|
|
return &m, nil
|
|
}
|
|
|
|
func (r *MimeRepo) GetByName(ctx context.Context, name string) (*domain.MIMEType, error) {
|
|
const sql = `SELECT id, name, extension FROM core.mime_types WHERE name = $1`
|
|
|
|
q := connOrTx(ctx, r.pool)
|
|
rows, err := q.Query(ctx, sql, name)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("MimeRepo.GetByName: %w", err)
|
|
}
|
|
row, err := pgx.CollectOneRow(rows, pgx.RowToStructByName[mimeRow])
|
|
if err != nil {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return nil, domain.ErrUnsupportedMIME
|
|
}
|
|
return nil, fmt.Errorf("MimeRepo.GetByName scan: %w", err)
|
|
}
|
|
m := toMIMEType(row)
|
|
return &m, nil
|
|
}
|