add simple Uberspace deployment option (simpler than Cloudflare)
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 <noreply@anthropic.com>
This commit is contained in:
16
.env.production.example
Normal file
16
.env.production.example
Normal file
@@ -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=/
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,6 +6,7 @@ config.local.json
|
|||||||
*.local.json
|
*.local.json
|
||||||
.env
|
.env
|
||||||
.env.*
|
.env.*
|
||||||
|
!.env.*.example
|
||||||
*.secret.*
|
*.secret.*
|
||||||
*.key
|
*.key
|
||||||
*.pem
|
*.pem
|
||||||
|
|||||||
150
UBERSPACE.md
Normal file
150
UBERSPACE.md
Normal file
@@ -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 <username>@<servername>.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/ <username>@<servername>.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 @ <IP von uberspace>
|
||||||
|
CNAME www <servername>.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
|
||||||
58
server/index.js
Normal file
58
server/index.js
Normal file
@@ -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}`);
|
||||||
|
});
|
||||||
16
server/package.json
Normal file
16
server/package.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -130,8 +130,10 @@ export class ConfigService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get API URL for Steam refresh
|
* Get API URL for Steam refresh
|
||||||
* Development: Use local Vite server
|
* Supports multiple deployment scenarios:
|
||||||
* Production: Use Cloudflare Worker
|
* - 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 {
|
static getApiUrl(endpoint: string, workerUrl?: string): string {
|
||||||
// Development mode: Use Vite dev server middleware
|
// Development mode: Use Vite dev server middleware
|
||||||
@@ -139,15 +141,22 @@ export class ConfigService {
|
|||||||
return endpoint;
|
return endpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Production mode: Use Cloudflare Worker
|
// Production: Check for backend URL from environment
|
||||||
if (!workerUrl) {
|
const backendUrl = import.meta.env.VITE_API_URL;
|
||||||
throw new Error(
|
if (backendUrl) {
|
||||||
"Worker URL not configured. Please set up your Cloudflare Worker in Settings.",
|
const baseUrl = backendUrl.replace(/\/$/, "");
|
||||||
);
|
return `${baseUrl}${endpoint}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure workerUrl doesn't have trailing slash
|
// Fallback: Cloudflare Worker (if configured)
|
||||||
const baseUrl = workerUrl.replace(/\/$/, "");
|
if (workerUrl) {
|
||||||
return `${baseUrl}${endpoint}`;
|
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.",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ const apiMiddlewarePlugin = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
base: "/whattoplay/",
|
// GitHub Pages: /whattoplay/
|
||||||
|
// Uberspace: /
|
||||||
|
base: process.env.VITE_BASE_PATH || "/whattoplay/",
|
||||||
plugins: [react(), apiMiddlewarePlugin],
|
plugins: [react(), apiMiddlewarePlugin],
|
||||||
server: {
|
server: {
|
||||||
port: 5173,
|
port: 5173,
|
||||||
|
|||||||
Reference in New Issue
Block a user