mirror of
https://github.com/LogicLabs-OU/OpenArchiver.git
synced 2026-04-06 00:31:57 +02:00
Compare commits
6 Commits
v0.4.1
...
user-api-k
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f7c56da51 | ||
|
|
faa50885d8 | ||
|
|
d66e302f5c | ||
|
|
5c30ffbeaa | ||
|
|
daa18d64e9 | ||
|
|
d56259656d |
@@ -1,6 +1,7 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { ApiKeyService } from '../../services/ApiKeyService';
|
||||
import { z } from 'zod';
|
||||
import { config } from '../../config';
|
||||
|
||||
const generateApiKeySchema = z.object({
|
||||
name: z.string().min(1, 'API kay name must be more than 1 characters').max(255, 'API kay name must not be more than 255 characters'),
|
||||
@@ -9,6 +10,9 @@ const generateApiKeySchema = z.object({
|
||||
|
||||
export class ApiKeyController {
|
||||
public async generateApiKey(req: Request, res: Response) {
|
||||
if (config.app.isDemo) {
|
||||
return res.status(403).json({ message: req.t('errors.demoMode') });
|
||||
}
|
||||
try {
|
||||
const { name, expiresInDays } = generateApiKeySchema.parse(req.body);
|
||||
if (!req.user || !req.user.sub) {
|
||||
@@ -38,6 +42,9 @@ export class ApiKeyController {
|
||||
}
|
||||
|
||||
public async deleteApiKey(req: Request, res: Response) {
|
||||
if (config.app.isDemo) {
|
||||
return res.status(403).json({ message: req.t('errors.demoMode') });
|
||||
}
|
||||
const { id } = req.params;
|
||||
if (!req.user || !req.user.sub) {
|
||||
return res.status(401).json({ message: 'Unauthorized' });
|
||||
|
||||
@@ -6,7 +6,11 @@ const windowInMinutes = Math.ceil(config.api.rateLimit.windowMs / 60000);
|
||||
export const rateLimiter = rateLimit({
|
||||
windowMs: config.api.rateLimit.windowMs,
|
||||
max: config.api.rateLimit.max,
|
||||
message: `Too many requests from this IP, please try again after ${windowInMinutes} minutes`,
|
||||
message: {
|
||||
status: 429,
|
||||
message: `Too many requests from this IP, please try again after ${windowInMinutes} minutes`
|
||||
},
|
||||
statusCode: 429,
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
legacyHeaders: false
|
||||
});
|
||||
|
||||
@@ -93,7 +93,19 @@ const apiKeyRouter = apiKeyRoutes(authService);
|
||||
app.use('/v1/upload', uploadRouter);
|
||||
|
||||
// Middleware for all other routes
|
||||
app.use(rateLimiter);
|
||||
app.use((req, res, next) => {
|
||||
// exclude certain API endpoints from the rate limiter, for example status, system settings
|
||||
const excludedPatterns = [
|
||||
/^\/v\d+\/auth\/status$/,
|
||||
/^\/v\d+\/settings\/system$/
|
||||
];
|
||||
for (const pattern of excludedPatterns) {
|
||||
if (pattern.test(req.path)) {
|
||||
return next();
|
||||
}
|
||||
}
|
||||
rateLimiter(req, res, next);
|
||||
});
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
|
||||
|
||||
@@ -6,8 +6,9 @@ import type { SystemSettings } from '@open-archiver/types';
|
||||
|
||||
export const load: LayoutServerLoad = async (event) => {
|
||||
const { locals, url } = event;
|
||||
try {
|
||||
const response = await api('/auth/status', event);
|
||||
const response = await api('/auth/status', event);
|
||||
|
||||
if (response.ok) {
|
||||
const { needsSetup } = await response.json();
|
||||
|
||||
if (needsSetup && url.pathname !== '/setup') {
|
||||
@@ -17,19 +18,24 @@ export const load: LayoutServerLoad = async (event) => {
|
||||
if (!needsSetup && url.pathname === '/setup') {
|
||||
throw redirect(307, '/signin');
|
||||
}
|
||||
} catch (error) {
|
||||
throw error;
|
||||
} else {
|
||||
// if auth status check fails, we can't know if the setup is complete,
|
||||
// so we redirect to signin page as a safe fallback.
|
||||
if (url.pathname !== '/signin') {
|
||||
console.error('Failed to get auth status:', await response.text());
|
||||
throw redirect(307, '/signin');
|
||||
}
|
||||
}
|
||||
|
||||
const settingsResponse = await api('/settings', event);
|
||||
const settings: SystemSettings | null = settingsResponse.ok
|
||||
? await settingsResponse.json()
|
||||
const systemSettingsResponse = await api('/settings/system', event);
|
||||
const systemSettings: SystemSettings | null = systemSettingsResponse.ok
|
||||
? await systemSettingsResponse.json()
|
||||
: null;
|
||||
|
||||
return {
|
||||
user: locals.user,
|
||||
accessToken: locals.accessToken,
|
||||
isDemo: process.env.IS_DEMO === 'true',
|
||||
settings,
|
||||
systemSettings,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
let finalTheme = $theme;
|
||||
|
||||
if (finalTheme === 'system') {
|
||||
finalTheme = data.settings?.theme || 'system';
|
||||
finalTheme = data.systemSettings?.theme || 'system';
|
||||
}
|
||||
|
||||
const isDark =
|
||||
|
||||
@@ -8,8 +8,8 @@ export const load: LayoutLoad = async ({ url, data }) => {
|
||||
|
||||
let initLocale: SupportedLanguage = 'en'; // Default fallback
|
||||
|
||||
if (data.settings?.language) {
|
||||
initLocale = data.settings.language;
|
||||
if (data.systemSettings?.language) {
|
||||
initLocale = data.systemSettings.language;
|
||||
}
|
||||
|
||||
console.log(initLocale);
|
||||
|
||||
@@ -11,9 +11,9 @@ export const load: PageServerLoad = async (event) => {
|
||||
throw error(response.status, message || 'Failed to fetch system settings');
|
||||
}
|
||||
|
||||
const settings: SystemSettings = await response.json();
|
||||
const systemSettings: SystemSettings = await response.json();
|
||||
return {
|
||||
settings,
|
||||
systemSettings,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
let { data, form }: { data: PageData; form: any } = $props();
|
||||
let settings = $state(data.settings);
|
||||
let settings = $state(data.systemSettings);
|
||||
let isSaving = $state(false);
|
||||
|
||||
const languageOptions: { value: SupportedLanguage; label: string }[] = [
|
||||
|
||||
Reference in New Issue
Block a user