- Set Spanish as default language with ephemeral/encrypted privacy focus - Translate all user-facing strings and legal pages to Spanish - Replace Norwegian flag with Spanish flag in footer - Remove Hemmelig/terces.cloud links, add cloudhost.es sponsorship - Rewrite PrivacyPage: zero data collection, ephemeral design emphasis - Rewrite TermsPage: Spanish law, RGPD, paste.es/CloudHost.es references - Update PWA manifest, HTML meta tags, package.json branding - Rename webhook headers to X-Paste-Event / X-Paste-Signature - Update API docs title and contact to paste.es / cloudhost.es Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
61 lines
2.4 KiB
TypeScript
61 lines
2.4 KiB
TypeScript
import { z } from 'zod';
|
|
|
|
const sanitizeString = (str: string) => str.trim().replace(/[\x00-\x1F\x7F]/g, '');
|
|
|
|
const sanitizedString = (maxLength: number) =>
|
|
z.string().transform(sanitizeString).pipe(z.string().max(maxLength));
|
|
|
|
// Max logo size: 512KB in base64 (which is ~683KB as base64 string)
|
|
const MAX_LOGO_BASE64_LENGTH = 700000;
|
|
|
|
export const instanceSettingsSchema = z.object({
|
|
instanceName: sanitizedString(100).optional(),
|
|
instanceDescription: sanitizedString(500).optional(),
|
|
instanceLogo: z
|
|
.string()
|
|
.max(MAX_LOGO_BASE64_LENGTH, 'Logo must be smaller than 512KB')
|
|
.refine(
|
|
(val) => {
|
|
if (!val || val === '') return true;
|
|
// Check if it's a valid base64 data URL for images
|
|
return /^data:image\/(png|jpeg|jpg|gif|svg\+xml|webp);base64,/.test(val);
|
|
},
|
|
{ message: 'Logo must be a valid image (PNG, JPEG, GIF, SVG, or WebP)' }
|
|
)
|
|
.optional(),
|
|
allowRegistration: z.boolean().optional(),
|
|
requireEmailVerification: z.boolean().optional(),
|
|
maxSecretsPerUser: z.number().int().min(1).optional(),
|
|
defaultSecretExpiration: z.number().int().min(1).optional(),
|
|
maxSecretSize: z.number().int().min(1).optional(),
|
|
|
|
allowPasswordProtection: z.boolean().optional(),
|
|
allowIpRestriction: z.boolean().optional(),
|
|
allowFileUploads: z.boolean().optional(),
|
|
maxPasswordAttempts: z.number().int().min(1).optional(),
|
|
sessionTimeout: z.number().int().min(1).optional(),
|
|
enableRateLimiting: z.boolean().optional(),
|
|
rateLimitRequests: z.number().int().min(1).optional(),
|
|
rateLimitWindow: z.number().int().min(1).optional(),
|
|
|
|
// Organization features
|
|
requireInviteCode: z.boolean().optional(),
|
|
allowedEmailDomains: sanitizedString(500).optional(),
|
|
requireRegisteredUser: z.boolean().optional(),
|
|
disableEmailPasswordSignup: z.boolean().optional(),
|
|
|
|
// Webhook notifications
|
|
webhookEnabled: z.boolean().optional(),
|
|
webhookUrl: z.string().url().optional().or(z.literal('')),
|
|
webhookSecret: sanitizedString(200).optional(),
|
|
webhookOnView: z.boolean().optional(),
|
|
webhookOnBurn: z.boolean().optional(),
|
|
|
|
// Important message alert
|
|
importantMessage: sanitizedString(1000).optional(),
|
|
|
|
// Prometheus metrics
|
|
metricsEnabled: z.boolean().optional(),
|
|
metricsSecret: sanitizedString(200).optional(),
|
|
});
|