feat(store): per-account folder_mapping + excluded_folders columns

This commit is contained in:
2026-07-03 11:39:21 +07:00
parent e7870c6aa4
commit d6d17ee544
4 changed files with 71 additions and 15 deletions
+35 -15
View File
@@ -6,19 +6,21 @@ import (
)
type Account struct {
ID int64
TaskID int64
SrcLogin string
SrcPassEnc string
DstLogin string
DstPassEnc string
TestSrcStatus string
TestDstStatus string
Status string
Copied int64
Skipped int64
Errors int64
LastError string
ID int64
TaskID int64
SrcLogin string
SrcPassEnc string
DstLogin string
DstPassEnc string
TestSrcStatus string
TestDstStatus string
Status string
Copied int64
Skipped int64
Errors int64
LastError string
FolderMapping map[string]string
ExcludedFolders []string
}
func (s *Store) CreateAccount(ctx context.Context, a Account) (int64, error) {
@@ -39,7 +41,8 @@ func (s *Store) DeleteAccount(ctx context.Context, id int64) error {
func (s *Store) ListAccountsByTask(ctx context.Context, taskID int64) ([]Account, error) {
rows, err := s.Pool.Query(ctx,
`SELECT id, task_id, src_login, src_pass_enc, dst_login, dst_pass_enc,
test_src_status, test_dst_status, status, copied_count, skipped_count, error_count, last_error
test_src_status, test_dst_status, status, copied_count, skipped_count,
error_count, last_error, folder_mapping, excluded_folders
FROM accounts WHERE task_id=$1 ORDER BY id`, taskID)
if err != nil {
return nil, err
@@ -49,7 +52,8 @@ func (s *Store) ListAccountsByTask(ctx context.Context, taskID int64) ([]Account
for rows.Next() {
var a Account
if err := rows.Scan(&a.ID, &a.TaskID, &a.SrcLogin, &a.SrcPassEnc, &a.DstLogin, &a.DstPassEnc,
&a.TestSrcStatus, &a.TestDstStatus, &a.Status, &a.Copied, &a.Skipped, &a.Errors, &a.LastError); err != nil {
&a.TestSrcStatus, &a.TestDstStatus, &a.Status, &a.Copied, &a.Skipped, &a.Errors, &a.LastError,
&a.FolderMapping, &a.ExcludedFolders); err != nil {
return nil, err
}
out = append(out, a)
@@ -95,3 +99,19 @@ func (s *Store) IncAccountCounters(ctx context.Context, id, copied, skipped, err
id, copied, skipped, errs)
return err
}
// SetAccountFolderMapping persists an account's per-folder rename map and the
// set of source folders to skip. nil is normalized to empty so JSONB stays
// '{}' / '[]' rather than null.
func (s *Store) SetAccountFolderMapping(ctx context.Context, id int64, mapping map[string]string, excluded []string) error {
if mapping == nil {
mapping = map[string]string{}
}
if excluded == nil {
excluded = []string{}
}
_, err := s.Pool.Exec(ctx,
`UPDATE accounts SET folder_mapping=$2, excluded_folders=$3 WHERE id=$1`,
id, mapping, excluded)
return err
}
+28
View File
@@ -64,3 +64,31 @@ func TestResetAccountCounters(t *testing.T) {
a.Copied, a.Skipped, a.Errors)
}
}
func TestSetAccountFolderMapping(t *testing.T) {
s := testStore(t)
ctx := context.Background()
epSrc, _ := s.CreateEndpoint(ctx, Endpoint{RoleLabel: "src", Host: "a", Port: 993, TLSMode: "ssl"})
epDst, _ := s.CreateEndpoint(ctx, Endpoint{RoleLabel: "dst", Host: "b", Port: 993, TLSMode: "ssl"})
taskID, _ := s.CreateTask(ctx, Task{Name: "t", SrcEndpointID: epSrc, DstEndpointID: epDst})
accID, _ := s.CreateAccount(ctx, Account{TaskID: taskID, SrcLogin: "u", SrcPassEnc: "x", DstLogin: "u2", DstPassEnc: "y"})
// Fresh account defaults: empty map, empty exclusions (not nil after scan).
accs, _ := s.ListAccountsByTask(ctx, taskID)
if len(accs) != 1 || len(accs[0].FolderMapping) != 0 || len(accs[0].ExcludedFolders) != 0 {
t.Fatalf("defaults: map=%v excl=%v", accs[0].FolderMapping, accs[0].ExcludedFolders)
}
if err := s.SetAccountFolderMapping(ctx, accID,
map[string]string{"Спам": "Spam"}, []string{"Trash"}); err != nil {
t.Fatalf("set: %v", err)
}
accs, _ = s.ListAccountsByTask(ctx, taskID)
a := accs[0]
if a.FolderMapping["Спам"] != "Spam" {
t.Fatalf("mapping not persisted: %v", a.FolderMapping)
}
if len(a.ExcludedFolders) != 1 || a.ExcludedFolders[0] != "Trash" {
t.Fatalf("excluded not persisted: %v", a.ExcludedFolders)
}
}
@@ -0,0 +1,2 @@
ALTER TABLE accounts DROP COLUMN excluded_folders;
ALTER TABLE accounts DROP COLUMN folder_mapping;
@@ -0,0 +1,6 @@
ALTER TABLE accounts ADD COLUMN folder_mapping JSONB NOT NULL DEFAULT '{}';
ALTER TABLE accounts ADD COLUMN excluded_folders JSONB NOT NULL DEFAULT '[]';
-- Carry the existing task-wide mapping onto its accounts so behavior is preserved.
UPDATE accounts a SET folder_mapping = t.folder_mapping
FROM tasks t WHERE a.task_id = t.id AND t.folder_mapping <> '{}'::jsonb;