dd6958b4d6
- PWA: manifest, service worker (cache-first static, network-first API), offline page, install prompt banner - Auto-discovery: Docker socket + Traefik API scanning, approval UI - Quick-add bookmarklet: popup-based add page, favicon auto-detect - Multi-tab sync: BroadcastChannel for theme + data changes - i18n translations for all new strings (EN/RU)
72 lines
2.0 KiB
TypeScript
72 lines
2.0 KiB
TypeScript
import { json } from '@sveltejs/kit';
|
|
import type { RequestHandler } from './$types';
|
|
import { requireAuth } from '$lib/server/middleware/authenticate.js';
|
|
import * as appService from '$lib/server/services/appService.js';
|
|
import { success, error } from '$lib/server/utils/response.js';
|
|
import { z } from 'zod';
|
|
|
|
const quickAddSchema = z.object({
|
|
url: z
|
|
.string()
|
|
.url('Invalid URL')
|
|
.refine(
|
|
(u) => u.startsWith('http://') || u.startsWith('https://'),
|
|
'URL must use http or https protocol'
|
|
),
|
|
name: z.string().min(1, 'Name is required').max(200),
|
|
description: z.string().max(1000).optional()
|
|
});
|
|
|
|
/**
|
|
* POST /api/apps/quick-add — Quick-add an app with sensible defaults.
|
|
* Accepts { url, name, description? }, creates app with healthcheck enabled
|
|
* and attempts to auto-detect a favicon icon from the URL's domain.
|
|
*/
|
|
export const POST: RequestHandler = async (event) => {
|
|
const user = requireAuth(event);
|
|
|
|
let body: unknown;
|
|
try {
|
|
body = await event.request.json();
|
|
} catch {
|
|
return json(error('Invalid JSON body'), { status: 400 });
|
|
}
|
|
|
|
const parsed = quickAddSchema.safeParse(body);
|
|
if (!parsed.success) {
|
|
const messages = parsed.error.errors.map((e) => e.message).join(', ');
|
|
return json(error(messages), { status: 400 });
|
|
}
|
|
|
|
const { url, name, description } = parsed.data;
|
|
|
|
// Attempt to derive a favicon URL from the domain
|
|
let faviconUrl: string | undefined;
|
|
try {
|
|
const parsedUrl = new URL(url);
|
|
faviconUrl = `${parsedUrl.origin}/favicon.ico`;
|
|
} catch {
|
|
// URL parsing failed — skip icon detection
|
|
}
|
|
|
|
try {
|
|
const app = await appService.create({
|
|
name,
|
|
url,
|
|
description,
|
|
icon: faviconUrl,
|
|
iconType: faviconUrl ? 'url' : 'lucide',
|
|
healthcheckEnabled: true,
|
|
healthcheckInterval: 300,
|
|
healthcheckMethod: 'GET',
|
|
healthcheckExpectedStatus: 200,
|
|
healthcheckTimeout: 5000,
|
|
createdById: user.id
|
|
});
|
|
return json(success(app), { status: 201 });
|
|
} catch (err) {
|
|
const message = err instanceof Error ? err.message : 'Failed to create app';
|
|
return json(error(message), { status: 500 });
|
|
}
|
|
};
|