diff --git a/apps/admin-dashboard/src/components/source-config-panel.tsx b/apps/admin-dashboard/src/components/source-config-panel.tsx index 25790f0..831fc34 100644 --- a/apps/admin-dashboard/src/components/source-config-panel.tsx +++ b/apps/admin-dashboard/src/components/source-config-panel.tsx @@ -79,11 +79,14 @@ export function SourceConfigPanel({ source, onUpdate }: SourceConfigPanelProps) (v) => typeof v === "string" && v.length > 0, ) - await replaceSource(source.id, { + const body: Parameters[1] = { enabled, config: getUserConfig(), - ...(hasCredentials && source.perUserCredentials ? { credentials: credentialFields } : {}), - }) + } + if (hasCredentials && source.perUserCredentials) { + body.credentials = credentialFields + } + await replaceSource(source.id, body) // For non-per-user credentials (provider-level), still use the admin endpoint. if (hasCredentials && !source.perUserCredentials) { diff --git a/apps/aelis-backend/src/session/user-session-manager.test.ts b/apps/aelis-backend/src/session/user-session-manager.test.ts index 7f6f167..7bf442f 100644 --- a/apps/aelis-backend/src/session/user-session-manager.test.ts +++ b/apps/aelis-backend/src/session/user-session-manager.test.ts @@ -828,7 +828,7 @@ describe("UserSessionManager.updateSourceCredentials", () => { }) }) -describe("UserSessionManager.upsertSourceConfig", () => { +describe("UserSessionManager.saveSourceConfig", () => { test("upserts config without credentials (existing behavior)", async () => { setEnabledSources(["test"]) const factory = mock(async () => createStubSource("test")) @@ -842,7 +842,7 @@ describe("UserSessionManager.upsertSourceConfig", () => { // Create a session first so we can verify the source is refreshed await manager.getOrCreate("user-1") - await manager.upsertSourceConfig("user-1", "test", { + await manager.saveSourceConfig("user-1", "test", { enabled: true, config: { key: "value" }, }) @@ -871,7 +871,7 @@ describe("UserSessionManager.upsertSourceConfig", () => { await manager.getOrCreate("user-1") const creds = { username: "alice", password: "s3cret" } - await manager.upsertSourceConfig("user-1", "test", { + await manager.saveSourceConfig("user-1", "test", { enabled: true, config: { serverUrl: "https://example.com" }, credentials: creds, @@ -901,7 +901,7 @@ describe("UserSessionManager.upsertSourceConfig", () => { expect(session.hasSource("test")).toBe(false) // Set mockFindResult to undefined so find() returns a row (simulating the row was just created by upsertConfig) - await manager.upsertSourceConfig("user-1", "test", { + await manager.saveSourceConfig("user-1", "test", { enabled: true, config: {}, credentials: { token: "abc" }, @@ -922,7 +922,7 @@ describe("UserSessionManager.upsertSourceConfig", () => { }) await expect( - manager.upsertSourceConfig("user-1", "test", { + manager.saveSourceConfig("user-1", "test", { enabled: true, config: {}, credentials: { token: "abc" }, @@ -938,7 +938,7 @@ describe("UserSessionManager.upsertSourceConfig", () => { }) await expect( - manager.upsertSourceConfig("user-1", "unknown", { + manager.saveSourceConfig("user-1", "unknown", { enabled: true, config: {}, }), diff --git a/apps/aelis-backend/src/session/user-session-manager.ts b/apps/aelis-backend/src/session/user-session-manager.ts index 10db38d..af87366 100644 --- a/apps/aelis-backend/src/session/user-session-manager.ts +++ b/apps/aelis-backend/src/session/user-session-manager.ts @@ -11,7 +11,6 @@ import type { FeedSourceProvider } from "./feed-source-provider.ts" import { CredentialStorageUnavailableError, InvalidSourceConfigError, - InvalidSourceCredentialsError, SourceNotFoundError, } from "../sources/errors.ts" import { sources } from "../sources/user-sources.ts" @@ -180,7 +179,7 @@ export class UserSessionManager { * @throws {InvalidSourceConfigError} if config fails schema validation * @throws {CredentialStorageUnavailableError} if credentials are provided but no encryptor is configured */ - async upsertSourceConfig( + async saveSourceConfig( userId: string, sourceId: string, data: { enabled: boolean; config?: unknown; credentials?: unknown }, @@ -224,12 +223,12 @@ export class UserSessionManager { session.removeSource(sourceId) } else { // Prefer the just-provided credentials over what was in the DB. - const credentials = - data.credentials !== undefined - ? data.credentials - : existingRow?.credentials - ? this.decryptCredentials(existingRow.credentials) - : null + let credentials: unknown = null + if (data.credentials !== undefined) { + credentials = data.credentials + } else if (existingRow?.credentials) { + credentials = this.decryptCredentials(existingRow.credentials) + } const source = await provider.feedSourceForUser(userId, config, credentials) if (session.hasSource(sourceId)) { session.replaceSource(sourceId, source) diff --git a/apps/aelis-backend/src/sources/http.ts b/apps/aelis-backend/src/sources/http.ts index 5406a59..22f870f 100644 --- a/apps/aelis-backend/src/sources/http.ts +++ b/apps/aelis-backend/src/sources/http.ts @@ -168,7 +168,7 @@ async function handleReplaceSource(c: Context) { const user = c.get("user")! try { - await sessionManager.upsertSourceConfig(user.id, sourceId, { + await sessionManager.saveSourceConfig(user.id, sourceId, { enabled, config, credentials,