chore(immich): drop the IMMICH_URL hook
A deployment no longer suggests an address for the first server: every address is each account's own and is typed in the app, so the field opens blank with a placeholder. The env var, its compose passthrough, the example line and the `defaultUrl` field that carried it are all gone; the backend's config route now answers with the saved list alone.
This commit is contained in:
@@ -23,10 +23,3 @@ SMTP_PASS=
|
||||
# What the mail says it is from. Defaults to SMTP_USER, then a noreply address.
|
||||
SMTP_FROM=
|
||||
SMTP_SECURE=
|
||||
|
||||
# Immich, as a second photo source beside the folders on disk in LIBRARY. This is
|
||||
# only the address the "add an Immich server" dialog starts with — the key is
|
||||
# each account's own, typed in the app and kept in the database (never in the
|
||||
# browser, never in this file). Leave it empty and the field starts blank; the
|
||||
# "add Immich" button is there either way.
|
||||
IMMICH_URL=https://photos.labz.io.vn
|
||||
|
||||
@@ -280,16 +280,13 @@ export function immichRoutes(
|
||||
return server;
|
||||
};
|
||||
|
||||
// The saved list, plus the address this deployment suggests for the first one.
|
||||
// No upstream call at all: the tree has to draw on a phone with no signal.
|
||||
// The saved list. No upstream call at all: the tree has to draw on a phone with
|
||||
// no signal. Nothing here suggests an address — the address and the key are each
|
||||
// account's own, typed in the app, so a fresh server is a blank field.
|
||||
app.get('/api/immich/config', async (req, reply) => {
|
||||
const user = member(req, reply);
|
||||
if (!user) return;
|
||||
return reply.send({
|
||||
ok: true,
|
||||
defaultUrl: process.env.IMMICH_URL ?? '',
|
||||
servers: immichServers(user.id).map(view),
|
||||
});
|
||||
return reply.send({ ok: true, servers: immichServers(user.id).map(view) });
|
||||
});
|
||||
|
||||
// The dialog's "check this key": nothing is stored, so a typo never becomes a
|
||||
|
||||
@@ -191,7 +191,6 @@ const server = spawn(process.execPath, entry, {
|
||||
PORT: String(PORT),
|
||||
DATA_DIR,
|
||||
NODE_ENV: 'test',
|
||||
IMMICH_URL: `http://127.0.0.1:${fake.port}`,
|
||||
},
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
});
|
||||
@@ -253,7 +252,7 @@ try {
|
||||
check('the key never comes back in clear', !JSON.stringify(saved.body).includes(KEY), JSON.stringify(saved.body));
|
||||
|
||||
const config = await user.req('/immich/config');
|
||||
check('the saved list carries the deployment default address', config.body?.defaultUrl === url, JSON.stringify(config.body?.defaultUrl));
|
||||
check('the saved list suggests no address of its own', config.body?.defaultUrl === undefined, JSON.stringify(config.body?.defaultUrl));
|
||||
check('the saved server is listed with its version', config.body?.servers?.[0]?.version === '3.1.0', JSON.stringify(config.body?.servers?.[0]));
|
||||
|
||||
// A server saved without a name falls back to its host, which is what the tree
|
||||
|
||||
@@ -23,11 +23,6 @@ services:
|
||||
SMTP_PASS: ${SMTP_PASS:-}
|
||||
SMTP_FROM: ${SMTP_FROM:-}
|
||||
SMTP_SECURE: ${SMTP_SECURE:-}
|
||||
# Immich, offered as a second photo source in LIBRARY. This is only the
|
||||
# address the "add an Immich server" dialog starts with: the URL and the
|
||||
# key belong to each account, are typed in the app, and are stored in the
|
||||
# users table. Empty simply means the field starts blank.
|
||||
IMMICH_URL: ${IMMICH_URL:-}
|
||||
volumes:
|
||||
# SQLite (WAL) lives on the host so a rebuild never loses accounts.
|
||||
- ./data:/data
|
||||
|
||||
@@ -133,7 +133,7 @@ await context.route('**/api/**', (route) => {
|
||||
|
||||
if (path === '/api/immich/config') {
|
||||
if (req.method() === 'GET') {
|
||||
return send(200, { ok: true, defaultUrl: 'https://photos.example.com', servers: saved.map(view) });
|
||||
return send(200, { ok: true, servers: saved.map(view) });
|
||||
}
|
||||
if (req.method() === 'DELETE') {
|
||||
const at = saved.findIndex((s) => s.id === url.searchParams.get('id'));
|
||||
@@ -297,10 +297,12 @@ await page.waitForSelector('[data-key="lib-add-immich"]', { timeout: 60_000 });
|
||||
await page.click('[data-key="lib-add-immich"]');
|
||||
await page.waitForSelector('[data-key="immich-url"]');
|
||||
step(
|
||||
'the dialog opens with the address the deployment suggests already in it',
|
||||
(await page.inputValue('[data-key="immich-url"]')) === 'https://photos.example.com',
|
||||
'the dialog opens blank: the address is each reader’s to type, with a nudge in the field',
|
||||
(await page.inputValue('[data-key="immich-url"]')) === '' &&
|
||||
(await page.getAttribute('[data-key="immich-url"]', 'placeholder')) === 'https://photos.example.com',
|
||||
await page.inputValue('[data-key="immich-url"]')
|
||||
);
|
||||
await page.fill('[data-key="immich-url"]', 'https://photos.example.com');
|
||||
await page.fill('[data-key="immich-key"]', 'good-key');
|
||||
await page.click('[data-key="immich-check"]');
|
||||
await page.waitForSelector('[data-key="immich-status"]');
|
||||
|
||||
@@ -336,10 +336,6 @@ function Catalogue({ admin }: { admin: boolean }) {
|
||||
// A server is not a folder on the disk — see `engine/immich.ts` — so it is the
|
||||
// column's second source and the page holds no key of its own.
|
||||
const [servers, setServers] = useState<ImmichServer[]>([]);
|
||||
// The address this deployment suggests for a first server — `IMMICH_URL`, which
|
||||
// the backend answers with. Offered as a filled-in field, not as a saved row:
|
||||
// it is a suggestion until a key is typed against it.
|
||||
const [immichUrl, setImmichUrl] = useState('');
|
||||
const [addImmich, setAddImmich] = useState(false);
|
||||
const [editAlbums, setEditAlbums] = useState<LibraryFolder | null>(null);
|
||||
// The strip starts on the catalogue the last screen read: coming back from the
|
||||
@@ -800,7 +796,6 @@ function Catalogue({ admin }: { admin: boolean }) {
|
||||
const config = await immichConfig();
|
||||
if (!alive) return;
|
||||
setServers(config.servers);
|
||||
setImmichUrl(config.defaultUrl);
|
||||
|
||||
// 5. Await photos reload if still running
|
||||
await reloadPromise;
|
||||
@@ -1106,7 +1101,6 @@ function Catalogue({ admin }: { admin: boolean }) {
|
||||
// holds the key, and what it says is what the dialog will show next time.
|
||||
const config = await immichConfig(true);
|
||||
setServers(config.servers);
|
||||
setImmichUrl(config.defaultUrl);
|
||||
if (!isNew && edited) {
|
||||
const next: LibraryFolder = { ...edited, albums: saved.albums };
|
||||
await putFolder(next);
|
||||
@@ -3123,7 +3117,6 @@ function Catalogue({ admin }: { admin: boolean }) {
|
||||
it goes straight up to the backend, which probes it before it stores. */}
|
||||
{addImmich ? (
|
||||
<ImmichDialog
|
||||
defaultUrl={immichUrl}
|
||||
onSaved={(saved, isNew) => void serverSaved(saved, isNew)}
|
||||
onClose={() => setAddImmich(false)}
|
||||
/>
|
||||
@@ -3131,7 +3124,6 @@ function Catalogue({ admin }: { admin: boolean }) {
|
||||
{editAlbums ? (
|
||||
<ImmichDialog
|
||||
server={servers.find((s) => s.id === editAlbums.server) ?? null}
|
||||
defaultUrl=""
|
||||
onSaved={(saved, isNew) => void serverSaved(saved, isNew)}
|
||||
onClose={() => setEditAlbums(null)}
|
||||
/>
|
||||
|
||||
@@ -87,16 +87,16 @@ async function get<T>(path: string): Promise<T | null> {
|
||||
}
|
||||
}
|
||||
|
||||
// The saved servers, plus the address this deployment suggests for the first one.
|
||||
// One call per session: the tree has to draw on the first paint, and a server
|
||||
// list that is asked for on every render is a round trip per render.
|
||||
let configOnce: Promise<{ defaultUrl: string; servers: ImmichServer[] }> | null = null;
|
||||
// The saved servers. One call per session: the tree has to draw on the first
|
||||
// paint, and a server list that is asked for on every render is a round trip per
|
||||
// render.
|
||||
let configOnce: Promise<{ servers: ImmichServer[] }> | null = null;
|
||||
|
||||
export function immichConfig(fresh = false): Promise<{ defaultUrl: string; servers: ImmichServer[] }> {
|
||||
export function immichConfig(fresh = false): Promise<{ servers: ImmichServer[] }> {
|
||||
if (fresh || !configOnce) {
|
||||
configOnce = get<{ defaultUrl?: string; servers?: ImmichServer[] }>('/api/immich/config').then((body) =>
|
||||
body ? { defaultUrl: body.defaultUrl ?? '', servers: body.servers ?? [] } : { defaultUrl: '', servers: [] }
|
||||
);
|
||||
configOnce = get<{ servers?: ImmichServer[] }>('/api/immich/config').then((body) => ({
|
||||
servers: body?.servers ?? [],
|
||||
}));
|
||||
}
|
||||
return configOnce;
|
||||
}
|
||||
|
||||
@@ -32,19 +32,17 @@ const UUID = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i;
|
||||
|
||||
export function ImmichDialog({
|
||||
server,
|
||||
defaultUrl,
|
||||
onSaved,
|
||||
onClose,
|
||||
}: {
|
||||
server?: ImmichServer | null;
|
||||
defaultUrl: string;
|
||||
onSaved: (saved: ImmichServer, isNew: boolean) => void;
|
||||
onClose: () => void;
|
||||
}) {
|
||||
const { t } = useI18n();
|
||||
const editing = Boolean(server);
|
||||
const [name, setName] = useState(server?.name ?? '');
|
||||
const [url, setUrl] = useState(server?.url ?? defaultUrl);
|
||||
const [url, setUrl] = useState(server?.url ?? '');
|
||||
const [key, setKey] = useState('');
|
||||
// The key a node is being given in place of the one it has. Blank means the
|
||||
// question was not asked — the node keeps the key it already answered with — so
|
||||
|
||||
Reference in New Issue
Block a user