Files
immich-folio/lib/config/settingValues.ts
Ralksta ad8b09700b release: v0.19.0
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>
2026-09-29 22:50:30 +02:00

72 lines
3.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Values in settings.yaml that the site would silently ignore (QA A-14).
*
* `validateSettings` (settingsSchema.ts) checks types and structure and
* deliberately leaves values to the resolvers, which clamp or fall back so
* hand-edited YAML never takes the site down. For the fields the panel offers
* an input for, that meant a save of `accent: rot` or nine columns in a 1–6
* field was reported as "Saved" while the site showed something else.
*
* This checks exactly those fields — the site URL, the accent, the grid
* columns and gap — with the resolvers' own rules and bounds, so it can never
* be stricter than what renders. Kept apart from settingsSchema.ts so the
* settings form can run it as you type without bundling zod; the save route
* runs it before writing, and the doctor over settings.yaml as it is.
*/
import { normaliseSiteUrl } from '../siteUrl';
import { isAccentHex } from './theme';
import { PHOTO_GRID_COLUMNS_MAX, PHOTO_GRID_COLUMNS_MIN, PHOTO_GRID_GAP_MAX } from './schema';
import type { SettingsFieldError } from './settingsSchema';
function inRange(value: unknown, min: number, max: number): boolean {
return typeof value === 'number' && Number.isInteger(value) && value >= min && value <= max;
}
/** One error per offending field; an empty string counts as unset, as in the resolvers. */
export function validateSettingValues(input: unknown): SettingsFieldError[] {
if (!input || typeof input !== 'object') return [];
const s = input as {
url?: unknown;
theme?: unknown;
grid?: { columns?: unknown; gap?: unknown } | null;
};
const errors: SettingsFieldError[] = [];
// resolveSiteUrl: an unusable value falls back to SITE_URL, or to nothing,
// and the sitemap, feed and structured data go out without the address.
if (typeof s.url === 'string' && s.url.trim() !== '' && !normaliseSiteUrl(s.url)) {
errors.push({
field: 'url',
message: 'Must be a full http(s) address, e.g. https://photos.example.com.',
});
}
// resolveTheme: anything but hex falls back to the preset's accent.
const accent =
s.theme && typeof s.theme === 'object' ? (s.theme as { accent?: unknown }).accent : undefined;
if (typeof accent === 'string' && accent.trim() !== '' && !isAccentHex(accent)) {
errors.push({ field: 'theme.accent', message: 'Must be a hex colour such as #e60012.' });
}
// getConfig clamps both into these bounds (lib/config/index.ts).
const grid = s.grid && typeof s.grid === 'object' ? s.grid : null;
if (
grid?.columns != null &&
!inRange(grid.columns, PHOTO_GRID_COLUMNS_MIN, PHOTO_GRID_COLUMNS_MAX)
) {
errors.push({
field: 'grid.columns',
message: `Must be a whole number from ${PHOTO_GRID_COLUMNS_MIN} to ${PHOTO_GRID_COLUMNS_MAX}.`,
});
}
if (grid?.gap != null && !inRange(grid.gap, 0, PHOTO_GRID_GAP_MAX)) {
errors.push({
field: 'grid.gap',
message: `Must be a whole number from 0 to ${PHOTO_GRID_GAP_MAX}.`,
});
}
return errors;
}