diff --git a/backend/handlers/handle_spotify.go b/backend/handlers/handle_spotify.go index 852a339..7217b06 100644 --- a/backend/handlers/handle_spotify.go +++ b/backend/handlers/handle_spotify.go @@ -21,7 +21,10 @@ func (store *Store) CompleteSpotifyAuth(ctx *gin.Context) { 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)) diff --git a/backend/services/spotify.go b/backend/services/spotify.go index 6808d72..24fe31c 100644 --- a/backend/services/spotify.go +++ b/backend/services/spotify.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "os" + "path/filepath" "time" "github.com/zmb3/spotify/v2" @@ -34,6 +35,10 @@ func SaveSpotifyToken(path string, tok *oauth2.Token) error { 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, "", " ") if err != nil { return err diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 51c0387..c6b03f6 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -6,12 +6,16 @@ services: - /app/node_modules environment: - NODE_ENV=development + backend: + environment: + - SPOTIFY_REDIRECT_URI=https://localhost/api/spotify/callback nginx: environment: - DEV_MODE=true - SEED_DB=true ports: - 80:80 + - 443:443 certbot: profiles: - disabled diff --git a/nginx/Dockerfile b/nginx/Dockerfile index c75cab9..747f14c 100644 --- a/nginx/Dockerfile +++ b/nginx/Dockerfile @@ -1,6 +1,6 @@ FROM nginx:latest 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/* COPY nginx.conf.template /etc/nginx/nginx.conf.template COPY nginx_setup.conf.template /etc/nginx/nginx_setup.conf.template diff --git a/nginx/entrypoint.sh b/nginx/entrypoint.sh index 83fa286..46f991d 100755 --- a/nginx/entrypoint.sh +++ b/nginx/entrypoint.sh @@ -3,7 +3,15 @@ set -e # Check if dev mode, certificate exists, or setup mode 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}' \ /etc/nginx/nginx.conf diff --git a/nginx/nginx_dev.conf.template b/nginx/nginx_dev.conf.template index 7fa11e3..6d4ab3b 100644 --- a/nginx/nginx_dev.conf.template +++ b/nginx/nginx_dev.conf.template @@ -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; + } + + } + } diff --git a/vue/src/components/util/Chat.vue b/vue/src/components/util/Chat.vue index eb2c847..de4b33a 100644 --- a/vue/src/components/util/Chat.vue +++ b/vue/src/components/util/Chat.vue @@ -195,7 +195,8 @@ onUnmounted(() => { diff --git a/vue/src/views/home/Steam.vue b/vue/src/views/home/Steam.vue index 2f4b6eb..a93a687 100644 --- a/vue/src/views/home/Steam.vue +++ b/vue/src/views/home/Steam.vue @@ -120,4 +120,11 @@ p { .fade-leave-to { opacity: 0; } + +@media (max-width: 850px) { + .steam-wrapper { + height: auto; + min-height: 120px; + } +}