Add HTTPS support in dev mode and fix mobile layout issues
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 4m19s

Generate self-signed certs for local HTTPS, add port 443 and full SSL
server block to dev nginx config, add Spotify redirect URI env var,
improve Spotify token error handling, and fix Chat/Steam mobile sizing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-26 11:18:32 +00:00
parent 619692687f
commit 932e257152
8 changed files with 126 additions and 4 deletions

View File

@@ -21,7 +21,10 @@ func (store *Store) CompleteSpotifyAuth(ctx *gin.Context) {
return return
} }
services.SaveSpotifyToken(services.SPOTIFY_TOKEN_JSON_PATH, token) if err := services.SaveSpotifyToken(services.SPOTIFY_TOKEN_JSON_PATH, token); err != nil {
ctx.String(http.StatusInternalServerError, "Failed to save token: %v", err)
return
}
client := spotify.New(store.SpotifyAuth.Client(c, token)) client := spotify.New(store.SpotifyAuth.Client(c, token))

View File

@@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"os" "os"
"path/filepath"
"time" "time"
"github.com/zmb3/spotify/v2" "github.com/zmb3/spotify/v2"
@@ -34,6 +35,10 @@ func SaveSpotifyToken(path string, tok *oauth2.Token) error {
Expiry: tok.Expiry, Expiry: tok.Expiry,
} }
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
return fmt.Errorf("creating token directory: %w", err)
}
jsonBytes, err := json.MarshalIndent(data, "", " ") jsonBytes, err := json.MarshalIndent(data, "", " ")
if err != nil { if err != nil {
return err return err

View File

@@ -6,12 +6,16 @@ services:
- /app/node_modules - /app/node_modules
environment: environment:
- NODE_ENV=development - NODE_ENV=development
backend:
environment:
- SPOTIFY_REDIRECT_URI=https://localhost/api/spotify/callback
nginx: nginx:
environment: environment:
- DEV_MODE=true - DEV_MODE=true
- SEED_DB=true - SEED_DB=true
ports: ports:
- 80:80 - 80:80
- 443:443
certbot: certbot:
profiles: profiles:
- disabled - disabled

View File

@@ -1,6 +1,6 @@
FROM nginx:latest FROM nginx:latest
RUN rm -rf /etc/nginx/html/* && \ RUN rm -rf /etc/nginx/html/* && \
apt-get update && apt-get install -y gettext-base && \ apt-get update && apt-get install -y gettext-base openssl && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
COPY nginx.conf.template /etc/nginx/nginx.conf.template COPY nginx.conf.template /etc/nginx/nginx.conf.template
COPY nginx_setup.conf.template /etc/nginx/nginx_setup.conf.template COPY nginx_setup.conf.template /etc/nginx/nginx_setup.conf.template

View File

@@ -3,7 +3,15 @@ set -e
# Check if dev mode, certificate exists, or setup mode # Check if dev mode, certificate exists, or setup mode
if [ "$DEV_MODE" = "true" ]; then if [ "$DEV_MODE" = "true" ]; then
echo "Dev mode. Using HTTP-only nginx config." echo "Dev mode. Generating self-signed certificate for HTTPS."
CERT_DIR="/etc/letsencrypt/live/localhost"
if [ ! -f "$CERT_DIR/fullchain.pem" ]; then
mkdir -p "$CERT_DIR"
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout "$CERT_DIR/privkey.pem" \
-out "$CERT_DIR/fullchain.pem" \
-subj "/CN=localhost" 2>/dev/null
fi
envsubst '${DOMAIN} ${BACKEND_HOST} ${BACKEND_PORT} ${BACKEND_ENDPOINT} ${ICECAST_HOST} ${ICECAST_PORT} ${GITEA_HOST} ${GITEA_PORT}' \ envsubst '${DOMAIN} ${BACKEND_HOST} ${BACKEND_PORT} ${BACKEND_ENDPOINT} ${ICECAST_HOST} ${ICECAST_PORT} ${GITEA_HOST} ${GITEA_PORT}' \
</etc/nginx/nginx_dev.conf.template \ </etc/nginx/nginx_dev.conf.template \
>/etc/nginx/nginx.conf >/etc/nginx/nginx.conf

View File

@@ -131,4 +131,98 @@ http {
} }
server {
listen 443 ssl;
server_name $DOMAIN www.$DOMAIN;
ssl_certificate /etc/letsencrypt/live/localhost/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/localhost/privkey.pem;
location /uploads/ {
alias /uploads/;
add_header X-Content-Type-Options nosniff always;
add_header Content-Disposition "inline" always;
add_header Content-Security-Policy "default-src 'none'; img-src 'self'; style-src 'none'; script-src 'none'" always;
}
location / {
proxy_pass http://vue:5173;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
location $BACKEND_ENDPOINT {
return 301 $BACKEND_ENDPOINT/;
}
location $BACKEND_ENDPOINT/ws {
rewrite ^$BACKEND_ENDPOINT/(.*)$ /$1 break;
proxy_pass http://$BACKEND_HOST:$BACKEND_PORT/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
}
location $BACKEND_ENDPOINT/auth/login {
limit_req zone=login burst=3 nodelay;
rewrite ^$BACKEND_ENDPOINT/(.*)$ /$1 break;
proxy_pass http://$BACKEND_HOST:$BACKEND_PORT/;
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;
}
location $BACKEND_ENDPOINT/messages/upload {
limit_req zone=upload burst=3 nodelay;
rewrite ^$BACKEND_ENDPOINT/(.*)$ /$1 break;
proxy_pass http://$BACKEND_HOST:$BACKEND_PORT/;
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;
}
location $BACKEND_ENDPOINT/ {
limit_req zone=api burst=20 nodelay;
rewrite ^$BACKEND_ENDPOINT/(.*)$ /$1 break;
proxy_pass http://$BACKEND_HOST:$BACKEND_PORT/;
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;
}
location /radio {
return 301 /radio/;
}
location /radio/ {
proxy_pass http://$ICECAST_HOST:$ICECAST_PORT/;
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;
}
location /gitea {
return 301 /gitea/;
}
location /gitea/ {
proxy_pass http://$GITEA_HOST:$GITEA_PORT/;
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;
}
}
} }

View File

@@ -195,7 +195,8 @@ onUnmounted(() => {
<style scoped> <style scoped>
@media (max-width: 850px) { @media (max-width: 850px) {
.chat-root { .chat-root {
max-height: 400px; max-height: none;
height: 100%;
} }
} }
</style> </style>

View File

@@ -120,4 +120,11 @@ p {
.fade-leave-to { .fade-leave-to {
opacity: 0; opacity: 0;
} }
@media (max-width: 850px) {
.steam-wrapper {
height: auto;
min-height: 120px;
}
}
</style> </style>