package database import ( "database/sql" "encoding/json" "time" "github.com/jmoiron/sqlx" "github.com/peterqiu0516/sub-store/internal/model" "github.com/peterqiu0516/sub-store/internal/util" ) type CollectionRepo struct { db *sqlx.DB } func NewCollectionRepo(db *sqlx.DB) *CollectionRepo { return &CollectionRepo{db: db} } type collectionRow struct { ID string `db:"id"` Name string `db:"name"` SourceIdsJSON string `db:"source_ids_json"` RenameEnabled int `db:"rename_enabled"` RenameOptionsJSON string `db:"rename_options_json"` TemplateID string `db:"template_id"` IgnoreFailed int `db:"ignore_failed"` Enabled int `db:"enabled"` MetaJSON string `db:"meta_json"` CreatedAt int64 `db:"created_at"` UpdatedAt int64 `db:"updated_at"` } const collectionColumns = `id, name, source_ids_json, rename_enabled, rename_options_json, template_id, ignore_failed, enabled, meta_json, created_at, updated_at` func (r *CollectionRepo) List() ([]model.CollectionRecord, error) { var rows []collectionRow if err := r.db.Select(&rows, `SELECT `+collectionColumns+` FROM collections ORDER BY created_at ASC`); err != nil { return nil, err } result := make([]model.CollectionRecord, 0, len(rows)) for _, row := range rows { result = append(result, collectionFromRow(row)) } return result, nil } func (r *CollectionRepo) Get(id string) (*model.CollectionRecord, error) { var row collectionRow if err := r.db.Get(&row, `SELECT `+collectionColumns+` FROM collections WHERE id = ?`, id); err != nil { if err == sql.ErrNoRows { return nil, nil } return nil, err } rec := collectionFromRow(row) return &rec, nil } func (r *CollectionRepo) Upsert(input model.CollectionRecord) (model.CollectionRecord, error) { now := time.Now().UnixMilli() id := input.ID if id == "" { id = util.ToId(input.Name) } existing, _ := r.Get(id) createdAt := now if existing != nil { createdAt = existing.CreatedAt } sourceIds := input.SourceIds if sourceIds == nil { sourceIds = []string{} } meta := input.Meta if meta == nil { meta = map[string]any{} } templateId := input.TemplateId if templateId == "" { templateId = "acl4ssr-mihomo" } rec := model.CollectionRecord{ ID: id, Name: input.Name, SourceIds: sourceIds, RenameEnabled: input.RenameEnabled, RenameOptions: input.RenameOptions, TemplateId: templateId, IgnoreFailed: input.IgnoreFailed, Enabled: input.Enabled, Meta: meta, CreatedAt: createdAt, UpdatedAt: now, } sourceIdsJSON := marshalJSON(rec.SourceIds) metaJSON := marshalJSON(rec.Meta) renameOptionsJSON := "" if rec.RenameOptions != nil { renameOptionsJSON = marshalJSON(rec.RenameOptions) } _, err := r.db.Exec( `INSERT INTO collections (id, name, source_ids_json, rename_enabled, rename_options_json, template_id, ignore_failed, enabled, meta_json, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET name = excluded.name, source_ids_json = excluded.source_ids_json, rename_enabled = excluded.rename_enabled, rename_options_json = excluded.rename_options_json, template_id = excluded.template_id, ignore_failed = excluded.ignore_failed, enabled = excluded.enabled, meta_json = excluded.meta_json, updated_at = excluded.updated_at`, rec.ID, rec.Name, sourceIdsJSON, boolToInt(rec.RenameEnabled), renameOptionsJSON, rec.TemplateId, boolToInt(rec.IgnoreFailed), boolToInt(rec.Enabled), metaJSON, rec.CreatedAt, rec.UpdatedAt, ) if err != nil { return rec, err } return rec, nil } func (r *CollectionRepo) Delete(id string) error { _, err := r.db.Exec("DELETE FROM collections WHERE id = ?", id) return err } func (r *CollectionRepo) Sort(ids []string) error { now := time.Now().UnixMilli() tx, err := r.db.Beginx() if err != nil { return err } defer tx.Rollback() for i, id := range ids { _, err := tx.Exec("UPDATE collections SET created_at = ?, updated_at = ? WHERE id = ?", now+int64(i), now, id) if err != nil { return err } } return tx.Commit() } func collectionFromRow(row collectionRow) model.CollectionRecord { var sourceIds []string json.Unmarshal([]byte(row.SourceIdsJSON), &sourceIds) if sourceIds == nil { sourceIds = []string{} } var renameOptions *model.RenameOptions if row.RenameOptionsJSON != "" { var opts model.RenameOptions if err := json.Unmarshal([]byte(row.RenameOptionsJSON), &opts); err == nil { renameOptions = &opts } } return model.CollectionRecord{ ID: row.ID, Name: row.Name, SourceIds: sourceIds, RenameEnabled: row.RenameEnabled != 0, RenameOptions: renameOptions, TemplateId: row.TemplateID, IgnoreFailed: row.IgnoreFailed != 0, Enabled: row.Enabled != 0, Meta: model.UnmarshalMeta(row.MetaJSON), CreatedAt: row.CreatedAt, UpdatedAt: row.UpdatedAt, } }