Merge pull request #17 from JBB0807/save-backend-b
Working save and deploy
This commit is contained in:
commit
771f1e680c
15 changed files with 1235 additions and 86008 deletions
|
|
@ -4,11 +4,67 @@ const axios = require("axios");
|
|||
const bcrypt = require("bcrypt");
|
||||
require("dotenv").config();
|
||||
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;
|
||||
|
||||
studentRouter.post("/save", (req, res) => {});
|
||||
const notebook = {
|
||||
cells: [
|
||||
{
|
||||
cell_type: "code",
|
||||
execution_count: null,
|
||||
metadata: {
|
||||
language: "python"
|
||||
},
|
||||
outputs: [],
|
||||
source: code.split('\n').map(line => line + '\n')
|
||||
}
|
||||
],
|
||||
metadata: {
|
||||
kernelspec: {
|
||||
display_name: "Python 3",
|
||||
language: "python",
|
||||
name: "python3"
|
||||
},
|
||||
language_info: {
|
||||
name: "python",
|
||||
version: "3.x"
|
||||
}
|
||||
},
|
||||
nbformat: 4,
|
||||
nbformat_minor: 5
|
||||
};
|
||||
|
||||
studentRouter.post("/deploy", (req, res) => {});
|
||||
// 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);
|
||||
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((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.post("/deploy", (req, res) => {
|
||||
|
||||
});
|
||||
|
||||
studentRouter.get("/assignment/:qrnum", (req, res) => {
|
||||
const qrnum = req.params.qrnum;
|
||||
|
|
@ -68,4 +124,18 @@ studentRouter.post("/verify", async (req, res) => {
|
|||
}
|
||||
});
|
||||
|
||||
// post restart from deployment service /appname/restart endpoint
|
||||
studentRouter.post("/restart", async (req, res) => {
|
||||
const { appName } = req.body;
|
||||
console.log("Received request to restart app:", appName);
|
||||
try {
|
||||
const response = await axios.post(`${DEPLOY_API_URL}/${appName}/restart`);
|
||||
console.log("Restart response:", response.data);
|
||||
res.status(response.status).json(response.data);
|
||||
} catch (error) {
|
||||
console.error("Error restarting app:", error.message);
|
||||
res.status(error.response?.status || 500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = studentRouter;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
require('dotenv').config();
|
||||
require("dotenv").config();
|
||||
const cors = require("cors");
|
||||
const passport = require("passport");
|
||||
const session = require("express-session");
|
||||
|
|
@ -13,7 +13,7 @@ const s3 = new AWS.S3({
|
|||
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
||||
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
||||
region: process.env.AWS_REGION,
|
||||
s3ForcePathStyle: true
|
||||
s3ForcePathStyle: true,
|
||||
});
|
||||
const BUCKET = process.env.COMMON_BUCKET;
|
||||
|
||||
|
|
@ -51,18 +51,25 @@ app.get("/", (req, res) => {
|
|||
res.send("OK");
|
||||
});
|
||||
|
||||
app.get("/notebook/save/:appname", async (req, res) => {
|
||||
});
|
||||
|
||||
app.get("/notebook/:appName", async (req, res) => {
|
||||
try {
|
||||
const { appName } = req.params;
|
||||
const prefix = `${appName}/notebooks/`;
|
||||
const list = await s3.listObjectsV2({ Bucket: BUCKET, Prefix: prefix }).promise();
|
||||
const list = await s3
|
||||
.listObjectsV2({ Bucket: BUCKET, Prefix: prefix })
|
||||
.promise();
|
||||
if (!list.Contents || list.Contents.length === 0) {
|
||||
return res.status(404).json({ error: "Notebook not found" });
|
||||
}
|
||||
const latest = list.Contents.reduce((prev, curr) =>
|
||||
prev.LastModified > curr.LastModified ? prev : curr
|
||||
);
|
||||
const data = await s3.getObject({ Bucket: BUCKET, Key: latest.Key }).promise();
|
||||
const data = await s3
|
||||
.getObject({ Bucket: BUCKET, Key: latest.Key })
|
||||
.promise();
|
||||
res.send(data.Body.toString("utf-8"));
|
||||
} catch (error) {
|
||||
console.error("Failed to load notebook:", error);
|
||||
|
|
@ -71,4 +78,6 @@ app.get("/notebook/:appName", async (req, res) => {
|
|||
});
|
||||
|
||||
const port = process.env.NODE_PORT || 8080;
|
||||
app.listen({ port: port, host: '::', ipv6Only: false }, () => console.log(`Listening on ${port}...`));
|
||||
app.listen({ port: port, host: "::", ipv6Only: false }, () =>
|
||||
console.log(`Listening on ${port}...`)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ RUN npm ci --only=production
|
|||
COPY src ./src
|
||||
COPY snakeapi_service ./snakeapi_service
|
||||
|
||||
ENV FLY_ACCESS_TOKEN="FlyV1 fm2_lJPECAAAAAAACJJHxBByW6wRXXxQ17OD8xlRRR5cwrVodHRwczovL2FwaS5mbHkuaW8vdjGUAJLOAA//nh8Lk7lodHRwczovL2FwaS5mbHkuaW8vYWFhL3YxxDwQBQg0Vif1OLMYOJOtNVokX+9SIVL2E8QoNub0JDBE4wNh97aUAPiiNvpAAMhM/eO7SWUVAx5rcDTBjf7ETtdnvXtcHaqOnK2HmNSV9K9UVy5Or3Sd+0+kxqDoWRXGE0y5pdb8+HNqwMcryszYvAv8HVcoKFgF4qd7GmzniNvZETOkrbsjMsU1+mVXTMQgh7H9z6IcGVjJozV92cDsSn91USqxOmBdwFQAkFGwPV0=,fm2_lJPETtdnvXtcHaqOnK2HmNSV9K9UVy5Or3Sd+0+kxqDoWRXGE0y5pdb8+HNqwMcryszYvAv8HVcoKFgF4qd7GmzniNvZETOkrbsjMsU1+mVXTMQQqnJP464DwxC6D4e3p9THZMO5aHR0cHM6Ly9hcGkuZmx5LmlvL2FhYS92MZgEks5oESI9zwAAAAEkCUBbF84AD2FZCpHOAA9hWQzEEL6gO8olFxMOq1uFxP1yJavEIBDKb7RuqVr/sFQniKl0S2HMM6+AQJH3940ly0mufbYx"
|
||||
ENV FLY_ACCESS_TOKEN="FlyV1 fm2_lJPECAAAAAAACJJHxBAjRF69RAjf3FXXuVT+M3bcwrVodHRwczovL2FwaS5mbHkuaW8vdjGUAJLOAA//nh8Lk7lodHRwczovL2FwaS5mbHkuaW8vYWFhL3YxxDxmIdNTu/DGjUSyYxuC5W7Rio4bNT5w6c1Ihi+ZJnjcmEutbt5KuyFcCo1C0CFPEhrP4hY5SEvXN58GHUDEToWZ0GwI5ndmIsZnhWSG8TBixbuFTaBb8lTBU5lNOvm2l4rX1i6dfId7S9Ko6qXpOzl9oYngy0zw+g2MwXuQrH6/XELBdEy/KThVeTEjt8QgBzOo/Eae+DsrATm6WjVv9f5a4iS/s7WtYHydZZr3z9M=,fm2_lJPEToWZ0GwI5ndmIsZnhWSG8TBixbuFTaBb8lTBU5lNOvm2l4rX1i6dfId7S9Ko6qXpOzl9oYngy0zw+g2MwXuQrH6/XELBdEy/KThVeTEjt8QQNZaUoOrVdOnk6Vo/DkeMGsO5aHR0cHM6Ly9hcGkuZmx5LmlvL2FhYS92MZgEks5oGwzFzwAAAAEkEyrjF84AD2FZCpHOAA9hWQzEEASQrBHkPDFO3LlZDaxRRIjEIEW1ki/syKHnhFamHgze8PFeunPOAmNmh57hslV04lL7"
|
||||
|
||||
EXPOSE 3006
|
||||
CMD ["node", "src/index.js"]
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -220,6 +220,28 @@ app.post("/:appName/upload", async (req, res) => {
|
|||
}
|
||||
});
|
||||
|
||||
// restart a Fly app
|
||||
app.post("/:appName/restart", async (req, res) => {
|
||||
const { appName } = req.params;
|
||||
try {
|
||||
const fly = createFlyClient();
|
||||
const { data: machines } = await fly.get(`/apps/${appName}/machines`);
|
||||
if (!machines || !Array.isArray(machines) || machines.length === 0) {
|
||||
return res.status(404).json({ error: "No machines found for this app" });
|
||||
}
|
||||
const results = await Promise.all(
|
||||
machines.map(machine =>
|
||||
fly.post(`/apps/${appName}/machines/${machine.id}/restart`)
|
||||
)
|
||||
);
|
||||
res.json({ status: "restarted", app: appName, count: results.length });
|
||||
} catch (err) {
|
||||
console.error("Restart error:", err.response?.data || err.message);
|
||||
res.status(500).json({ error: err.response?.data || err.message });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Delete a Fly app
|
||||
app.post("/:appName/delete", async (req, res) => {
|
||||
const { appName } = req.params;
|
||||
|
|
|
|||
22
gameboard-service/.dockerignore
Normal file
22
gameboard-service/.dockerignore
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# General
|
||||
.DS_Store
|
||||
.vscode
|
||||
|
||||
# Node
|
||||
node_modules
|
||||
|
||||
# SvelteKit
|
||||
.output
|
||||
.svelte-kit
|
||||
/build
|
||||
/package
|
||||
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
|
||||
# Netlify
|
||||
.netlify
|
||||
18
gameboard-service/.github/workflows/fly-deploy.yml
vendored
Normal file
18
gameboard-service/.github/workflows/fly-deploy.yml
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# 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 }}
|
||||
47
gameboard-service/Dockerfile
Normal file
47
gameboard-service/Dockerfile
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# syntax = docker/dockerfile:1
|
||||
|
||||
# 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"
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
# Final stage for app image
|
||||
FROM base
|
||||
|
||||
# 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
|
||||
|
||||
# Start the server by default, this can be overwritten at runtime
|
||||
EXPOSE 3005
|
||||
CMD [ "node", "./build/index.js" ]
|
||||
20
gameboard-service/fly.toml
Normal file
20
gameboard-service/fly.toml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
app = "gameboard-service-aged-glitter-8141"
|
||||
primary_region = "sea"
|
||||
|
||||
[env]
|
||||
PORT = "3005"
|
||||
|
||||
[build]
|
||||
|
||||
[http_service]
|
||||
internal_port = 3005
|
||||
force_https = true
|
||||
auto_stop_machines = "stop"
|
||||
auto_start_machines = true
|
||||
min_machines_running = 0
|
||||
processes = ["app"]
|
||||
|
||||
[[vm]]
|
||||
memory = "1gb"
|
||||
cpu_kind = "shared"
|
||||
cpus = 1
|
||||
991
gameboard-service/package-lock.json
generated
991
gameboard-service/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -15,11 +15,13 @@
|
|||
"test:unit": "vitest run"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@flydotio/dockerfile": "^0.7.10",
|
||||
"@iconify-json/heroicons": "^1.1.11",
|
||||
"@iconify-json/heroicons-solid": "^1.1.7",
|
||||
"@neoconfetti/svelte": "^1.0.0",
|
||||
"@playwright/test": "^1.28.1",
|
||||
"@sveltejs/adapter-auto": "^2.0.0",
|
||||
"@sveltejs/adapter-node": "^1.3.1",
|
||||
"@sveltejs/adapter-static": "^2.0.3",
|
||||
"@sveltejs/kit": "^1.30.3",
|
||||
"@tailwindcss/forms": "^0.5.4",
|
||||
|
|
|
|||
12
gameboard-service/src/hooks.server.ts
Normal file
12
gameboard-service/src/hooks.server.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import type { Handle } from '@sveltejs/kit';
|
||||
|
||||
export const handle: Handle = async ({ event, resolve }) => {
|
||||
const response = await resolve(event);
|
||||
|
||||
response.headers.delete('X-Frame-Options');
|
||||
response.headers.set('X-Frame-Options', 'ALLOWALL');
|
||||
|
||||
response.headers.set('Content-Security-Policy', "frame-ancestors *");
|
||||
|
||||
return response;
|
||||
};
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
// 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-auto';
|
||||
import adapter from "@sveltejs/adapter-static";
|
||||
// import adapter from '@sveltejs/adapter-node';
|
||||
import adapter from '@sveltejs/adapter-node';
|
||||
// import adapter from "@sveltejs/adapter-static";
|
||||
|
||||
import { vitePreprocess } from "@sveltejs/kit/vite";
|
||||
import { vitePreprocess } from '@sveltejs/kit/vite';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
|
|
@ -13,12 +14,8 @@ const config = {
|
|||
preprocess: vitePreprocess(),
|
||||
kit: {
|
||||
adapter: adapter({
|
||||
// These are the defaults, see https://kit.svelte.dev/docs/adapter-static
|
||||
pages: "build",
|
||||
assets: "build",
|
||||
fallback: undefined,
|
||||
precompress: false,
|
||||
strict: true
|
||||
// This option specifies the output directory for the build
|
||||
out: 'build'
|
||||
})
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ mkdir -p "${NOTEBOOK_DIR}"
|
|||
# fetch latest notebook
|
||||
echo "Syncing notebooks from S3 bucket..."
|
||||
aws --endpoint-url "$AWS_ENDPOINT_URL_S3" --region "$AWS_REGION" \
|
||||
s3 sync "s3://$BUCKET_NAME/$INSTANCE_PREFIX/notebooks/" "${NOTEBOOK_DIR}/"
|
||||
s3 sync "s3://$COMMON_BUCKET/$INSTANCE_PREFIX/notebooks/" "${NOTEBOOK_DIR}/"
|
||||
|
||||
# convert to Python script for dynamic import
|
||||
echo "Finding the latest notebook..."
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue