minor changes

This commit is contained in:
Bhavnoor Singh Saroya 2025-08-25 14:23:55 -07:00
parent 909b29dfb5
commit af2e220116
30 changed files with 598 additions and 350 deletions

View file

@ -75,7 +75,7 @@ async function convertToAssignment(req) {
console.log("Converted assignment object:", assignment);
return assignment;
}
}
// Create Assignment
app.post("/assignments", async (req, res) => {
@ -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 {

View file

@ -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

View file

@ -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

View file

@ -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",

View file

@ -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"

View file

@ -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 {
@ -194,7 +199,7 @@ intructorRouter.delete(
}
// 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
@ -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 });

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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;

View file

@ -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");
}
// 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(

View file

@ -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');

View file

@ -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

View file

@ -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}]`,
});

View file

@ -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

View file

@ -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 }}

View file

@ -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 }}

View file

@ -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

View file

@ -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;"]

View file

@ -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

View file

@ -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";
}
}

View file

@ -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),

View file

@ -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,

View file

@ -1,21 +1,38 @@
// 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
})
}
};

View file

@ -10,6 +10,6 @@ export default defineConfig({
},
server: {
host: "0.0.0.0",
port: 3005
port: 3000
}
});

View file

@ -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

View file

@ -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

View file

@ -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