v1.1.0: keyboard shortcuts (CH), onboarding tour, admin projects tab, remember-me login, Luftbild CH removed, hose settings in admin, credit link, font Barlow, map auto-save viewport, rate-limit 10/5min

This commit is contained in:
Pepe Ziberi
2026-02-24 19:49:42 +01:00
parent cb575f9a82
commit d893373bd9
16 changed files with 618 additions and 54 deletions

View File

@@ -21,11 +21,11 @@ export interface UserPayload {
emailVerified?: boolean
}
export async function createToken(user: UserPayload): Promise<string> {
export async function createToken(user: UserPayload, rememberMe = false): Promise<string> {
return await new SignJWT({ user })
.setProtectedHeader({ alg: 'HS256' })
.setIssuedAt()
.setExpirationTime('24h')
.setExpirationTime(rememberMe ? '30d' : '24h')
.sign(JWT_SECRET)
}

View File

@@ -72,7 +72,7 @@ export function rateLimit(config: RateLimitConfig) {
}
// Pre-configured limiters for different endpoints
export const loginLimiter = rateLimit({ id: 'login', max: 5, windowSeconds: 60 * 15 }) // 5 attempts per 15 min
export const loginLimiter = rateLimit({ id: 'login', max: 10, windowSeconds: 60 * 5 }) // 10 attempts per 5 min
export const registerLimiter = rateLimit({ id: 'register', max: 3, windowSeconds: 60 * 60 }) // 3 per hour
export const forgotPasswordLimiter = rateLimit({ id: 'forgot-pw', max: 3, windowSeconds: 60 * 15 }) // 3 per 15 min
export const resendVerificationLimiter = rateLimit({ id: 'resend-verify', max: 3, windowSeconds: 60 * 15 })
@@ -94,8 +94,12 @@ export function getClientIp(req: Request): string {
/** Helper: create a 429 response with retry-after header */
export function rateLimitResponse(resetAt: number) {
const retryAfter = Math.ceil((resetAt - Date.now()) / 1000)
const minutes = Math.ceil(retryAfter / 60)
return new Response(
JSON.stringify({ error: 'Zu viele Anfragen. Bitte versuchen Sie es später erneut.' }),
JSON.stringify({
error: `Zu viele Versuche. Bitte warten Sie ${minutes > 1 ? `${minutes} Minuten` : `${retryAfter} Sekunden`} und versuchen es erneut.`,
retryAfter,
}),
{
status: 429,
headers: {