The environment config loader (src/lib/env-config-loader.ts) overwrites the entire giteaConfig JSON column on every restart, but does not include any backup-related fields. This silently erases user-configured backup settings.
Affected Fields
backupStrategy
backupBeforeSync
backupRetentionCount
backupRetentionDays
backupDirectory
blockSyncOnBackupFailure
Root Cause
env-config-loader.ts:279-311 builds the giteaConfig object without backup fields. Then at line 360, the entire column is replaced:
Other fields (like lfs, wiki, visibility) are preserved because they have ?? existingConfig fallbacks in the builder. Backup fields were added later (v3.11.0) but never added to the env loader.
Impact
UI settings don't persist — user changes backup strategy in UI → restarts → reverts to default "on-force-push" (via mapDbToUiConfig fallback)
No env var support — PRE_SYNC_BACKUP_STRATEGY env var works at runtime but is never persisted to DB or shown in UI
UI/runtime mismatch — UI always shows "on-force-push" regardless of actual runtime behavior
Reproduction
Set GITEA_URL, GITEA_TOKEN etc. as env vars (triggers hasEnvConfig() → true)
Start app, go to Settings → change backup strategy to e.g. "Always Backup"
Save successfully
Restart the app
Settings page shows "Smart (on-force-push)" again — the saved value is gone
Workaround
Set PRE_SYNC_BACKUP_STRATEGY env var (e.g. disabled, always, on-force-push, block-on-force-push). This is read at sync time by resolveBackupStrategy() in repo-backup.ts and takes effect because the DB field is empty. However, the UI will not reflect this value.
Suggested Fix
Add backup fields to the env loader's giteaConfig builder with ?? existingConfig fallbacks, consistent with how other fields are handled:
Optionally, also add new env vars (e.g. BACKUP_STRATEGY) to parseEnvConfig() so users can configure backup strategy purely via env.
Originally created by @tiennm99 on GitHub (Apr 14, 2026).
Original GitHub issue: https://github.com/RayLabsHQ/gitea-mirror/issues/267
## Bug
The environment config loader (`src/lib/env-config-loader.ts`) overwrites the entire `giteaConfig` JSON column on every restart, but does not include any backup-related fields. This silently erases user-configured backup settings.
## Affected Fields
- `backupStrategy`
- `backupBeforeSync`
- `backupRetentionCount`
- `backupRetentionDays`
- `backupDirectory`
- `blockSyncOnBackupFailure`
## Root Cause
`env-config-loader.ts:279-311` builds the `giteaConfig` object without backup fields. Then at line 360, the entire column is replaced:
```ts
await db.update(configs).set({
giteaConfig, // replaces the whole JSON column — backup fields vanish
...
});
```
Other fields (like `lfs`, `wiki`, `visibility`) are preserved because they have `?? existingConfig` fallbacks in the builder. Backup fields were added later (v3.11.0) but never added to the env loader.
## Impact
1. **UI settings don't persist** — user changes backup strategy in UI → restarts → reverts to default `"on-force-push"` (via `mapDbToUiConfig` fallback)
2. **No env var support** — `PRE_SYNC_BACKUP_STRATEGY` env var works at runtime but is never persisted to DB or shown in UI
3. **UI/runtime mismatch** — UI always shows `"on-force-push"` regardless of actual runtime behavior
## Reproduction
1. Set `GITEA_URL`, `GITEA_TOKEN` etc. as env vars (triggers `hasEnvConfig() → true`)
2. Start app, go to Settings → change backup strategy to e.g. "Always Backup"
3. Save successfully
4. Restart the app
5. Settings page shows "Smart (on-force-push)" again — the saved value is gone
## Workaround
Set `PRE_SYNC_BACKUP_STRATEGY` env var (e.g. `disabled`, `always`, `on-force-push`, `block-on-force-push`). This is read at sync time by `resolveBackupStrategy()` in `repo-backup.ts` and takes effect because the DB field is empty. However, the UI will not reflect this value.
## Suggested Fix
Add backup fields to the env loader's `giteaConfig` builder with `?? existingConfig` fallbacks, consistent with how other fields are handled:
```ts
// In env-config-loader.ts giteaConfig builder:
backupStrategy: existingConfig?.[0]?.giteaConfig?.backupStrategy ?? "on-force-push",
backupBeforeSync: existingConfig?.[0]?.giteaConfig?.backupBeforeSync ?? true,
backupRetentionCount: existingConfig?.[0]?.giteaConfig?.backupRetentionCount ?? 5,
backupRetentionDays: existingConfig?.[0]?.giteaConfig?.backupRetentionDays ?? 30,
backupDirectory: existingConfig?.[0]?.giteaConfig?.backupDirectory ?? "data/repo-backups",
blockSyncOnBackupFailure: existingConfig?.[0]?.giteaConfig?.blockSyncOnBackupFailure ?? true,
```
Optionally, also add new env vars (e.g. `BACKUP_STRATEGY`) to `parseEnvConfig()` so users can configure backup strategy purely via env.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Originally created by @tiennm99 on GitHub (Apr 14, 2026).
Original GitHub issue: https://github.com/RayLabsHQ/gitea-mirror/issues/267
Bug
The environment config loader (
src/lib/env-config-loader.ts) overwrites the entiregiteaConfigJSON column on every restart, but does not include any backup-related fields. This silently erases user-configured backup settings.Affected Fields
backupStrategybackupBeforeSyncbackupRetentionCountbackupRetentionDaysbackupDirectoryblockSyncOnBackupFailureRoot Cause
env-config-loader.ts:279-311builds thegiteaConfigobject without backup fields. Then at line 360, the entire column is replaced:Other fields (like
lfs,wiki,visibility) are preserved because they have?? existingConfigfallbacks in the builder. Backup fields were added later (v3.11.0) but never added to the env loader.Impact
"on-force-push"(viamapDbToUiConfigfallback)PRE_SYNC_BACKUP_STRATEGYenv var works at runtime but is never persisted to DB or shown in UI"on-force-push"regardless of actual runtime behaviorReproduction
GITEA_URL,GITEA_TOKENetc. as env vars (triggershasEnvConfig() → true)Workaround
Set
PRE_SYNC_BACKUP_STRATEGYenv var (e.g.disabled,always,on-force-push,block-on-force-push). This is read at sync time byresolveBackupStrategy()inrepo-backup.tsand takes effect because the DB field is empty. However, the UI will not reflect this value.Suggested Fix
Add backup fields to the env loader's
giteaConfigbuilder with?? existingConfigfallbacks, consistent with how other fields are handled:Optionally, also add new env vars (e.g.
BACKUP_STRATEGY) toparseEnvConfig()so users can configure backup strategy purely via env.