feat: multi-instance provider support

- provider_configs: add id TEXT PK; migrate existing rows (provider_type becomes id)
- local_files_index: add provider_id column + index; scope all queries per instance
- ProviderConfigRow: add id field; add get_by_id to trait
- LocalIndex:🆕 add provider_id param; all SQL scoped by provider_id
- factory: thread provider_id through build_local_files_bundle
- AppState.local_index: Option<Arc<LocalIndex>> → HashMap<String, Arc<LocalIndex>>
- admin_providers: restructured routes (POST /admin/providers create, PUT/DELETE /{id}, POST /test)
- admin_providers: use row.id as registry key for jellyfin and local_files
- files.rescan: optional ?provider=<id> query param
- frontend: add id to ProviderConfig, update api/hooks, new multi-instance panel UX
This commit is contained in:
2026-03-19 22:54:41 +01:00
parent 373e1c7c0a
commit 311fdd4006
14 changed files with 563 additions and 111 deletions

View File

@@ -238,8 +238,10 @@ export const api = {
},
files: {
rescan: (token: string) =>
request<{ items_found: number }>("/files/rescan", { method: "POST", token }),
rescan: (token: string, provider?: string) => {
const qs = provider ? `?provider=${encodeURIComponent(provider)}` : "";
return request<{ items_found: number }>(`/files/rescan${qs}`, { method: "POST", token });
},
},
transcode: {
@@ -268,26 +270,35 @@ export const api = {
getProviders: (token: string) =>
request<ProviderConfig[]>("/admin/providers", { token }),
createProvider: (
token: string,
payload: { id: string; provider_type: string; config_json: Record<string, string>; enabled: boolean },
) =>
request<ProviderConfig>("/admin/providers", {
method: "POST",
body: JSON.stringify(payload),
token,
}),
updateProvider: (
token: string,
type: string,
id: string,
payload: { config_json: Record<string, string>; enabled: boolean },
) =>
request<ProviderConfig>(`/admin/providers/${type}`, {
request<ProviderConfig>(`/admin/providers/${id}`, {
method: "PUT",
body: JSON.stringify(payload),
token,
}),
deleteProvider: (token: string, type: string) =>
request<void>(`/admin/providers/${type}`, { method: "DELETE", token }),
deleteProvider: (token: string, id: string) =>
request<void>(`/admin/providers/${id}`, { method: "DELETE", token }),
testProvider: (
token: string,
type: string,
payload: { config_json: Record<string, string>; enabled: boolean },
payload: { provider_type: string; config_json: Record<string, string> },
) =>
request<ProviderTestResult>(`/admin/providers/${type}/test`, {
request<ProviderTestResult>("/admin/providers/test", {
method: "POST",
body: JSON.stringify(payload),
token,

View File

@@ -162,6 +162,7 @@ export interface ConfigResponse {
}
export interface ProviderConfig {
id: string;
provider_type: string;
config_json: Record<string, string>;
enabled: boolean;