From 0fe09f01f8914ea976788aef9779838f060d7a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20F=C3=B6rtsch?= Date: Fri, 6 Feb 2026 20:50:56 +0100 Subject: [PATCH] add simple Uberspace deployment option (simpler than Cloudflare) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of the complex Cloudflare Worker setup with CORS proxy, you can now deploy both PWA and backend on Uberspace (~5€/month). Changes: - Add Express.js backend server (server/index.js) - Update ConfigService to support VITE_API_URL env variable - Make base path configurable via VITE_BASE_PATH - Add comprehensive Uberspace deployment guide - Add .env.production.example for configuration Deployment options: 1. Uberspace (recommended): Simple, all-in-one hosting 2. GitHub Pages + Cloudflare Workers: Free but complex setup Co-Authored-By: Claude --- .env.production.example | 16 ++++ .gitignore | 1 + UBERSPACE.md | 150 ++++++++++++++++++++++++++++++++++ server/index.js | 58 +++++++++++++ server/package.json | 16 ++++ src/services/ConfigService.ts | 29 ++++--- vite.config.ts | 4 +- 7 files changed, 263 insertions(+), 11 deletions(-) create mode 100644 .env.production.example create mode 100644 UBERSPACE.md create mode 100644 server/index.js create mode 100644 server/package.json diff --git a/.env.production.example b/.env.production.example new file mode 100644 index 0000000..f77cf8c --- /dev/null +++ b/.env.production.example @@ -0,0 +1,16 @@ +# Backend URL (wo läuft dein Express Server?) +# Uberspace / eigenes Backend +VITE_API_URL=https://your-username.uber.space + +# GitHub Pages (wenn du GitHub Pages nutzt, aber Uberspace Backend) +# VITE_API_URL=https://your-username.uber.space + +# Lokales Backend (für Development mit separatem Backend) +# VITE_API_URL=http://localhost:3000 + +# Base Path (für URLs und Routing) +# GitHub Pages deployment: +# VITE_BASE_PATH=/whattoplay/ + +# Uberspace deployment (root): +# VITE_BASE_PATH=/ diff --git a/.gitignore b/.gitignore index 18a5667..52722bf 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ config.local.json *.local.json .env .env.* +!.env.*.example *.secret.* *.key *.pem diff --git a/UBERSPACE.md b/UBERSPACE.md new file mode 100644 index 0000000..afc1f04 --- /dev/null +++ b/UBERSPACE.md @@ -0,0 +1,150 @@ +# Uberspace Deployment + +Einfacheres Setup: Hoste sowohl PWA als auch Backend auf Uberspace. + +## Voraussetzungen + +- Uberspace Account (https://uberspace.de) +- SSH Zugriff +- Node.js (bereits auf Uberspace vorinstalliert) + +## 1. Backend deployen + +```bash +# SSH auf deinen Uberspace +ssh @.uberspace.de + +# Repository klonen +cd ~ +git clone https://github.com/felixfoertsch/whattoplay.git +cd whattoplay/server + +# Dependencies installieren +npm install + +# Backend als Service einrichten +uberspace web backend set / --http --port 3000 +``` + +### Backend als Daemon (automatischer Start) + +Erstelle `~/etc/services.d/whattoplay-server.ini`: + +```ini +[program:whattoplay-server] +directory=%(ENV_HOME)s/whattoplay/server +command=node index.js +autostart=yes +autorestart=yes +startsecs=60 +environment=PORT="3000" +``` + +Starte den Service: +```bash +supervisorctl reread +supervisorctl update +supervisorctl start whattoplay-server +supervisorctl status +``` + +## 2. PWA deployen + +```bash +# Auf deinem lokalen Rechner +# Build mit Uberspace URL als base +npm run build + +# Upload nach Uberspace +rsync -avz dist/ @.uberspace.de:~/html/ + +# Oder direkt auf Uberspace builden: +cd ~/whattoplay +npm install +npm run build +cp -r dist/* ~/html/ +``` + +## 3. Vite Config anpassen + +Für Uberspace Deployment brauchst du keine spezielle `base`: + +```typescript +// vite.config.ts +export default defineConfig({ + // base: "/whattoplay/", // <- entfernen für Uberspace + plugins: [react()], + // ... +}); +``` + +## 4. App Config anpassen + +Für Development kannst du die `.env` nutzen: + +```bash +# .env.development +VITE_API_URL=http://localhost:3000 + +# .env.production +VITE_API_URL=https://your-username.uber.space +``` + +Dann in `ConfigService.ts`: + +```typescript +static getApiUrl(endpoint: string): string { + const baseUrl = import.meta.env.VITE_API_URL || ''; + return `${baseUrl}${endpoint}`; +} +``` + +## 5. Domain einrichten (optional) + +Falls du eine eigene Domain hast: + +```bash +uberspace web domain add your-domain.com +``` + +Dann DNS Records setzen: +``` +A @ +CNAME www .uberspace.de +``` + +## Logs + +```bash +# Server logs +supervisorctl tail whattoplay-server + +# Webserver logs +tail -f ~/logs/webserver/access_log +``` + +## Updates deployen + +```bash +# Backend update +cd ~/whattoplay +git pull +cd server +npm install +supervisorctl restart whattoplay-server + +# PWA update +cd ~/whattoplay +npm install +npm run build +cp -r dist/* ~/html/ +``` + +## Kosten + +Uberspace: ~5€/Monat (pay what you want, Minimum 1€) +- Unbegrenzter Traffic +- SSH Zugriff +- Node.js, PHP, Python, Ruby Support +- MySQL/PostgreSQL Datenbanken +- Deutlich einfacher als Cloudflare Workers Setup diff --git a/server/index.js b/server/index.js new file mode 100644 index 0000000..b41f423 --- /dev/null +++ b/server/index.js @@ -0,0 +1,58 @@ +import express from 'express'; +import cors from 'cors'; +import fetch from 'node-fetch'; + +const app = express(); +const PORT = process.env.PORT || 3000; + +// Enable CORS for your PWA +app.use(cors({ + origin: process.env.ALLOWED_ORIGIN || '*' +})); + +app.use(express.json()); + +// Health check +app.get('/health', (req, res) => { + res.json({ status: 'ok' }); +}); + +// Proxy for Steam API - exactly like the worker +app.all('/api/*', async (req, res) => { + const path = req.url.replace('/api', ''); + const steamUrl = `https://store.steampowered.com${path}`; + + console.log(`Proxying: ${req.method} ${steamUrl}`); + + try { + const response = await fetch(steamUrl, { + method: req.method, + headers: { + 'User-Agent': 'WhatToPlay/1.0', + 'Accept': 'application/json', + ...(req.body && { 'Content-Type': 'application/json' }) + }, + ...(req.body && { body: JSON.stringify(req.body) }) + }); + + const contentType = response.headers.get('content-type'); + + if (contentType && contentType.includes('application/json')) { + const data = await response.json(); + res.json(data); + } else { + const text = await response.text(); + res.send(text); + } + } catch (error) { + console.error('Proxy error:', error); + res.status(500).json({ + error: 'Proxy error', + message: error.message + }); + } +}); + +app.listen(PORT, () => { + console.log(`Server running on port ${PORT}`); +}); diff --git a/server/package.json b/server/package.json new file mode 100644 index 0000000..96dfc35 --- /dev/null +++ b/server/package.json @@ -0,0 +1,16 @@ +{ + "name": "whattoplay-server", + "version": "1.0.0", + "type": "module", + "description": "Simple proxy server for WhatToPlay Steam API calls", + "main": "index.js", + "scripts": { + "start": "node index.js", + "dev": "node --watch index.js" + }, + "dependencies": { + "express": "^4.18.2", + "cors": "^2.8.5", + "node-fetch": "^3.3.2" + } +} diff --git a/src/services/ConfigService.ts b/src/services/ConfigService.ts index a9da1b9..1099755 100644 --- a/src/services/ConfigService.ts +++ b/src/services/ConfigService.ts @@ -130,8 +130,10 @@ export class ConfigService { /** * Get API URL for Steam refresh - * Development: Use local Vite server - * Production: Use Cloudflare Worker + * Supports multiple deployment scenarios: + * - Development: Vite dev server proxy + * - Uberspace: Backend on same domain via VITE_API_URL + * - Cloudflare Workers: User-configured Worker URL (fallback) */ static getApiUrl(endpoint: string, workerUrl?: string): string { // Development mode: Use Vite dev server middleware @@ -139,15 +141,22 @@ export class ConfigService { return endpoint; } - // Production mode: Use Cloudflare Worker - if (!workerUrl) { - throw new Error( - "Worker URL not configured. Please set up your Cloudflare Worker in Settings.", - ); + // Production: Check for backend URL from environment + const backendUrl = import.meta.env.VITE_API_URL; + if (backendUrl) { + const baseUrl = backendUrl.replace(/\/$/, ""); + return `${baseUrl}${endpoint}`; } - // Ensure workerUrl doesn't have trailing slash - const baseUrl = workerUrl.replace(/\/$/, ""); - return `${baseUrl}${endpoint}`; + // Fallback: Cloudflare Worker (if configured) + if (workerUrl) { + const baseUrl = workerUrl.replace(/\/$/, ""); + return `${baseUrl}${endpoint}`; + } + + // No backend configured + throw new Error( + "Backend not configured. Please deploy the server or set up a Cloudflare Worker.", + ); } } diff --git a/vite.config.ts b/vite.config.ts index 8b9dbeb..8580bd0 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -23,7 +23,9 @@ const apiMiddlewarePlugin = { }; export default defineConfig({ - base: "/whattoplay/", + // GitHub Pages: /whattoplay/ + // Uberspace: / + base: process.env.VITE_BASE_PATH || "/whattoplay/", plugins: [react(), apiMiddlewarePlugin], server: { port: 5173,