Fix daily puzzle rotation timezone issue
- Added lib/dateUtils.ts for consistent timezone handling - Updated app/page.tsx and app/api/daily/route.ts to use Europe/Berlin timezone - Updated lib/gameState.ts to sync client-side daily check with server time - Exposed TZ env var to client in next.config.ts
This commit is contained in:
49
README.md
49
README.md
@@ -82,6 +82,55 @@ Das Projekt ist für den Betrieb mit Docker optimiert.
|
|||||||
- URL: `/admin`
|
- URL: `/admin`
|
||||||
- Standard-Passwort: `admin123` (Bitte in `docker-compose.yml` ändern!)
|
- Standard-Passwort: `admin123` (Bitte in `docker-compose.yml` ändern!)
|
||||||
|
|
||||||
|
## Nginx-Konfiguration (für Reverse Proxy)
|
||||||
|
|
||||||
|
Wenn du Nginx als Reverse Proxy verwendest, benötigst du spezielle Einstellungen für Audio-Streaming:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name your-domain.com;
|
||||||
|
|
||||||
|
# Erhöhe Upload-Limit
|
||||||
|
client_max_body_size 50M;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://localhost:3010;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
|
||||||
|
# Wichtig für Audio-Streaming: Range Requests weiterleiten
|
||||||
|
proxy_set_header Range $http_range;
|
||||||
|
proxy_set_header If-Range $http_if_range;
|
||||||
|
proxy_no_cache $http_range $http_if_range;
|
||||||
|
|
||||||
|
# Standard Proxy Headers
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Eine vollständige Beispiel-Konfiguration findest du in `nginx.conf.example`.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Audio-Dateien lassen sich nicht abspielen (in Produktion mit Nginx)
|
||||||
|
|
||||||
|
**Problem:** MP3-Dateien funktionieren lokal, aber nicht hinter Nginx.
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
1. Stelle sicher, dass Nginx Range Requests unterstützt (siehe Nginx-Konfiguration oben)
|
||||||
|
2. Prüfe die Nginx-Logs: `sudo tail -f /var/log/nginx/error.log`
|
||||||
|
3. Teste direkt ohne Nginx: `http://localhost:3010/uploads/dateiname.mp3`
|
||||||
|
4. Überprüfe die Response-Headers im Browser (Developer Tools → Network)
|
||||||
|
|
||||||
|
**Wichtige Nginx-Einstellungen:**
|
||||||
|
- `proxy_set_header Range $http_range;` - Leitet Range Requests weiter
|
||||||
|
- `proxy_buffering off;` - Deaktiviert Buffering für große Dateien
|
||||||
|
- `client_max_body_size 50M;` - Erlaubt große Uploads
|
||||||
|
|
||||||
## Lizenz
|
## Lizenz
|
||||||
|
|
||||||
MIT
|
MIT
|
||||||
|
|||||||
@@ -1,18 +1,12 @@
|
|||||||
import { NextResponse } from 'next/server';
|
import { NextResponse } from 'next/server';
|
||||||
import { PrismaClient } from '@prisma/client';
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
import { getTodayISOString } from '@/lib/dateUtils';
|
||||||
|
|
||||||
const prisma = new PrismaClient();
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
try {
|
try {
|
||||||
// Use timezone from environment variable (default: Europe/Berlin)
|
const today = getTodayISOString();
|
||||||
const timezone = process.env.TZ || 'Europe/Berlin';
|
|
||||||
const today = new Date().toLocaleDateString('en-CA', {
|
|
||||||
timeZone: timezone,
|
|
||||||
year: 'numeric',
|
|
||||||
month: '2-digit',
|
|
||||||
day: '2-digit'
|
|
||||||
}); // Format: "2025-11-21"
|
|
||||||
|
|
||||||
let dailyPuzzle = await prisma.dailyPuzzle.findUnique({
|
let dailyPuzzle = await prisma.dailyPuzzle.findUnique({
|
||||||
where: { date: today },
|
where: { date: today },
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import Game from '@/components/Game';
|
import Game from '@/components/Game';
|
||||||
|
import { getTodayISOString } from '@/lib/dateUtils';
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic';
|
export const dynamic = 'force-dynamic';
|
||||||
|
|
||||||
@@ -14,7 +15,7 @@ if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;
|
|||||||
|
|
||||||
async function getDailyPuzzle() {
|
async function getDailyPuzzle() {
|
||||||
try {
|
try {
|
||||||
const today = new Date().toISOString().split('T')[0];
|
const today = getTodayISOString();
|
||||||
console.log(`[getDailyPuzzle] Checking puzzle for date: ${today}`);
|
console.log(`[getDailyPuzzle] Checking puzzle for date: ${today}`);
|
||||||
|
|
||||||
let dailyPuzzle = await prisma.dailyPuzzle.findUnique({
|
let dailyPuzzle = await prisma.dailyPuzzle.findUnique({
|
||||||
|
|||||||
8
lib/dateUtils.ts
Normal file
8
lib/dateUtils.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export function getTodayISOString(timezone = process.env.TZ || 'Europe/Berlin'): string {
|
||||||
|
return new Date().toLocaleDateString('en-CA', {
|
||||||
|
timeZone: timezone,
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit'
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
|
import { getTodayISOString } from './dateUtils';
|
||||||
|
|
||||||
export interface GameState {
|
export interface GameState {
|
||||||
date: string;
|
date: string;
|
||||||
@@ -31,7 +32,7 @@ export function useGameState() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Load game state
|
// Load game state
|
||||||
const stored = localStorage.getItem(STORAGE_KEY);
|
const stored = localStorage.getItem(STORAGE_KEY);
|
||||||
const today = new Date().toISOString().split('T')[0];
|
const today = getTodayISOString();
|
||||||
|
|
||||||
if (stored) {
|
if (stored) {
|
||||||
const parsed: GameState = JSON.parse(stored);
|
const parsed: GameState = JSON.parse(stored);
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ const nextConfig: NextConfig = {
|
|||||||
bodySizeLimit: '50mb',
|
bodySizeLimit: '50mb',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
env: {
|
||||||
|
TZ: process.env.TZ || 'Europe/Berlin',
|
||||||
|
},
|
||||||
async headers() {
|
async headers() {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user