- CreateCredential was setting cred.Data = cred.Masked directly on the
store pointer, corrupting stored credentials with masked data
- Fixed by using a value copy for API response
- Also fix nextID starts at 1 to avoid ID=0 issue
- New config/credential_store.go: persistent credential storage (credentials.json)
- New API: CRUD for DNS credentials + credential-types endpoint
- Certificate model now supports credential_id reference
- Create certificate auto-resolves credential_id to DNS config
- New Vue page: Credentials.vue for managing saved DNS keys
- CertCreate.vue: select existing credential or manual input + save as new
- Secrets masked in API responses, never exposed in list
- Remove GORM and SQLite, use simple JSON file (data/certs.json) for persistence
- CertStore with sync.RWMutex for thread-safe in-memory cache
- All handlers updated to use config.Store instead of config.DB
- Cron job and stats updated accordingly
- go mod tidy removes unused gorm/sqlite dependencies
Previously the CreateCertificate handler checked domain existence and then
inserted separately, creating a race window where concurrent requests could
both pass the check and trigger 'UNIQUE constraint failed' on INSERT.
Now uses SQLite's native INSERT ... ON CONFLICT (upsert) which atomically
handles the uniqueness constraint at the database level, eliminating the
race condition entirely.