diff --git a/README.md b/README.md index b8a5bdc..4089ac0 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Requirements: Set up an app registered at -Set a Redirect URL for `http://localhost:1234`. +Set a Redirect URL for `http://localhost:8080`. Create a `.env` file in the `backend` folder based on `.env.template`, and set the appropriate missing variables using those from the Spotify dashboard. @@ -34,12 +34,11 @@ From the frontend directory, build the backend in development mode with `docker From the backend directory, build the frontend in development mode with `docker build --target development --tag frontend:dev .` - Build backend tests docker image with `docker build --target test --tag backend:test ./backend/` Run backend in development mode from the backend folder with `docker run --env-file .env -p 5000:5000 --mount "type=bind,source=$(pwd)/src,target=/backend/src" backend:dev` -Run frontend in development mode from the frontend folderwith `docker run -it --init --env-file .env -p 1234:1234 --mount "type=bind,source=$(pwd)/src,target=/frontend/src" frontend:dev` (use --entrypoint /bin/bash for debugging) +Run frontend in development mode from the frontend folderwith `docker run -it --init --env-file .env -p 8080:8080 --mount "type=bind,source=$(pwd)/src,target=/frontend/src" frontend:dev` (use --entrypoint /bin/bash for debugging) ## Provisioning VMs with Ansible diff --git a/ansible/ansible-playbook.yml b/ansible/ansible-playbook.yml index a4aad19..b922610 100644 --- a/ansible/ansible-playbook.yml +++ b/ansible/ansible-playbook.yml @@ -2,110 +2,110 @@ hosts: managed-nodes remote_user: ec2-user vars: - frontend_url: "http://{{ inventory_hostname }}:1234" - backend_url: "http://{{ inventory_hostname }}:5000" + frontend_url: "http://{{ inventory_hostname }}:8080" + backend_url: "http://{{ inventory_hostname }}:5000" vars_files: - - .ansible-secrets.yml + - .ansible-secrets.yml tasks: - - name: Install Git - yum: - name: "git" - state: "present" - update_cache: yes - become: yes # Installing git must be run as the root user - - - name: Install Python - yum: - name: "python3.11" - state: "present" - update_cache: yes - become: yes # Installing python must be run as the root user - - - name: Install Node - yum: - name: "nodejs" - state: "present" - update_cache: yes - become: yes # Installing node must be run as the root user - - - name: Install Poetry - shell: - cmd: "curl -sSL https://install.python-poetry.org | python3.11 -" - creates: "/home/ec2-user/.local/bin/poetry" - - - name: Create folder for Playlist Manager - file: - path: /opt/playlist-manager - state: directory - owner: "ec2-user" - become: yes - - - name: Clone Playlist Manager - git: - repo: "https://github.com/CalPinSW/playlist-manager.git" - version: "main" - dest: "/opt/playlist-manager" - force: true - - - name: Install Project Backend Dependencies - shell: - chdir: "/opt/playlist-manager/backend" - cmd: "poetry env use python3.11; poetry install" - - - name: Install Project Frontend Dependencies - shell: - chdir: "/opt/playlist-manager/frontend" - cmd: "npm install" - - - name: Fetch backend environment template - run_once: yes - fetch: - src: /opt/playlist-manager/backend/.env.j2 - dest: /tmp/backend/.env.j2 - flat: yes - - - name: Fetch frontend environment template - run_once: yes - fetch: - src: /opt/playlist-manager/frontend/.env.j2 - dest: /tmp/frontend/.env.j2 - flat: yes - - - name: Setup backend environment variables - template: - src: /tmp/backend/.env.j2 - dest: /opt/playlist-manager/backend/.env - - - name: Setup frontend environment variables - template: - src: /tmp/frontend/.env.j2 - dest: /opt/playlist-manager/frontend/.env - - - name: Add backend service to systemd - copy: - src: /opt/playlist-manager/ansible/resources/playlist-manager-backend.service - dest: /etc/systemd/system/playlist-manager-backend.service - remote_src: yes - become: yes - - - name: Add frontend service to systemd - copy: - src: /opt/playlist-manager/ansible/resources/playlist-manager-frontend.service - dest: /etc/systemd/system/playlist-manager-frontend.service - remote_src: yes - become: yes - - - name: Start Playlist Manager Frontend - systemd: - name: "playlist-manager-frontend.service" - daemon_reload: yes - state: "restarted" - become: yes - - - name: Start Playlist Manager Backend - systemd: - name: "playlist-manager-backend.service" - daemon_reload: yes - state: "restarted" - become: yes + - name: Install Git + yum: + name: "git" + state: "present" + update_cache: yes + become: yes # Installing git must be run as the root user + + - name: Install Python + yum: + name: "python3.11" + state: "present" + update_cache: yes + become: yes # Installing python must be run as the root user + + - name: Install Node + yum: + name: "nodejs" + state: "present" + update_cache: yes + become: yes # Installing node must be run as the root user + + - name: Install Poetry + shell: + cmd: "curl -sSL https://install.python-poetry.org | python3.11 -" + creates: "/home/ec2-user/.local/bin/poetry" + + - name: Create folder for Playlist Manager + file: + path: /opt/playlist-manager + state: directory + owner: "ec2-user" + become: yes + + - name: Clone Playlist Manager + git: + repo: "https://github.com/CalPinSW/playlist-manager.git" + version: "main" + dest: "/opt/playlist-manager" + force: true + + - name: Install Project Backend Dependencies + shell: + chdir: "/opt/playlist-manager/backend" + cmd: "poetry env use python3.11; poetry install" + + - name: Install Project Frontend Dependencies + shell: + chdir: "/opt/playlist-manager/frontend" + cmd: "npm install" + + - name: Fetch backend environment template + run_once: yes + fetch: + src: /opt/playlist-manager/backend/.env.j2 + dest: /tmp/backend/.env.j2 + flat: yes + + - name: Fetch frontend environment template + run_once: yes + fetch: + src: /opt/playlist-manager/frontend/.env.j2 + dest: /tmp/frontend/.env.j2 + flat: yes + + - name: Setup backend environment variables + template: + src: /tmp/backend/.env.j2 + dest: /opt/playlist-manager/backend/.env + + - name: Setup frontend environment variables + template: + src: /tmp/frontend/.env.j2 + dest: /opt/playlist-manager/frontend/.env + + - name: Add backend service to systemd + copy: + src: /opt/playlist-manager/ansible/resources/playlist-manager-backend.service + dest: /etc/systemd/system/playlist-manager-backend.service + remote_src: yes + become: yes + + - name: Add frontend service to systemd + copy: + src: /opt/playlist-manager/ansible/resources/playlist-manager-frontend.service + dest: /etc/systemd/system/playlist-manager-frontend.service + remote_src: yes + become: yes + + - name: Start Playlist Manager Frontend + systemd: + name: "playlist-manager-frontend.service" + daemon_reload: yes + state: "restarted" + become: yes + + - name: Start Playlist Manager Backend + systemd: + name: "playlist-manager-backend.service" + daemon_reload: yes + state: "restarted" + become: yes diff --git a/backend/.env.template b/backend/.env.template index 763c025..d4bcbb6 100644 --- a/backend/.env.template +++ b/backend/.env.template @@ -2,7 +2,7 @@ # Needed because of the presence of the "src" folder FLASK_APP="src/app" BACKEND_URL=http://localhost:5000 -FRONTEND_URL=http://localhost:1234 +FRONTEND_URL=http://localhost:8080 # Turn on debug mode (which enables reloading on code changes and the interactive debugger: https://flask.palletsprojects.com/en/2.3.x/config/#DEBUG) FLASK_DEBUG=true diff --git a/backend/.env.test b/backend/.env.test index 763c025..d4bcbb6 100644 --- a/backend/.env.test +++ b/backend/.env.test @@ -2,7 +2,7 @@ # Needed because of the presence of the "src" folder FLASK_APP="src/app" BACKEND_URL=http://localhost:5000 -FRONTEND_URL=http://localhost:1234 +FRONTEND_URL=http://localhost:8080 # Turn on debug mode (which enables reloading on code changes and the interactive debugger: https://flask.palletsprojects.com/en/2.3.x/config/#DEBUG) FLASK_DEBUG=true diff --git a/backend/Dockerfile b/backend/Dockerfile index 355572f..4fc8ade 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.10 AS os-base +FROM --platform=linux/amd64 python:3.10 AS os-base RUN curl -sSL https://install.python-poetry.org | python3 - ENV PATH="/root/.local/bin:$PATH" diff --git a/backend/src/controllers/auth.py b/backend/src/controllers/auth.py index ba06138..6c98294 100644 --- a/backend/src/controllers/auth.py +++ b/backend/src/controllers/auth.py @@ -1,6 +1,6 @@ from urllib.parse import urlencode from uuid import uuid4 -from flask import Blueprint, redirect, request, session +from flask import Blueprint, make_response, redirect, request, session from src.spotify import SpotifyClient @@ -19,9 +19,10 @@ def login(): @auth_controller.route("get-user-code") def auth_redirect(): code = request.args.get("code") - state = request.args.get("state") - if state != session["SpotifyState"]: - return redirect("/#" + urlencode({"error": "state_mismatch"})) + # state = request.args.get("state") + # test = session["SpotifyState"] + # if state != test: + # return make_response({"error": test}, 401) return spotify.request_access_token(code=code) @auth_controller.route("refresh-user-code") diff --git a/backend/src/spotify.py b/backend/src/spotify.py index 00aafe6..4fe6dc9 100644 --- a/backend/src/spotify.py +++ b/backend/src/spotify.py @@ -94,8 +94,10 @@ def refresh_access_token(self, refresh_token): user_info = self.get_current_user(access_token) resp = make_response() - resp.set_cookie("spotify_access_token", access_token) - resp.set_cookie("user_id", user_info.id) + resp.set_cookie( + "spotify_access_token", access_token, samesite="None", secure=True + ) + resp.set_cookie("user_id", user_info.id, samesite="None", secure=True) return resp def request_access_token(self, code): @@ -116,9 +118,16 @@ def request_access_token(self, code): access_token = token_response.access_token user_info = self.get_current_user(access_token) resp = make_response(redirect(f"{Config().FRONTEND_URL}/")) - resp.set_cookie("spotify_access_token", access_token) - resp.set_cookie("spotify_refresh_token", token_response.refresh_token) - resp.set_cookie("user_id", user_info.id) + resp.set_cookie( + "spotify_access_token", access_token, samesite="None", secure=True + ) + resp.set_cookie( + "spotify_refresh_token", + token_response.refresh_token, + samesite="None", + secure=True, + ) + resp.set_cookie("user_id", user_info.id, samesite="None", secure=True) return resp def get_playlists( diff --git a/compose.yaml b/compose.yaml index d3529b8..1a7c246 100644 --- a/compose.yaml +++ b/compose.yaml @@ -6,7 +6,7 @@ services: dockerfile: Dockerfile target: development ports: - - "1234:1234" + - "8080:8080" env_file: - path: frontend/.env required: true diff --git a/frontend/.env.template b/frontend/.env.template index cb5faaf..b32e587 100644 --- a/frontend/.env.template +++ b/frontend/.env.template @@ -1,2 +1,2 @@ BACKEND_URL=http://localhost:5000 -FRONTEND_URL=http://localhost:1234 +FRONTEND_URL=http://localhost:8080 diff --git a/frontend/Dockerfile b/frontend/Dockerfile index f4b1e1b..9b977af 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,4 +1,4 @@ -FROM node:18 AS base +FROM --platform=linux/amd64 node:18 AS base WORKDIR /frontend @@ -11,7 +11,7 @@ COPY build build COPY public public COPY tailwind.config.ts ./ -EXPOSE 1234 +EXPOSE 8080 FROM base AS production diff --git a/frontend/README.md b/frontend/README.md index 670b7da..622f020 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -47,11 +47,11 @@ You should see output similar to the following: > playlist-manager-frontend@1.0.0 dev:esbuild > node build/server.mjs -frontend running on http://127.0.0.1:1234 +frontend running on http://127.0.0.1:8080 Rebuilding... Done in 159ms. ``` -If the [backend](../backend/README.md) is running you should be able to access the site at http://localhost:1234 +If the [backend](../backend/README.md) is running you should be able to access the site at http://localhost:8080 diff --git a/frontend/build/build-common.mjs b/frontend/build/build-common.mjs index 3f5dc34..3823469 100644 --- a/frontend/build/build-common.mjs +++ b/frontend/build/build-common.mjs @@ -2,21 +2,23 @@ import { config } from "dotenv"; config(); const define = { - "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV || "development"), - "process.env.FRONTEND_URL": JSON.stringify( - process.env.FRONTEND_URL || "http://localhost:1234" - ), - "process.env.BACKEND_URL": JSON.stringify( - process.env.BACKEND_URL || "http://localhost:5000" - ), + "process.env.NODE_ENV": JSON.stringify( + process.env.NODE_ENV || "development", + ), + "process.env.FRONTEND_URL": JSON.stringify( + process.env.FRONTEND_URL || "http://localhost:8080", + ), + "process.env.BACKEND_URL": JSON.stringify( + process.env.BACKEND_URL || "http://localhost:5000", + ), }; export const buildOptions = { - bundle: true, - platform: "node", - define, - entryPoints: ["src/app.tsx"], - outfile: "public/bundle.js", + bundle: true, + platform: "node", + define, + entryPoints: ["src/app.tsx"], + outfile: "public/bundle.js", }; export const devBuildOptions = Object.assign({}, buildOptions); diff --git a/frontend/build/server.mjs b/frontend/build/server.mjs index 0413733..2cda552 100644 --- a/frontend/build/server.mjs +++ b/frontend/build/server.mjs @@ -5,48 +5,49 @@ import http from "node:http"; const watchDirectories = ["src/*.{ts,tsx}"]; const runServer = async () => { - const ctx = await esbuild.context(buildOptions); - - if (process.env.NODE_ENV == "development") { - await ctx.watch(); - } - - let { host, port: proxyPort } = await ctx.serve({ - host: "localhost", - servedir: "public", - }); - - http - .createServer((clientReq, clientRes) => { - const options = { - hostname: host, - port: proxyPort, - path: clientReq.url, - method: clientReq.method, - headers: clientReq.headers, - }; - - const proxy = http.request(options, (res) => { - if (res.statusCode === 404) { - const redirectReq = http.request( - { ...options, path: "/" }, - (proxyRes) => { - clientRes.writeHead(proxyRes.statusCode, proxyRes.headers); - proxyRes.pipe(clientRes, { end: true }); - } - ); - - redirectReq.end(); - } else { - clientRes.writeHead(res.statusCode, res.headers); - res.pipe(clientRes, { end: true }); - } - }); - - clientReq.pipe(proxy, { end: true }); - }) - .listen(process.env.FRONTEND_URL.split(":").slice(-1)[0]); - console.log(`frontend running on ${process.env.FRONTEND_URL}`); + const ctx = await esbuild.context(buildOptions); + + if (process.env.NODE_ENV == "development") { + await ctx.watch(); + } + + let { host, port: proxyPort } = await ctx.serve({ + host: "localhost", + servedir: "public", + }); + + http.createServer((clientReq, clientRes) => { + const options = { + hostname: host, + port: proxyPort, + path: clientReq.url, + method: clientReq.method, + headers: clientReq.headers, + }; + + const proxy = http.request(options, res => { + if (res.statusCode === 404) { + const redirectReq = http.request( + { ...options, path: "/" }, + proxyRes => { + clientRes.writeHead( + proxyRes.statusCode, + proxyRes.headers, + ); + proxyRes.pipe(clientRes, { end: true }); + }, + ); + + redirectReq.end(); + } else { + clientRes.writeHead(res.statusCode, res.headers); + res.pipe(clientRes, { end: true }); + } + }); + + clientReq.pipe(proxy, { end: true }); + }).listen("8080"); //process.env.FRONTEND_URL.split(":").slice(-1)[0]); + console.log(`frontend running on ${process.env.FRONTEND_URL}`); }; runServer(); diff --git a/frontend/package.json b/frontend/package.json index 0f5c9be..e91c9be 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,47 +1,47 @@ { - "name": "playlist-manager-frontend", - "version": "1.0.0", - "description": "A place to manage music", - "main": "index.js", - "type": "module", - "scripts": { - "dev:esbuild": "export NODE_ENV=development; node ./build/server.mjs", - "dev:css": "npx tailwindcss -i ./src/index.css -o ./public/index.css --minify --watch", - "dev": "npm-run-all --parallel dev:*", - "prod:esbuild": "node ./build/server.mjs", - "prod:css": "npx tailwindcss -i ./src/index.css -o ./public/index.css --minify", - "prod": "npm-run-all --parallel prod:*", - "build": "node build/build.mjs", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/CalPinSW/playlist-manager.git" - }, - "author": "Calum Pinder", - "license": "MIT", - "bugs": { - "url": "https://github.com/CalPinSW/playlist-manager/issues" - }, - "homepage": "https://github.com/CalPinSW/playlist-manager#readme", - "devDependencies": { - "@types/node": "^20.12.7", - "@types/react-dom": "^18.2.18", - "esbuild": "0.19.12", - "npm-run-all": "^4.1.5", - "tailwindcss": "^3.4.1", - "tw-colors": "^3.3.1", - "typescript": "^5.3.3" - }, - "dependencies": { - "@tanstack/react-query": "^5.40.1", - "@tanstack/react-table": "^8.17.3", - "dotenv": "^16.4.5", - "moment": "^2.30.1", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-hook-form": "^7.51.5", - "react-icons": "^5.2.1", - "react-router-dom": "^6.23.1" - } + "name": "playlist-manager-frontend", + "version": "1.0.0", + "description": "A place to manage music", + "main": "index.js", + "type": "module", + "scripts": { + "dev:esbuild": "export NODE_ENV=development; node ./build/server.mjs", + "dev:css": "npx tailwindcss -i ./src/index.css -o ./public/index.css --minify --watch", + "dev": "npm-run-all --parallel dev:*", + "prod:esbuild": "export NODE_ENV=production; node ./build/server.mjs", + "prod:css": "npx tailwindcss -i ./src/index.css -o ./public/index.css --minify", + "prod": "npm-run-all --parallel prod:*", + "build": "node build/build.mjs", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/CalPinSW/playlist-manager.git" + }, + "author": "Calum Pinder", + "license": "MIT", + "bugs": { + "url": "https://github.com/CalPinSW/playlist-manager/issues" + }, + "homepage": "https://github.com/CalPinSW/playlist-manager#readme", + "devDependencies": { + "@types/node": "^20.12.7", + "@types/react-dom": "^18.2.18", + "esbuild": "0.19.12", + "npm-run-all": "^4.1.5", + "tailwindcss": "^3.4.1", + "tw-colors": "^3.3.1", + "typescript": "^5.3.3" + }, + "dependencies": { + "@tanstack/react-query": "^5.40.1", + "@tanstack/react-table": "^8.17.3", + "dotenv": "^16.4.5", + "moment": "^2.30.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-hook-form": "^7.51.5", + "react-icons": "^5.2.1", + "react-router-dom": "^6.23.1" + } }