From 5eb1c8550e35e94105d13c5698affd5ce6678d5e Mon Sep 17 00:00:00 2001 From: DerJesen Date: Thu, 11 Dec 2025 09:49:24 +0100 Subject: [PATCH] first commit --- .env.example | 12 +++++ Smartes-Klassenzimmer-Backend/src/main.ts | 13 ++--- Smartes-Klassenzimmer-Frontend/src/lib/api.ts | 2 +- .../src/lib/stores/classroom.svelte.ts | 2 +- .../src/lib/stores/whiteboard.svelte.ts | 5 +- .../src/routes/+layout.ts | 7 ++- .../src/routes/api/[...path]/+server.ts | 51 +++++++++++++++++++ .../src/routes/chat/+page.svelte | 19 ++++--- Smartes-Klassenzimmer-Frontend/vite.config.ts | 20 +++++++- 9 files changed, 107 insertions(+), 24 deletions(-) create mode 100644 .env.example create mode 100644 Smartes-Klassenzimmer-Frontend/src/routes/api/[...path]/+server.ts diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..95f1a2d --- /dev/null +++ b/.env.example @@ -0,0 +1,12 @@ +# Database Connection +DATABASE_URL="postgres://postgres:" + +# Security +JWT_SECRET="change_this_to_a_secure_secret" + +# SMTP Configuration +SMTP_HOST="smtp.example.com" +SMTP_PORT=587 +SMTP_USER="user@example.com" +SMTP_PASS="password" +SMTP_FROM="noreply@smartes-klassenzimmer.de" diff --git a/Smartes-Klassenzimmer-Backend/src/main.ts b/Smartes-Klassenzimmer-Backend/src/main.ts index d9a8892..2760824 100644 --- a/Smartes-Klassenzimmer-Backend/src/main.ts +++ b/Smartes-Klassenzimmer-Backend/src/main.ts @@ -9,17 +9,10 @@ async function bootstrap() { // CORS Konfiguration app.enableCors({ - origin: [ - 'http://localhost:5173', // Vite Dev Server - 'http://127.0.0.1:5173', // Vite Dev Server (Alternative) - 'http://localhost:5500', - 'http://127.0.0.1:5500', - 'http://localhost', - '*' - ], + origin: true, credentials: true, methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'], - allowedHeaders: ['Content-Type', 'Authorization'], + allowedHeaders: ['Content-Type', 'Authorization', 'Accept'], }); // Globales Präfix für alle Routes @@ -37,6 +30,6 @@ async function bootstrap() { }), ); - await app.listen(process.env.PORT ?? 3000); + await app.listen(process.env.PORT ?? 3000, '0.0.0.0'); } bootstrap(); diff --git a/Smartes-Klassenzimmer-Frontend/src/lib/api.ts b/Smartes-Klassenzimmer-Frontend/src/lib/api.ts index 0971369..4843df3 100644 --- a/Smartes-Klassenzimmer-Frontend/src/lib/api.ts +++ b/Smartes-Klassenzimmer-Frontend/src/lib/api.ts @@ -2,7 +2,7 @@ import { browser } from '$app/environment'; // API Client für Backend-Kommunikation const API_BASE_URL = browser - ? (import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000/api') + ? (import.meta.env.VITE_API_BASE_URL || `http://${window.location.hostname}:3000/api`) : (import.meta.env.VITE_INTERNAL_API_URL || 'http://backend:3000/api'); interface ApiResponse { diff --git a/Smartes-Klassenzimmer-Frontend/src/lib/stores/classroom.svelte.ts b/Smartes-Klassenzimmer-Frontend/src/lib/stores/classroom.svelte.ts index a544c4e..249e6a9 100644 --- a/Smartes-Klassenzimmer-Frontend/src/lib/stores/classroom.svelte.ts +++ b/Smartes-Klassenzimmer-Frontend/src/lib/stores/classroom.svelte.ts @@ -28,7 +28,7 @@ class ClassroomStore { if (this.socket?.connected) return; if (!authStore.user) return; - this.socket = io('http://localhost:3000/api/classroom', { + this.socket = io(`http://${window.location.hostname}:3000/api/classroom`, { withCredentials: true, transports: ['websocket'], }); diff --git a/Smartes-Klassenzimmer-Frontend/src/lib/stores/whiteboard.svelte.ts b/Smartes-Klassenzimmer-Frontend/src/lib/stores/whiteboard.svelte.ts index 3d1773a..8d9a978 100644 --- a/Smartes-Klassenzimmer-Frontend/src/lib/stores/whiteboard.svelte.ts +++ b/Smartes-Klassenzimmer-Frontend/src/lib/stores/whiteboard.svelte.ts @@ -39,8 +39,9 @@ class WhiteboardStore { this.whiteboardId = whiteboardId; this.userId = userId; - // WebSocket-Verbindung herstellen - this.socket = io('http://localhost:3000/whiteboard', { + if (this.socket) return; + + this.socket = io(`http://${window.location.hostname}:3000/whiteboard`, { withCredentials: true, transports: ['websocket', 'polling'] }); diff --git a/Smartes-Klassenzimmer-Frontend/src/routes/+layout.ts b/Smartes-Klassenzimmer-Frontend/src/routes/+layout.ts index 5318cdb..e3e215b 100644 --- a/Smartes-Klassenzimmer-Frontend/src/routes/+layout.ts +++ b/Smartes-Klassenzimmer-Frontend/src/routes/+layout.ts @@ -1,5 +1,6 @@ import { redirect } from '@sveltejs/kit'; import type { LayoutLoad } from './$types'; +import { browser } from '$app/environment'; // Routen die NICHT authentifiziert sein müssen const PUBLIC_ROUTES = ['/', '/login', '/register']; @@ -10,9 +11,13 @@ export const load: LayoutLoad = async ({ url, fetch }) => { return {}; } + const apiBase = browser + ? `http://${window.location.hostname}:3000/api` + : 'http://localhost:3000/api'; + // Versuche den User zu laden try { - const response = await fetch('http://localhost:3000/api/auth/me', { + const response = await fetch(`${apiBase}/auth/me`, { credentials: 'include' }); diff --git a/Smartes-Klassenzimmer-Frontend/src/routes/api/[...path]/+server.ts b/Smartes-Klassenzimmer-Frontend/src/routes/api/[...path]/+server.ts new file mode 100644 index 0000000..71dd13a --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/routes/api/[...path]/+server.ts @@ -0,0 +1,51 @@ +import { env } from '$env/dynamic/private'; +import type { RequestHandler } from './$types'; + +// Fallback, falls die Variable nicht gesetzt ist (für lokale Entwicklung ohne Docker) +const TARGET_URL = env.VITE_INTERNAL_API_URL || 'http://localhost:3000/api'; + +const proxy: RequestHandler = async ({ request, params, url }) => { + // Der Pfad hinter /api/ (z.B. "auth/me") + const path = params.path; + const query = url.search; + + // Ziel-URL zusammenbauen + const targetUrl = `${TARGET_URL}/${path}${query}`; + + try { + // Request-Body und Header kopieren + const headers = new Headers(request.headers); + // Host-Header entfernen, damit das Backend nicht verwirrt ist + headers.delete('host'); + headers.delete('connection'); + + // Den ursprünglichen Request klonen und an das Ziel senden + const response = await fetch(targetUrl, { + method: request.method, + headers, + body: request.body, + // Wichtig für Cookies/Auth, falls nötig + // duplex: 'half' // Node 18+ fetch requirement für streams, falls verwendet + // @ts-ignore - sveltekit fetch wrapper handles this usually, but native fetch might need it + } as RequestInit); + + // Die Antwort vom Backend an den Client zurückgeben + return new Response(response.body, { + status: response.status, + statusText: response.statusText, + headers: response.headers + }); + } catch (err) { + console.error('Proxy Error:', err); + return new Response(JSON.stringify({ error: 'Proxy Error', details: String(err) }), { + status: 502, + headers: { 'Content-Type': 'application/json' } + }); + } +}; + +export const GET: RequestHandler = proxy; +export const POST: RequestHandler = proxy; +export const PUT: RequestHandler = proxy; +export const DELETE: RequestHandler = proxy; +export const PATCH: RequestHandler = proxy; diff --git a/Smartes-Klassenzimmer-Frontend/src/routes/chat/+page.svelte b/Smartes-Klassenzimmer-Frontend/src/routes/chat/+page.svelte index c7e9cef..c13e1a0 100644 --- a/Smartes-Klassenzimmer-Frontend/src/routes/chat/+page.svelte +++ b/Smartes-Klassenzimmer-Frontend/src/routes/chat/+page.svelte @@ -59,6 +59,9 @@ let showMembersModal = $state(false); let allUsers = $state([]); let selectedUserIds = $state([]); + + // Dynamic Base URL + const baseUrl = `http://${window.location.hostname}:3000`; $effect(() => { if (!authStore.isLoading) { @@ -82,7 +85,7 @@ async function loadGroups() { try { - const response = await fetch('http://localhost:3000/api/live/groups/my', { + const response = await fetch(`${baseUrl}/api/live/groups/my`, { credentials: 'include' }); @@ -110,7 +113,7 @@ try { // Nachrichten-Historie laden - const response = await fetch(`http://localhost:3000/api/live/groups/${group.id}/messages`, { + const response = await fetch(`${baseUrl}/api/live/groups/${group.id}/messages`, { credentials: 'include' }); @@ -151,7 +154,7 @@ console.log('User authenticated:', authStore.isAuthenticated); // Socket.io Verbindung - das httpOnly Cookie wird automatisch mitgesendet - socket = io('http://localhost:3000/api/live', { + socket = io(`${baseUrl}/api/live`, { withCredentials: true, transports: ['websocket', 'polling'] }); @@ -233,7 +236,7 @@ payload.description = newGroupDescription.trim(); } - const response = await fetch('http://localhost:3000/api/live/groups', { + const response = await fetch(`${baseUrl}/api/live/groups`, { method: 'POST', headers: { 'Content-Type': 'application/json' @@ -262,7 +265,7 @@ if (!confirm('Möchten Sie diese Gruppe wirklich löschen?')) return; try { - const response = await fetch(`http://localhost:3000/api/live/groups/${groupId}`, { + const response = await fetch(`${baseUrl}/api/live/groups/${groupId}`, { method: 'DELETE', credentials: 'include' }); @@ -305,7 +308,7 @@ async function loadAllUsers() { try { - const response = await fetch('http://localhost:3000/api/users', { + const response = await fetch(`${baseUrl}/api/users`, { credentials: 'include' }); @@ -328,7 +331,7 @@ if (!currentGroup || selectedUserIds.length === 0) return; try { - const response = await fetch(`http://localhost:3000/api/live/groups/${currentGroup.id}/members`, { + const response = await fetch(`${baseUrl}/api/live/groups/${currentGroup.id}/members`, { method: 'POST', headers: { 'Content-Type': 'application/json' @@ -360,7 +363,7 @@ if (!confirm('Möchten Sie dieses Mitglied wirklich entfernen?')) return; try { - const response = await fetch(`http://localhost:3000/api/live/groups/${currentGroup.id}/members/${userId}`, { + const response = await fetch(`${baseUrl}/api/live/groups/${currentGroup.id}/members/${userId}`, { method: 'DELETE', credentials: 'include' }); diff --git a/Smartes-Klassenzimmer-Frontend/vite.config.ts b/Smartes-Klassenzimmer-Frontend/vite.config.ts index bbf8c7d..1cade81 100644 --- a/Smartes-Klassenzimmer-Frontend/vite.config.ts +++ b/Smartes-Klassenzimmer-Frontend/vite.config.ts @@ -2,5 +2,23 @@ import { sveltekit } from '@sveltejs/kit/vite'; import { defineConfig } from 'vite'; export default defineConfig({ - plugins: [sveltekit()] + plugins: [sveltekit()], + server: { + host: 'localhost', + port: 5173, + proxy: { + '/api': { + target: 'http://10.77.48.43:3000', + changeOrigin: true, + configure: (proxy, _options) => { + proxy.on('proxyReq', (proxyReq, req, _res) => { + if (req.headers.cookie) { + proxyReq.setHeader('Cookie', req.headers.cookie); + } + console.log('[Backend Proxy]', req.method, req.url, '→ Cookies:', !!req.headers.cookie); + }); + } + } + } + } });