From af2e2201167f548175ea5f1f7d14f427ffcdc222 Mon Sep 17 00:00:00 2001 From: Bhavnoor Singh Saroya Date: Mon, 25 Aug 2025 14:23:55 -0700 Subject: [PATCH] minor changes --- assignment-db-service/app.js | 53 ++++++++- assignment-db-service/fly.toml | 24 ++-- assignment-service/fly.toml | 24 ++-- assignment-service/package-lock.json | 107 +++++++++++++++++ assignment-service/package.json | 1 + assignment-service/routes/InstructorRouter.js | 19 +-- assignment-service/routes/StudentRouter.js | 70 +++++------ auth-service/fly.toml | 44 +++---- auth-service/old.env.development | 11 ++ auth-service/passport.js | 6 +- auth-service/routes/assignment.js | 32 +++++ auth-service/routes/auth.js | 35 +++++- auth-service/server.js | 111 +++++++++++++++--- deployment-service/fly.toml | 26 ++-- deployment-service/src/index.js | 22 ++-- gameboard-service/.dockerignore | 28 +++-- .../.github/workflows/fly-deploy.yml | 18 --- .../.github/workflows/release.yaml | 29 ----- .../.github/workflows/tests.yaml | 43 ------- gameboard-service/Dockerfile | 52 +++----- gameboard-service/fly.toml | 29 +++-- gameboard-service/nginx.conf | 25 ++++ gameboard-service/package.json | 2 +- gameboard-service/src/lib/playback/types.ts | 4 +- gameboard-service/src/lib/settings/stores.ts | 3 +- gameboard-service/svelte.config.js | 37 ++++-- gameboard-service/vite.config.ts | 2 +- proxy-service/fly.toml | 44 +++---- user-db-service/app.js | 9 +- user-db-service/fly.toml | 38 +++--- 30 files changed, 598 insertions(+), 350 deletions(-) create mode 100644 auth-service/old.env.development create mode 100644 auth-service/routes/assignment.js delete mode 100644 gameboard-service/.github/workflows/fly-deploy.yml delete mode 100644 gameboard-service/.github/workflows/release.yaml delete mode 100644 gameboard-service/.github/workflows/tests.yaml create mode 100644 gameboard-service/nginx.conf diff --git a/assignment-db-service/app.js b/assignment-db-service/app.js index 8216bf2..1d8a9db 100644 --- a/assignment-db-service/app.js +++ b/assignment-db-service/app.js @@ -75,7 +75,7 @@ async function convertToAssignment(req) { console.log("Converted assignment object:", assignment); return assignment; - } +} // Create Assignment app.post("/assignments", async (req, res) => { @@ -86,7 +86,7 @@ app.post("/assignments", async (req, res) => { const newAssignment = await prisma.assignments.create({ data: assignment, }); - + console.log("Assignment created successfully:", newAssignment); res.json({ @@ -104,14 +104,20 @@ app.get("/assignments/instructor/:instructorId", async (req, res) => { try { const { instructorId } = req.params; console.log("InstructorID:", instructorId); + // changes below + // const whereClause = { instructorid: parseInt(instructorId) }; + const whereClause = {} + // changes above + const assignments = await prisma.assignments.findMany({ - where: { instructorid: parseInt(instructorId) }, - orderBy: { assignmentid: 'asc' }, + // where: { instructorid: parseInt(instructorId) }, + where: whereClause, + // orderBy: { assignmentid: 'asc' }, // commnented out to return in the chronological order }); if (assignments.length === 0) { return res - .status(404) + .status(204) .json({ message: "No assignments found for this instructor" }); } @@ -122,6 +128,43 @@ app.get("/assignments/instructor/:instructorId", async (req, res) => { } }); +// // Get Assignments by optional InstructorID (if not provided, return all assignments) +// app.get("/assignments/instructor/:instructorId?", async (req, res) => { +// try { + +// // force all queries to return all assignments even if instructorId is provided + +// // const { instructorId } = req.params; + +// const instructorId = null; + + + +// const whereClause = instructorId +// ? { instructorid: parseInt(instructorId) } +// : {}; + +// const assignments = await prisma.assignments.findMany({ +// where: whereClause, +// orderBy: { assignmentid: 'asc' }, +// }); + +// if (assignments.length === 0) { +// return res.status(204).json({ +// message: instructorId +// ? "No assignments found for this instructor" +// : "No assignments found", +// }); +// } + +// res.json(assignments); +// } catch (err) { +// console.error("Error fetching assignments:", err.message); +// res.status(500).json({ error: err.message }); +// } +// }); + + //Get assignment by assignmentid app.get("/assignments/:id", async (req, res) => { try { diff --git a/assignment-db-service/fly.toml b/assignment-db-service/fly.toml index e16d084..b24e99f 100644 --- a/assignment-db-service/fly.toml +++ b/assignment-db-service/fly.toml @@ -17,18 +17,18 @@ primary_region = 'sea' # processes = ['app'] [[services]] - protocol = 'tcp' - internal_port = 3000 - auto_stop_machines = 'stop' - auto_start_machines = true - ports = [] +protocol = 'tcp' +internal_port = 3000 +auto_stop_machines = 'off' +auto_start_machines = true +ports = [] - [services.concurrency] - type = 'requests' - hard_limit = 1000 - soft_limit = 500 +[services.concurrency] +type = 'requests' +hard_limit = 1000 +soft_limit = 500 [[vm]] - memory = '1gb' - cpu_kind = 'shared' - cpus = 1 +memory = '1gb' +cpu_kind = 'shared' +cpus = 1 diff --git a/assignment-service/fly.toml b/assignment-service/fly.toml index db92c67..300d38a 100644 --- a/assignment-service/fly.toml +++ b/assignment-service/fly.toml @@ -9,18 +9,18 @@ primary_region = 'sea' [build] [[services]] - protocol = 'tcp' - internal_port = 8080 - auto_stop_machines = 'stop' - auto_start_machines = true - ports = [] # ✅ No public ports = no public IP +protocol = 'tcp' +internal_port = 8080 +auto_stop_machines = 'off' +auto_start_machines = true +ports = [] # ✅ No public ports = no public IP - [services.concurrency] - type = 'requests' - hard_limit = 1000 - soft_limit = 500 +[services.concurrency] +type = 'requests' +hard_limit = 1000 +soft_limit = 500 [[vm]] - memory = '1gb' - cpu_kind = 'shared' - cpus = 1 +memory = '1gb' +cpu_kind = 'shared' +cpus = 1 diff --git a/assignment-service/package-lock.json b/assignment-service/package-lock.json index d8dba1c..9e5053c 100644 --- a/assignment-service/package-lock.json +++ b/assignment-service/package-lock.json @@ -16,6 +16,7 @@ "dotenv": "^16.5.0", "express": "^5.1.0", "express-session": "^1.18.1", + "http-proxy-middleware": "^3.0.5", "multer": "^1.4.5-lts.2", "nodemon": "^3.1.9", "passport": "^0.7.0" @@ -44,6 +45,24 @@ "node-pre-gyp": "bin/node-pre-gyp" } }, + "node_modules/@types/http-proxy": { + "version": "1.17.16", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.16.tgz", + "integrity": "sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "22.15.29", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.29.tgz", + "integrity": "sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -755,6 +774,12 @@ "node": ">= 0.6" } }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, "node_modules/events": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", @@ -1207,6 +1232,54 @@ "node": ">= 0.8" } }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.5.tgz", + "integrity": "sha512-GLZZm1X38BPY4lkXA01jhwxvDoOkkXqjgVyUzVxiEK4iuRu03PZoYHhHRwxnfhQMDuaxi3vVri0YgSro/1oWqg==", + "license": "MIT", + "dependencies": { + "@types/http-proxy": "^1.17.15", + "debug": "^4.3.6", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.3", + "is-plain-object": "^5.0.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -1384,6 +1457,15 @@ "node": ">=0.12.0" } }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", @@ -1493,6 +1575,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/mime-db": { "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", @@ -2053,6 +2148,12 @@ "node": ">=8.10.0" } }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -2514,6 +2615,12 @@ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", "license": "MIT" }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", diff --git a/assignment-service/package.json b/assignment-service/package.json index c4df28e..c743294 100644 --- a/assignment-service/package.json +++ b/assignment-service/package.json @@ -17,6 +17,7 @@ "dotenv": "^16.5.0", "express": "^5.1.0", "express-session": "^1.18.1", + "http-proxy-middleware": "^3.0.5", "multer": "^1.4.5-lts.2", "nodemon": "^3.1.9", "passport": "^0.7.0" diff --git a/assignment-service/routes/InstructorRouter.js b/assignment-service/routes/InstructorRouter.js index c2187b0..f3fc259 100644 --- a/assignment-service/routes/InstructorRouter.js +++ b/assignment-service/routes/InstructorRouter.js @@ -117,6 +117,7 @@ intructorRouter.get("/details/:id", async (req, res) => { ); console.log("Response from DB_ASSIGNMENT_SERVICE_URL:", response.data); + console.log("Response status:", response.status); res.status(response.status).json(response.data); } catch (error) { console.error("Error fetching assignment details:", error.message); @@ -129,16 +130,20 @@ intructorRouter.get( "/list/:id", // passport.authenticate("jwt", { session: false }), async (req, res) => { + console.log("/list/:id endpoint hit"); // if (req.isAuthenticated()) { try { const instructorId = req.params.id; - // console.log("Fetching assignments for instructorId:", instructorId); + console.log("Fetching assignments for instructorId:", instructorId); const response = await axios.get( `${DB_ASSIGNMENT_SERVICE_URL}/assignments/instructor/${instructorId}` ); - // console.log("Response from DB_ASSIGNMENT_SERVICE_URL:", response.data); + console.log("Response from DB_ASSIGNMENT_SERVICE_URL:", response.data); + console.log("Response status:", response.status); res.status(response.status).json(response.data); } catch (error) { + console.log("Error fetching assignments:", error.response); + console.error("Error fetching assignments:", error.message); res.status(error.response?.status || 500).json({ error: error.message }); } // } else { @@ -191,10 +196,10 @@ intructorRouter.delete( if (!assignmentData) { return res.status(404).json({ error: "Assignment not found" }); - } + } // Delete the Battlesnake API - if(assignmentData.appname){ + if (assignmentData.appname) { console.log(`Deleting Battlesnake API: ${assignmentData.appname}`); const deployResponse = await axios.post(`${DEPLOY_API_URL}/${assignmentData.appname}/delete`, { "appName": assignmentData.appname @@ -206,7 +211,7 @@ intructorRouter.delete( } console.log('Response from DEPLOY_API_URL:', deployResponse.data); } - + console.log("Deleting assignment from database:", assignmentId); const response = await axios.delete( `${DB_ASSIGNMENT_SERVICE_URL}/assignments/${assignmentId}` @@ -231,7 +236,7 @@ intructorRouter.get( `${DB_ASSIGNMENT_SERVICE_URL}/assignments/appname/${appName}` ); console.log("Response data:", response.data); - res.status(response.status).json({"exists": (response.data !== null && response.data !== undefined)}); + res.status(response.status).json({ "exists": (response.data !== null && response.data !== undefined) }); } catch (error) { console.error("Error fetching assignment by app name:", error.message); res.status(error.response?.status || 500).json({ error: error.message }); @@ -251,7 +256,7 @@ intructorRouter.get( `${DB_ASSIGNMENT_SERVICE_URL}/assignments/${qrcode}` ); console.log("Response data:", response.data); - res.status(response.status).json({"exists": (response.data !== null && response.data !== undefined)}); + res.status(response.status).json({ "exists": (response.data !== null && response.data !== undefined) }); } catch (error) { console.error("Error fetching assignment by QR code:", error.message); res.status(error.response?.status || 500).json({ error: error.message }); diff --git a/assignment-service/routes/StudentRouter.js b/assignment-service/routes/StudentRouter.js index 72930c9..f7a8f98 100644 --- a/assignment-service/routes/StudentRouter.js +++ b/assignment-service/routes/StudentRouter.js @@ -7,24 +7,24 @@ const DB_ASSIGNMENT_SERVICE_URL = process.env.DB_ASSIGNMENT_SERVICE_URL; const DEPLOY_API_URL = process.env.DEPLOY_API_URL || "http://localhost:3600"; studentRouter.post("/save", async (req, res) => { - //get the app name and code and save the latest jupyter file in s3 bucket - const { appName, code } = req.body; + //get the app name and code and save the latest jupyter file in s3 bucket + const { appName, code } = req.body; - console.log("Received save request for app:", appName); + console.log("Received save request for app:", appName); - const notebook = { - cells: [ + const notebook = { + cells: [ { cell_type: "code", execution_count: null, metadata: { - language: "python" + language: "python" }, outputs: [], source: code.split('\n').map(line => line + '\n') } - ], - metadata: { + ], + metadata: { kernelspec: { display_name: "Python 3", language: "python", @@ -34,36 +34,36 @@ studentRouter.post("/save", async (req, res) => { name: "python", version: "3.x" } - }, - nbformat: 4, - nbformat_minor: 5 - }; + }, + nbformat: 4, + nbformat_minor: 5 + }; - // Convert the notebook object to a JSON string and then to base64 - const jsonString = JSON.stringify(notebook, null, 2); - const base64 = Buffer.from(jsonString, 'utf-8').toString('base64'); + // Convert the notebook object to a JSON string and then to base64 + const jsonString = JSON.stringify(notebook, null, 2); + const base64 = Buffer.from(jsonString, 'utf-8').toString('base64'); - const notebookName = `${Date.now()}-notebook.ipynb`; - console.log("DEPLOY_API_URL:", DEPLOY_API_URL); - console.log("Uploading notebook:", notebookName, "to app:", appName); + const notebookName = `${Date.now()}-notebook.ipynb`; + console.log("DEPLOY_API_URL:", DEPLOY_API_URL); + console.log("Uploading notebook:", notebookName, "to app:", appName); - await fetch(`${DEPLOY_API_URL}/${appName}/upload`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ notebookName: notebookName, fileContentBase64: base64 }) + await fetch(`${DEPLOY_API_URL}/${appName}/upload`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ notebookName: notebookName, fileContentBase64: base64 }) + }) + .then((response) => { + if (!response.ok) throw new Error("Failed to save notebook"); + return response.json(); }) - .then((response) => { - if (!response.ok) throw new Error("Failed to save notebook"); - return response.json(); - }) - .then((data) => { - console.log("Notebook saved successfully:", data); - res.status(200).json(data); - }) - .catch((error) => { - console.error("Error saving notebook:", error.message); - res.status(500).json({ error: error.message }); - }); + .then((data) => { + console.log("Notebook saved successfully:", data); + res.status(200).json(data); + }) + .catch((error) => { + console.error("Error saving notebook:", error.message); + res.status(500).json({ error: error.message }); + }); }); studentRouter.get("/assignment/:qrnum", (req, res) => { @@ -105,7 +105,7 @@ studentRouter.post("/verify", async (req, res) => { const isPasswordValid = await bcrypt.compare( password, - response.data.passwordhash + response.data.passwordhash ); console.log("Password validation result:", isPasswordValid); diff --git a/auth-service/fly.toml b/auth-service/fly.toml index 34f4cbc..8536834 100644 --- a/auth-service/fly.toml +++ b/auth-service/fly.toml @@ -9,34 +9,34 @@ primary_region = 'sea' [build] [env] - PORT = '8080' +PORT = '8080' [http_service] - internal_port = 8080 - force_https = true - auto_stop_machines = 'stop' - auto_start_machines = true - min_machines_running = 0 - processes = ['app'] +internal_port = 8080 +force_https = true +auto_stop_machines = 'off' +auto_start_machines = true +min_machines_running = 0 +processes = ['app'] [[services]] - protocol = 'tcp' - internal_port = 8080 +protocol = 'tcp' +internal_port = 8080 - [[services.ports]] - port = 80 - handlers = ['http'] +[[services.ports]] +port = 80 +handlers = ['http'] - [[services.ports]] - port = 443 - handlers = ['tls', 'http'] +[[services.ports]] +port = 443 +handlers = ['tls', 'http'] - [[services.tcp_checks]] - interval = '10s' - timeout = '2s' - grace_period = '5s' +# [[services.tcp_checks]] +# interval = '10s' +# timeout = '2s' +# grace_period = '5s' [[vm]] - memory = '1gb' - cpu_kind = 'shared' - cpus = 1 +memory = '1gb' +cpu_kind = 'shared' +cpus = 1 diff --git a/auth-service/old.env.development b/auth-service/old.env.development new file mode 100644 index 0000000..88179ae --- /dev/null +++ b/auth-service/old.env.development @@ -0,0 +1,11 @@ +NODE_ENV=development + +GOOGLE_CLIENT_ID="485880105639-1in8tvb6ondnn198rasuj2d8ank06ntp.apps.googleusercontent.com" +GOOGLE_CLIENT_SECRET="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" +GOOGLE_CALLBACK_URL="http://localhost:8080/auth/google/callback" +LOGIN_REDIRECT_URL="http://localhost:5173/" +ACCEPTED_ORIGINS=http://localhost:3000,http://localhost:8081,http://localhost:3001,http://localhost:5173 +ASSIGNMENT_SERVICE_URL="http://localhost:8082" +DB_USER_SERVICE_URL="http://localhost:3100/" +AUTH_SESSION_KEY="f3f4d8e6b17a4b3abdc8e9a2c0457aaf91c0d5f6e3b7a9c8df624bd71ea35f42" +PORT=8080 \ No newline at end of file diff --git a/auth-service/passport.js b/auth-service/passport.js index 14f3481..9c07ad7 100644 --- a/auth-service/passport.js +++ b/auth-service/passport.js @@ -36,8 +36,11 @@ passport.use( try { console.log("Sending request to external auth service..."); + console.log( + `Request URL: ${process.env.ASSIGNMENT_SERVICE_URL}student/verify` + ); const response = await axios.post( - `${process.env.ASSIGNMENT_SERVICE_URL}/student/verify`, + `${process.env.ASSIGNMENT_SERVICE_URL}student/verify`, { qrNumber, password, @@ -64,6 +67,7 @@ passport.use( passport.serializeUser((user, done) => { console.log("Serializing user:", user); + console.log(process.env.NODE_ENV) // done(null, user); done(null, { userId: user.qrcodenumber || user.userId, diff --git a/auth-service/routes/assignment.js b/auth-service/routes/assignment.js new file mode 100644 index 0000000..3beb921 --- /dev/null +++ b/auth-service/routes/assignment.js @@ -0,0 +1,32 @@ +// routes/assignment.js +const express = require("express"); +const { createProxyMiddleware } = require("http-proxy-middleware"); + +const router = express.Router(); + +// Middleware to check authentication +function isAuthenticated(req, res, next) { + if (req.isAuthenticated && req.isAuthenticated()) { + return next(); + } else { + return res.status(401).json({ error: "Not authenticated, visit /login" }); + } +} + +// Proxy configuration +const proxy = createProxyMiddleware({ + target: "http://assignment-service.internal:8080", + changeOrigin: true, + pathRewrite: { + "^/assignment": "", // remove `/assignment` prefix when forwarding + }, + onProxyReq(proxyReq, req, res) { + // Optional: log or modify headers + console.log(`Proxying ${req.method} request to ${proxyReq.protocol}//${proxyReq.host}${proxyReq.path}`); + }, +}); + +// Apply both middleware +router.use(isAuthenticated, proxy); + +module.exports = router; diff --git a/auth-service/routes/auth.js b/auth-service/routes/auth.js index 45adef9..a5eac1c 100644 --- a/auth-service/routes/auth.js +++ b/auth-service/routes/auth.js @@ -6,6 +6,7 @@ const express = require("express"); const bodyParser = require("body-parser"); + auth.use(express.json()); auth.use(bodyParser.urlencoded({ extended: true })); @@ -20,7 +21,7 @@ auth.get( async (req, res) => { console.log("Google callback endpoint hit"); if (req.user) { - console.log(`${process.env.DB_USER_SERVICE_URL}instructor/register-user`); + // console.log(`${process.env.DB_USER_SERVICE_URL}instructor/register-user`); axios .post(`${process.env.DB_USER_SERVICE_URL}instructor/register-user`, { user: req.user, @@ -35,8 +36,25 @@ auth.get( console.error("Login error:", err); return res.status(500).send("Login failed"); } - return res.redirect(process.env.LOGIN_REDIRECT_URL); + // Force session save before redirect + req.session.save((err) => { + if (err) { + console.error("Session save error:", err); + return res.status(500).send("Session save failed"); + } + console.log("Session saved successfully"); + return res.redirect(process.env.LOGIN_REDIRECT_URL); + }); + console.log("User logged in successfully:", req.session); }); + + // req.login(req.user, (err) => { + // if (err) { + // console.error("Login error:", err); + // return res.status(500).send("Login failed"); + // } + // return res.redirect(process.env.LOGIN_REDIRECT_URL); + // }); }) .catch((error) => { console.error("Error registering user:", error.message); @@ -67,6 +85,19 @@ auth.get("/login/failed", (req, res) => { }); }); + +// Set a test cookie +auth.get("/test-cookie", (req, res) => { + res.cookie("test-session", "123", { + httpOnly: true, + secure: true, + sameSite: "none", + domain: "snake-byte.org", // Set the domain to allow cross-origin requests + }); + res.send("Cookie set"); +}); + + auth.get("/google", passport.authenticate("google", ["profile", "email"])); auth.post( diff --git a/auth-service/server.js b/auth-service/server.js index ce39321..7103c54 100644 --- a/auth-service/server.js +++ b/auth-service/server.js @@ -6,19 +6,86 @@ const passport = require("passport"); const passportSetup = require("./passport"); const authRoute = require("./routes/auth"); const apiRoute = require("./routes/api"); +const assignmentRoute = require("./routes/assignment"); + const session = require("express-session"); const app = express(); -// console.log("AUTH_URL:", process.env.AUTH_URL); +app.use((req, res, next) => { + console.log('Protocol before proxy:', req.protocol, 'Secure:', req.secure); + next(); +}); +app.use((req, res, next) => { + console.log('req.secure:', req.secure); + console.log('x-forwarded-proto:', req.headers['x-forwarded-proto']); + next(); +}); + + +app.set('trust proxy', true); // proxy magic that needs to happen + + +// app.use((req, res, next) => { +// console.log('Protocol after proxy:', req.protocol, 'Secure:', req.secure); +// next(); +// }); + + +const allowedOrigins = process.env.ACCEPTED_ORIGINS.split(","); + +const corsOptions = { + // origin: function (origin, callback) { + // if (!origin || allowedOrigins.includes(origin)) { + // callback(null, origin); // allow the request + // } else { + // callback(new Error("Not allowed by CORS")); + // } + // }, + origin: "https://snake-byte.org", // Replace with your frontend URL + // methods: ["GET", "POST", "OPTIONS"], + // allowedHeaders: ["Content-Type", "Authorization"], + credentials: true, +}; + +app.use(cors(corsOptions)); + + +// app.use((req, res, next) => { +// console.log("Session:", req.session); +// console.log("User:", req.user); +// next(); +// }); + +// app.use((req, res, next) => { +// res.cookie( +// 'myTestCookie', 'helloWorld', +// { +// httpOnly: true, +// secure: true, // Set to true if using HTTPS +// sameSite: 'none', // Use 'none' for cross-origin requests +// domain: 'jank-frontend.fly.dev', // Set the domain to allow cross-origin requests +// maxAge: 24 * 60 * 60 * 1000, // 1 day +// path: '/', // Set the path for the cookie +// } +// ); +// next(); +// }); + + +console.log("AUTH_URL:", process.env.AUTH_URL); const isProduction = process.env.NODE_ENV === "production"; app.use( session({ secret: process.env.AUTH_SESSION_KEY, resave: false, - saveUninitialized: false, + saveUninitialized: false, // true in development, false in production cookie: { + httpOnly: true, // true in production for sec maxAge: 24 * 60 * 60 * 1000, // 1 day + secure: true, //true // only true in production over HTTPS + sameSite: 'none', // or 'none' if using cross-origin + // domain: '', // Set the domain to allow cross-origin requests, or not? //keep production security settings below disable for the mean-time because we need to integrate redis session for cross-origin to work properly //sameSite: isProduction ? "none" : "lax", // or 'none' if using cross-origin //secure: isProduction, // only true in production over HTTPS @@ -26,28 +93,36 @@ app.use( }) ); +// console.log("this is the session", session); +// console.log("this is the cookie", session.cookie); + app.use(passport.initialize()); app.use(passport.session()); -const allowedOrigins = process.env.ACCEPTED_ORIGINS.split(","); -const corsOptions = { - origin: function (origin, callback) { - if (!origin || allowedOrigins.includes(origin)) { - callback(null, origin); // allow the request - } else { - callback(new Error("Not allowed by CORS")); - } - }, - methods: ["GET", "POST", "OPTIONS"], - allowedHeaders: ["Content-Type", "Authorization"], - credentials: true, -}; -app.use(cors(corsOptions)); +// app.use((req, res, next) => { +// res.on("finish", () => { +// console.log(`Response Status: ${res.statusCode}`); +// console.log(`Response Headers:`, res.getHeaders()); +// }); +// next(); +// }) + +// app.use((req, res, next) => { +// res.on("finish", () => { +// const headers = res.getHeaders(); +// console.log("Set-Cookie header:", headers["set-cookie"]); +// }); +// next(); +// }); + + +app.use("/assignment", assignmentRoute); app.use("/api", apiRoute); app.use("/auth", authRoute); -const port = process.env.PORT || 8080; -app.listen(port, () => console.log(`Listening on port ${port}...`)); +const port = 8080; +console.log(`Listening on port ${port}...`); +app.listen(port, '0.0.0.0'); diff --git a/deployment-service/fly.toml b/deployment-service/fly.toml index eee5ff1..70ee035 100644 --- a/deployment-service/fly.toml +++ b/deployment-service/fly.toml @@ -7,21 +7,21 @@ app = 'deployment-service' primary_region = 'sea' [build] - dockerfile = 'Dockerfile' +dockerfile = 'Dockerfile' [[services]] - protocol = 'tcp' - internal_port = 8080 - auto_stop_machines = 'stop' - auto_start_machines = true - ports = [] +protocol = 'tcp' +internal_port = 8080 +auto_stop_machines = 'off' +auto_start_machines = true +ports = [] - [services.concurrency] - type = 'requests' - hard_limit = 1000 - soft_limit = 500 +[services.concurrency] +type = 'requests' +hard_limit = 1000 +soft_limit = 500 [[vm]] - memory = '1gb' - cpu_kind = 'shared' - cpus = 1 +memory = '1gb' +cpu_kind = 'shared' +cpus = 1 diff --git a/deployment-service/src/index.js b/deployment-service/src/index.js index fc09763..ccb3863 100644 --- a/deployment-service/src/index.js +++ b/deployment-service/src/index.js @@ -122,7 +122,7 @@ app.post("/deploy", async (req, res) => { http_service: { internal_port: 8000, force_https: true, - auto_stop_machines: "stop", + auto_stop_machines: "suspend", auto_start_machines: true, min_machines_running: 0, processes: ["app"], @@ -147,14 +147,14 @@ app.post("/deploy", async (req, res) => { console.log("Machine config:", JSON.stringify(machineConfig, null, 2)); await fly.post(`/apps/${appName}/machines`, machineConfig); - console.log("Allocating IPv4 via GraphQL API"); - const v4resp = await gqlClient.post("", { - query: ALLOCATE_IP_MUTATION, - variables: { input: { appId: appName, type: "v4" } }, - }); - const ipv4 = v4resp.data.data.allocateIpAddress.ipAddress.address; - console.log("Allocated IPv4:", ipv4); - + // console.log("Allocating IPv4 via GraphQL API"); + // const v4resp = await gqlClient.post("", { + // query: ALLOCATE_IP_MUTATION, + // variables: { input: { appId: appName, type: "v4" } }, + // }); + // const ipv4 = v4resp.data.data.allocateIpAddress.ipAddress.address; + // console.log("Allocated IPv4:", ipv4); + const ipv4 = "snake-byte.org"; // Placeholder for IPv4 allocation, if needed console.log("Allocating IPv6 via GraphQL API"); const v6resp = await gqlClient.post("", { query: ALLOCATE_IP_MUTATION, @@ -175,7 +175,7 @@ app.post("/deploy", async (req, res) => { status: "created", app: appName, ipv4, - ipv6 : firstPrivateIp, + ipv6: firstPrivateIp, url_v4: `http://${ipv4}`, url_v6: `http://[${firstPrivateIp}]`, }); @@ -255,7 +255,7 @@ app.post("/:appName/delete", async (req, res) => { const appCheck = await fly.get(`/apps/${appName}`); console.log("App check response:", appCheck.status); - + await fly.delete(`/apps/${appName}`); return res.json({ status: "deleted", app: appName }); diff --git a/gameboard-service/.dockerignore b/gameboard-service/.dockerignore index 7514b28..d9abe68 100644 --- a/gameboard-service/.dockerignore +++ b/gameboard-service/.dockerignore @@ -1,22 +1,24 @@ +# flyctl launch added from .gitignore # General -.DS_Store -.vscode +**/.DS_Store +**/.vscode # Node -node_modules +**/node_modules # SvelteKit -.output -.svelte-kit -/build -/package +**/.output +**/.svelte-kit +build +package -.env -.env.* -!.env.example +**/.env +**/.env.* +!**/.env.example -vite.config.js.timestamp-* -vite.config.ts.timestamp-* +**/vite.config.js.timestamp-* +**/vite.config.ts.timestamp-* # Netlify -.netlify +**/.netlify +fly.toml diff --git a/gameboard-service/.github/workflows/fly-deploy.yml b/gameboard-service/.github/workflows/fly-deploy.yml deleted file mode 100644 index b0c246e..0000000 --- a/gameboard-service/.github/workflows/fly-deploy.yml +++ /dev/null @@ -1,18 +0,0 @@ -# See https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/ - -name: Fly Deploy -on: - push: - branches: - - main -jobs: - deploy: - name: Deploy app - runs-on: ubuntu-latest - concurrency: deploy-group # optional: ensure only one action runs at a time - steps: - - uses: actions/checkout@v4 - - uses: superfly/flyctl-actions/setup-flyctl@master - - run: flyctl deploy --remote-only - env: - FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} diff --git a/gameboard-service/.github/workflows/release.yaml b/gameboard-service/.github/workflows/release.yaml deleted file mode 100644 index b2e1543..0000000 --- a/gameboard-service/.github/workflows/release.yaml +++ /dev/null @@ -1,29 +0,0 @@ -name: Release - -on: - release: - types: [published] - branches: [main] - -jobs: - tests: - name: Tests - uses: ./.github/workflows/tests.yaml - - deploy: - name: netlify deploy - needs: [tests] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - run: echo ${{ github.event.release.tag_name }} > ./static/version - - run: npm ci - - run: npm install -g netlify-cli - - run: netlify build - env: - NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} - NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} - - run: netlify deploy --prod --message ${{ github.event.release.tag_name }} - env: - NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} - NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} diff --git a/gameboard-service/.github/workflows/tests.yaml b/gameboard-service/.github/workflows/tests.yaml deleted file mode 100644 index be84e2c..0000000 --- a/gameboard-service/.github/workflows/tests.yaml +++ /dev/null @@ -1,43 +0,0 @@ -name: Tests - -on: - push: # Branch pushes only, not tags - branches: - - "**" - workflow_call: # Allow other workflows to call this one - -jobs: - lint: - name: npm run lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - run: npm ci - - run: npm run lint - - check: - name: npm run check - needs: [lint] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - run: npm ci - - run: npm run check - - unit: - name: npm run test:unit - needs: [check] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - run: npm ci - - run: npm run test:unit - - build: - name: npm run build - needs: [check] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - run: npm ci - - run: npm run build diff --git a/gameboard-service/Dockerfile b/gameboard-service/Dockerfile index 88bd54c..0e7e123 100644 --- a/gameboard-service/Dockerfile +++ b/gameboard-service/Dockerfile @@ -1,47 +1,27 @@ -# syntax = docker/dockerfile:1 +# Build Stage +FROM node:18 AS build -# Adjust NODE_VERSION as desired -ARG NODE_VERSION=22.13.0 -FROM node:${NODE_VERSION}-slim AS base - -LABEL fly_launch_runtime="SvelteKit" - -# SvelteKit app lives here WORKDIR /app -# Set production environment -ENV NODE_ENV="production" -ENV PORT="3005" +COPY package*.json ./ +RUN npm install -# Throw-away build stage to reduce size of final image -FROM base AS build - -# Install packages needed to build node modules -RUN apt-get update -qq && \ - apt-get install --no-install-recommends -y build-essential node-gyp pkg-config python-is-python3 - -# Install node modules -COPY .npmrc package-lock.json package.json ./ -RUN npm ci --include=dev - -# Copy application code COPY . . - -# Build application RUN npm run build -# Remove development dependencies -RUN npm prune --omit=dev +# Serve Stage +FROM nginx:stable-alpine +# Copy built frontend into nginx public folder +COPY --from=build /app/build /usr/share/nginx/html -# Final stage for app image -FROM base +# Remove default Nginx config +RUN rm /etc/nginx/conf.d/default.conf -# Copy built application -COPY --from=build /app/build /app/build -COPY --from=build /app/node_modules /app/node_modules -COPY --from=build /app/package.json /app +# Add custom Nginx config +COPY nginx.conf /etc/nginx/conf.d/default.conf -# Start the server by default, this can be overwritten at runtime -EXPOSE 3005 -CMD [ "node", "./build/index.js" ] +# Expose port 80 (not 8080 inside container) +EXPOSE 3000 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/gameboard-service/fly.toml b/gameboard-service/fly.toml index 10ea6d4..8d94861 100644 --- a/gameboard-service/fly.toml +++ b/gameboard-service/fly.toml @@ -1,20 +1,23 @@ -app = "gameboard-service-aged-glitter-8141" -primary_region = "sea" +# fly.toml app configuration file generated for gameboard-service on 2025-06-01T21:09:25-07:00 +# +# See https://fly.io/docs/reference/configuration/ for information about how to use this file. +# -[env] - PORT = "3005" +app = 'gameboard-service' +primary_region = 'sea' [build] + [http_service] - internal_port = 3005 - force_https = true - auto_stop_machines = "stop" - auto_start_machines = true - min_machines_running = 0 - processes = ["app"] +internal_port = 3000 +force_https = true +auto_stop_machines = 'off' +auto_start_machines = true +min_machines_running = 0 +processes = ['app'] [[vm]] - memory = "1gb" - cpu_kind = "shared" - cpus = 1 +memory = '512mb' +cpu_kind = 'shared' +cpus = 1 diff --git a/gameboard-service/nginx.conf b/gameboard-service/nginx.conf new file mode 100644 index 0000000..9b792bb --- /dev/null +++ b/gameboard-service/nginx.conf @@ -0,0 +1,25 @@ + +server { + listen 3000; + server_name localhost; + + root /usr/share/nginx/html; + index index.html; + + # Enable compression + gzip on; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript; + + location / { + try_files $uri $uri/ /index.html; + expires 1h; + add_header Cache-Control "public, no-transform"; + } + + # Serve pre-compressed files if they exist + location ~ \.(?:js|css|html)$ { + gzip_static on; + expires 1h; + add_header Cache-Control "public, no-transform"; + } +} \ No newline at end of file diff --git a/gameboard-service/package.json b/gameboard-service/package.json index d98d33d..6c2ab6c 100644 --- a/gameboard-service/package.json +++ b/gameboard-service/package.json @@ -49,4 +49,4 @@ "vitest": "^0.32.2" }, "type": "module" -} +} \ No newline at end of file diff --git a/gameboard-service/src/lib/playback/types.ts b/gameboard-service/src/lib/playback/types.ts index 58cd6f1..95c82c2 100644 --- a/gameboard-service/src/lib/playback/types.ts +++ b/gameboard-service/src/lib/playback/types.ts @@ -88,8 +88,8 @@ export function engineEventToFrame( return { turn: engineGameEvent.Turn, - width: engineGameInfo.Game.Width, - height: engineGameInfo.Game.Height, + width: 11, + height: 11, snakes: engineGameEvent.Snakes.map(engineSnakeToSnake), food: engineGameEvent.Food.map(engineCoordsToPoint), hazards: engineGameEvent.Hazards.map(engineCoordsToPoint), diff --git a/gameboard-service/src/lib/settings/stores.ts b/gameboard-service/src/lib/settings/stores.ts index a59695d..72cca3b 100644 --- a/gameboard-service/src/lib/settings/stores.ts +++ b/gameboard-service/src/lib/settings/stores.ts @@ -56,7 +56,8 @@ export type Settings = { export function getDefaultSettings(): Settings { return { autoplay: false, - engine: "https://engine.battlesnake.com", + // engine: "https://engine.battlesnake.com", + engine: "https://snake-server.fly.dev", fps: 6, game: "", loop: false, diff --git a/gameboard-service/svelte.config.js b/gameboard-service/svelte.config.js index aae3794..9f6c8b5 100644 --- a/gameboard-service/svelte.config.js +++ b/gameboard-service/svelte.config.js @@ -1,23 +1,40 @@ -// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. -// If your environment is not supported or you settled on a specific environment, switch out the adapter. -// See https://kit.svelte.dev/docs/adapters for more information about adapters. +// // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. +// // If your environment is not supported or you settled on a specific environment, switch out the adapter. +// // See https://kit.svelte.dev/docs/adapters for more information about adapters. +// // import adapter from '@sveltejs/adapter-node'; // import adapter from '@sveltejs/adapter-node'; -import adapter from '@sveltejs/adapter-node'; -// import adapter from "@sveltejs/adapter-static"; +// // import adapter from "@sveltejs/adapter-static"; +// import { vitePreprocess } from '@sveltejs/kit/vite'; + +// /** @type {import('@sveltejs/kit').Config} */ +// const config = { +// // Consult https://kit.svelte.dev/docs/integrations#preprocessors +// // for more information about preprocessors +// preprocess: vitePreprocess(), +// kit: { +// adapter: adapter({ +// // This option specifies the output directory for the build +// out: 'build' +// }) +// } +// }; + +// export default config; +import adapter from '@sveltejs/adapter-static'; // Change this line import { vitePreprocess } from '@sveltejs/kit/vite'; /** @type {import('@sveltejs/kit').Config} */ const config = { - // Consult https://kit.svelte.dev/docs/integrations#preprocessors - // for more information about preprocessors preprocess: vitePreprocess(), kit: { adapter: adapter({ - // This option specifies the output directory for the build - out: 'build' + pages: 'build', + assets: 'build', + fallback: 'index.html', + precompress: true // Enable Brotli & Gzip precompression }) } }; -export default config; +export default config; \ No newline at end of file diff --git a/gameboard-service/vite.config.ts b/gameboard-service/vite.config.ts index a35c669..80ac3a7 100644 --- a/gameboard-service/vite.config.ts +++ b/gameboard-service/vite.config.ts @@ -10,6 +10,6 @@ export default defineConfig({ }, server: { host: "0.0.0.0", - port: 3005 + port: 3000 } }); diff --git a/proxy-service/fly.toml b/proxy-service/fly.toml index e62f4e3..db322a1 100644 --- a/proxy-service/fly.toml +++ b/proxy-service/fly.toml @@ -9,34 +9,34 @@ primary_region = 'sea' [build] [env] - PORT = '8080' +PORT = '8080' [http_service] - internal_port = 8080 - force_https = true - auto_stop_machines = 'stop' - auto_start_machines = true - min_machines_running = 0 - processes = ['app'] +internal_port = 8080 +force_https = true +auto_stop_machines = 'off' +auto_start_machines = true +min_machines_running = 0 +processes = ['app'] [[services]] - protocol = 'tcp' - internal_port = 8080 +protocol = 'tcp' +internal_port = 8080 - [[services.ports]] - port = 80 - handlers = ['http'] +[[services.ports]] +port = 80 +handlers = ['http'] - [[services.ports]] - port = 443 - handlers = ['tls', 'http'] +[[services.ports]] +port = 443 +handlers = ['tls', 'http'] - [[services.tcp_checks]] - interval = '10s' - timeout = '2s' - grace_period = '5s' +[[services.tcp_checks]] +interval = '10s' +timeout = '2s' +grace_period = '5s' [[vm]] - memory = '1gb' - cpu_kind = 'shared' - cpus = 1 +memory = '1gb' +cpu_kind = 'shared' +cpus = 1 diff --git a/user-db-service/app.js b/user-db-service/app.js index af23195..3a95d91 100644 --- a/user-db-service/app.js +++ b/user-db-service/app.js @@ -7,7 +7,7 @@ const instructorRouter = require("./routes/InstructorRouter"); const studentRouter = require("./routes/StudentRouter"); // require('dotenv').config(); // prisma client already loads .env apparently, double check before deploying -const port = process.env.NODE_PORT; // Use env for port +const port = process.env.NODE_PORT || 3000; // Use env for port console.log('NODE_PORT:', port); const prisma = new PrismaClient(); @@ -19,6 +19,7 @@ app.use("/student", studentRouter); app.use("/admin", adminRouter); app.use("/instructor", instructorRouter); -app.listen(port, () => { - console.log(`Server running at http://localhost:${port}`); -}); + +console.log(`Server running at http://localhost:${port}`); +// app.listen(port, '0.0.0.0'); +app.listen(3000, '::'); // im terrible i know - Bhav diff --git a/user-db-service/fly.toml b/user-db-service/fly.toml index 30dbd0d..d2b1428 100644 --- a/user-db-service/fly.toml +++ b/user-db-service/fly.toml @@ -16,27 +16,27 @@ primary_region = "sea" # processes = ["app"] [[services]] - protocol = "tcp" - internal_port = 3000 - internal_only = true # Makes this service only accessible internally - auto_start_machines = true - auto_stop_machines = true +protocol = "tcp" +internal_port = 3000 +internal_only = true # Makes this service only accessible internally +auto_start_machines = true +auto_stop_machines = "off" - # Removed public port exposure - # [[services.ports]] - # port = 80 - # handlers = ["http"] +# Removed public port exposure +# [[services.ports]] +# port = 80 +# handlers = ["http"] - # [[services.ports]] - # port = 443 - # handlers = ["tls", "http"] +# [[services.ports]] +# port = 443 +# handlers = ["tls", "http"] - [services.concurrency] - type = "requests" - hard_limit = 1000 - soft_limit = 500 +[services.concurrency] +type = "requests" +hard_limit = 1000 +soft_limit = 500 [[vm]] - memory = '1gb' - cpu_kind = "shared" - cpus = 1 +memory = '1gb' +cpu_kind = "shared" +cpus = 1