This commit is contained in:
Linrador 2025-10-17 10:05:57 +02:00
parent 563f3fb8ed
commit 28774efff1
11 changed files with 78 additions and 43 deletions

1
.env
View File

@ -10,6 +10,7 @@ SHARE_CODE_IV=9f1d67b8a3c4d261fa2b7c44a1d4f9c8
STEAM_API_KEY=0B3B2BF79ECD1E9262BB118A7FEF1973
NEXTAUTH_SECRET=ironieopen
NEXTAUTH_URL=https://new.ironieopen.de
NEXT_PUBLIC_APP_URL=https://new.ironieopen.de
AUTH_SECRET="57AUHXa+UmFrlnIEKxtrk8fLo+aZMtsa/oV6fklXkcE=" # Added by `npx auth`. Read more: https://cli.authjs.dev
ALLSTAR_TOKEN=ed033ac0-5df7-482e-a322-e2b4601955d3
PTERODACTYL_APP_API=ptla_O6Je82OvlCBFITDRgB1ZJ95AIyUSXYnVGgwRF6pO6d9

View File

@ -1,8 +1,10 @@
// next.config.ts
import type { NextConfig } from 'next'
import createNextIntlPlugin from 'next-intl/plugin';
const nextConfig: NextConfig = {
allowedDevOrigins: ['ironieopen.local', '*.ironieopen.local'],
output: 'standalone',
images: {
remotePatterns: [
{

View File

@ -1,7 +1,7 @@
generator client {
provider = "prisma-client-js"
output = "../src/generated/prisma"
binaryTargets = ["debian-openssl-3.0.x"]
binaryTargets = ["native", "debian-openssl-3.0.x"]
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
{
"name": "prisma-client-36cae6f6c55a75367433ee2726eff09a48a8505c964fc6fc94b065a58ace6eab",
"name": "prisma-client-b0dd8e490fa796a1b8f7491edf507ed2476889f3413a4e0ea5f23c3e5f8271e6",
"main": "index.js",
"types": "index.d.ts",
"browser": "default.js",

View File

@ -1,6 +1,7 @@
generator client {
provider = "prisma-client-js"
output = "../src/generated/prisma"
provider = "prisma-client-js"
output = "../src/generated/prisma"
binaryTargets = ["native", "debian-openssl-3.0.x"]
}
datasource db {

File diff suppressed because one or more lines are too long

View File

@ -1,14 +1,15 @@
// /src/i18n/request.ts
import {getRequestConfig} from 'next-intl/server'
import {hasLocale} from 'next-intl'
import {routing} from './routing'
export const runtime = 'nodejs'; // wichtig: nicht Edge
export default getRequestConfig(async ({requestLocale}) => {
const requested = await requestLocale
const locale = hasLocale(routing.locales, requested) ? (requested as string) : routing.defaultLocale
import {getRequestConfig} from 'next-intl/server';
import {hasLocale} from 'next-intl';
import {routing} from './routing';
// ⬇️ Eine JSON pro Locale laden
const messages = (await import(`../messages/${locale}.json`)).default
export default getRequestConfig(async ({locale}) => {
const effective =
hasLocale(routing.locales, locale) ? (locale as string) : routing.defaultLocale;
return { locale, messages }
})
const messages = (await import(`../messages/${effective}.json`)).default;
return { locale: effective, messages };
});

View File

@ -1,4 +1,4 @@
// middleware.ts
// /src/middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import createIntlMiddleware from 'next-intl/middleware';
@ -7,12 +7,10 @@ import { routing } from './i18n/routing';
const handleI18n = createIntlMiddleware(routing);
// ---- Type-Guard: prüft, ob ein Objekt eine boolsche isAdmin-Property hat
function hasAdminFlag(v: unknown): v is { isAdmin: boolean } {
return typeof v === 'object' && v !== null &&
typeof (v as Record<string, unknown>).isAdmin === 'boolean';
}
function getCurrentLocaleFromPath(pathname: string, locales: readonly string[], fallback: string) {
const first = pathname.split('/')[1];
return locales.includes(first) ? first : fallback;
@ -28,10 +26,10 @@ function stripLeadingLocale(pathname: string, locales: readonly string[]) {
}
function isProtectedPath(pathnameNoLocale: string) {
return (
pathnameNoLocale.startsWith('/') ||
pathnameNoLocale.startsWith('/settings') ||
pathnameNoLocale.startsWith('/matches') ||
pathnameNoLocale.startsWith('/team') ||
pathnameNoLocale === '/' ||
pathnameNoLocale.startsWith('/settings') ||
pathnameNoLocale.startsWith('/matches') ||
pathnameNoLocale.startsWith('/team') ||
pathnameNoLocale.startsWith('/admin')
);
}
@ -54,10 +52,31 @@ export default async function middleware(req: NextRequest) {
}
const i18nRes = handleI18n(req);
if (i18nRes.headers.get('location') || i18nRes.headers.get('x-middleware-rewrite')) {
// 1) Rewrites -> immer auf aktuelle öffentliche Origin
const rew = i18nRes.headers.get('x-middleware-rewrite');
if (rew) {
const t = new URL(rew, req.url);
const out = new URL(req.url);
out.pathname = t.pathname;
out.search = t.search;
return NextResponse.rewrite(out);
}
// 2) Redirects (Location) -> ebenfalls auf aktuelle Origin mappen
const loc = i18nRes.headers.get('location');
if (loc) {
const t = new URL(loc, req.url);
if (t.hostname !== req.nextUrl.hostname) {
const out = new URL(req.url);
out.pathname = t.pathname;
out.search = t.search;
return NextResponse.redirect(out); // 307
}
return i18nRes;
}
// 3) Geschützte Bereiche
const { locales, defaultLocale } = routing;
const url = req.nextUrl;
const pathnameNoLocale = stripLeadingLocale(pathname, locales);
@ -67,7 +86,6 @@ export default async function middleware(req: NextRequest) {
const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET });
// Adminschutz (ohne any)
if (pathnameNoLocale.startsWith('/admin')) {
const isAdmin = hasAdminFlag(token) && token.isAdmin === true;
if (!isAdmin) {
@ -78,7 +96,6 @@ export default async function middleware(req: NextRequest) {
}
}
// Allgemeiner Auth-Schutz
if (!token) {
const loginUrl = new URL('/api/auth/signin', req.url);
loginUrl.searchParams.set('callbackUrl', url.toString());