Release v0.19.0. See the `## [0.19.0] — 2026-09-29` section of [CHANGELOG.md](https://github.com/ralksta/immich-folio/blob/dev/CHANGELOG.md) for the full list. Highlights: client proofing links (#683), custom content pages (#738), asset picker search (#740), concurrent-edit detection (#742), env-locked settings (#739), an accessibility baseline (#741), three security fixes with advisories (#737, #755, #759), and a pre-release quality pass (#780–#797). --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: ralksta <ralksta@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Ralf <rohlenbo79@gmail.com> Co-authored-by: Paul Thies <110185070+ImScheinox@users.noreply.github.com> Co-authored-by: lancetm714 <108419425+lancetm714@users.noreply.github.com> Co-authored-by: Lance Martin <lancetaylor95@gmail.com>
93 lines
2.9 KiB
TypeScript
93 lines
2.9 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
|
|
vi.mock('@/lib/admin/auth', () => ({
|
|
isAdminEnabled: vi.fn(() => true),
|
|
isAdminAuthenticated: vi.fn(async () => true),
|
|
COOKIE_NAME: 'folio_admin_session',
|
|
}));
|
|
|
|
vi.mock('@/lib/admin/yaml-service', () => ({
|
|
readSettingsYaml: vi.fn(async () => ({
|
|
sitePassword: 'scrypt:stored:hash',
|
|
contact: { enabled: true, notifyUrl: 'https://ntfy.sh/stored' },
|
|
})),
|
|
readSettingsYamlVersioned: vi.fn(async () => ({
|
|
data: {
|
|
sitePassword: 'scrypt:stored:hash',
|
|
contact: { enabled: true, notifyUrl: 'https://ntfy.sh/stored' },
|
|
},
|
|
version: 'v1',
|
|
})),
|
|
writeSettingsYaml: vi.fn(async () => undefined),
|
|
}));
|
|
|
|
vi.mock('@/lib/config', () => ({
|
|
invalidateConfigCache: vi.fn(),
|
|
getConfigOrNull: vi.fn(() => null),
|
|
}));
|
|
|
|
vi.mock('@/lib/immich', () => ({ immich: { invalidateAll: vi.fn() } }));
|
|
vi.mock('next/cache', () => ({ revalidatePath: vi.fn() }));
|
|
|
|
const env = vi.hoisted(() => ({
|
|
SITE_PASSWORD: 'env-secret-password' as string | undefined,
|
|
CONTACT_NOTIFY_URL: 'https://ntfy.sh/env-secret-topic' as string | undefined,
|
|
SITE_TITLE: 'Env Title',
|
|
}));
|
|
vi.mock('@/lib/env', () => ({ env }));
|
|
|
|
import { writeSettingsYaml } from '@/lib/admin/yaml-service';
|
|
import { GET, PUT } from '../settings/route';
|
|
|
|
/**
|
|
* The panel renders these fields locked (#605). The payload carries only the
|
|
* variable names: the values are a password and an ntfy topic.
|
|
*/
|
|
describe('/api/admin/settings env locks (#605)', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
env.SITE_PASSWORD = 'env-secret-password';
|
|
env.CONTACT_NOTIFY_URL = 'https://ntfy.sh/env-secret-topic';
|
|
});
|
|
|
|
it('GET names the locking variables and never their values', async () => {
|
|
const res = await GET();
|
|
const text = await res.text();
|
|
|
|
expect(JSON.parse(text).envLocks).toEqual({
|
|
sitePassword: 'SITE_PASSWORD',
|
|
'contact.notifyUrl': 'CONTACT_NOTIFY_URL',
|
|
});
|
|
expect(text).not.toContain('env-secret-password');
|
|
expect(text).not.toContain('env-secret-topic');
|
|
});
|
|
|
|
it('GET reports no locks when the variables are unset', async () => {
|
|
env.SITE_PASSWORD = undefined;
|
|
env.CONTACT_NOTIFY_URL = undefined;
|
|
const body = await (await GET()).json();
|
|
expect(body.envLocks).toEqual({});
|
|
});
|
|
|
|
it('PUT keeps the stored value of a locked field', async () => {
|
|
const res = await PUT(
|
|
new Request('http://localhost/api/admin/settings', {
|
|
method: 'PUT',
|
|
body: JSON.stringify({
|
|
settings: {
|
|
title: 'Folio',
|
|
sitePassword: 'typed-in-panel',
|
|
contact: { enabled: true, notifyUrl: 'https://ntfy.sh/typed' },
|
|
},
|
|
}),
|
|
}),
|
|
);
|
|
expect(res.status).toBe(200);
|
|
expect(vi.mocked(writeSettingsYaml).mock.calls[0][0]).toMatchObject({
|
|
title: 'Folio',
|
|
sitePassword: 'scrypt:stored:hash',
|
|
contact: { enabled: true, notifyUrl: 'https://ntfy.sh/stored' },
|
|
});
|
|
});
|
|
});
|