Compare commits

..

1 Commits

Author SHA1 Message Date
Mees Frensel 06c9f2ae73 fix: disallow forward slash in shared link slug 2026-07-07 14:06:04 +02:00
8 changed files with 23 additions and 27 deletions
+3
View File
@@ -23019,6 +23019,7 @@
"slug": {
"description": "Custom URL slug",
"nullable": true,
"pattern": "^[^\\/]*$",
"type": "string"
},
"type": {
@@ -23065,6 +23066,7 @@
"slug": {
"description": "Custom URL slug",
"nullable": true,
"pattern": "^[^\\/]*$",
"type": "string"
}
},
@@ -23145,6 +23147,7 @@
"slug": {
"description": "Custom URL slug",
"nullable": true,
"pattern": "^[^\\/]*$",
"type": "string"
},
"type": {
+5 -12
View File
@@ -163,26 +163,19 @@
"description": "Filter by distance to a coordinate",
"properties": {
"latitude": {
"type": "number",
"type": "string",
"title": "Latitude",
"description": "GPS latitude of a coordinate which the asset must be close to",
"minimum": -90,
"maximum": 90,
"precision": 0.000001
"description": "GPS latitude of a coordinate which the asset must be close to"
},
"longitude": {
"type": "number",
"type": "string",
"title": "Longitude",
"description": "GPS longitude of a coordinate which the asset must be close to",
"minimum": -180,
"maximum": 180,
"precision": 0.000001
"description": "GPS longitude of a coordinate which the asset must be close to"
},
"radius": {
"type": "number",
"title": "Maximum distance",
"description": "How close in kilometres the asset must be to the given point",
"minimum": 0
"description": "How close in kilometres the asset must be to the given point"
}
}
}
+3 -3
View File
@@ -96,10 +96,10 @@ const methods = wrapper<Manifest>({
return { workflow: { continue: false } };
}
const configLat = config.coordinate?.latitude;
const configLon = config.coordinate?.longitude;
const configLat = Number.parseFloat(config.coordinate?.latitude ?? '');
const configLon = Number.parseFloat(config.coordinate?.longitude ?? '');
if (configLat === undefined || configLon === undefined) {
if (Number.isNaN(configLat) || Number.isNaN(configLat)) {
return { workflow: { continue: true } };
}
-3
View File
@@ -12,9 +12,6 @@ const JsonSchemaPropertySchema = z
description: z.string().describe('Description'),
default: z.any().optional().describe('Default value'),
enum: z.array(z.string()).optional().describe('Valid choices for enum types'),
minimum: z.number().optional().describe('Minimum value for number types'),
maximum: z.number().optional().describe('Maximum value for number types'),
precision: z.number().default(1).optional().describe('Smallest interval (granularity) for number types'),
array: z.boolean().optional().describe('Type is an array type'),
required: z.array(z.string()).optional().describe('A list of required properties'),
uiHint: z
+9 -3
View File
@@ -18,6 +18,12 @@ const SharedLinkSearchSchema = z
})
.meta({ id: 'SharedLinkSearchDto' });
const SharedLinkSlugSchema = z
.string()
.regex(/^[^\/]*$/, 'forward slash "/" is not allowed')
.nullable()
.describe('Custom URL slug');
const SharedLinkCreateSchema = z
.object({
type: SharedLinkTypeSchema,
@@ -25,7 +31,7 @@ const SharedLinkCreateSchema = z
albumId: z.uuidv4().optional().describe('Album ID (for album sharing)'),
description: z.string().nullable().optional().describe('Link description'),
password: z.string().nullable().optional().describe('Link password'),
slug: z.string().nullable().optional().describe('Custom URL slug'),
slug: SharedLinkSlugSchema.optional(),
expiresAt: isoDatetimeToDate.nullable().describe('Expiration date').default(null).optional(),
allowUpload: z.boolean().optional().describe('Allow uploads'),
allowDownload: z.boolean().default(true).optional().describe('Allow downloads'),
@@ -37,7 +43,7 @@ const SharedLinkEditSchema = z
.object({
description: z.string().nullable().optional().describe('Link description'),
password: z.string().nullable().optional().describe('Link password'),
slug: z.string().nullable().optional().describe('Custom URL slug'),
slug: SharedLinkSlugSchema.optional(),
expiresAt: isoDatetimeToDate.nullish().describe('Expiration date'),
allowUpload: z.boolean().optional().describe('Allow uploads'),
allowDownload: z.boolean().optional().describe('Allow downloads'),
@@ -66,7 +72,7 @@ const SharedLinkResponseSchema = z
allowUpload: z.boolean().describe('Allow uploads'),
allowDownload: z.boolean().describe('Allow downloads'),
showMetadata: z.boolean().describe('Show metadata'),
slug: z.string().nullable().describe('Custom URL slug'),
slug: SharedLinkSlugSchema,
})
.describe('Shared link response')
.meta({ id: 'SharedLinkResponseDto' });
@@ -100,7 +100,7 @@
</Field>
{:else if schema.type === 'number'}
<Field {label} {description}>
<NumberInput bind:value={getNumber, setValue} step={schema.precision} min={schema.minimum} max={schema.maximum} />
<NumberInput bind:value={getNumber, setValue} />
</Field>
{:else if schema.type === 'string'}
<Field {label} {description}>
@@ -32,8 +32,8 @@
<div class="mt-4 flex flex-col gap-4">
<div>
<Field label={$t('custom_url')} description={$t('shared_link_custom_url_description')}>
<Input bind:value={slug} autocomplete="off" />
<Field label={$t('custom_url')} description={$t('shared_link_custom_url_description')} invalid={slug.includes('/')}>
<Input bind:value={slug} pattern="[^\/]*" autocomplete="off" />
</Field>
{#if slug}
<Text size="tiny" color="muted" class="pt-2 break-all">/s/{encodeURIComponent(slug)}</Text>
-3
View File
@@ -93,9 +93,6 @@ export type JSONSchemaProperty = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
default?: any;
enum?: string[];
minimum?: number;
maximum?: number;
precision?: number;
array?: boolean;
properties?: Record<string, JSONSchemaProperty>;
required?: string[];