commit
8c5fe4266f
16 changed files with 3646 additions and 1 deletions
20
assignment-db-service/Dockerfile
Normal file
20
assignment-db-service/Dockerfile
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
# Use the official Node.js image as the base image
|
||||||
|
ARG NODE_VERSION=22.13.1
|
||||||
|
FROM node:${NODE_VERSION}-slim AS base
|
||||||
|
|
||||||
|
# Set the working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
RUN apt-get update -y && apt-get install -y openssl && npm install && npx prisma generate
|
||||||
|
|
||||||
|
# Copy the application source code
|
||||||
|
COPY --link . .
|
||||||
|
|
||||||
|
# Expose the application port
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
# Define the command to run the application
|
||||||
|
CMD ["node", "app.js"]
|
||||||
130
assignment-db-service/app.js
Normal file
130
assignment-db-service/app.js
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
const express = require('express');
|
||||||
|
const { PrismaClient } = require('@prisma/client');
|
||||||
|
const bcrypt = require('bcrypt');
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
const port = process.env.NODE_PORT || 3000;
|
||||||
|
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(express.urlencoded({ extended: true }));
|
||||||
|
|
||||||
|
// Create Assignment
|
||||||
|
app.post('/assignments', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
CampID,
|
||||||
|
ProgramID,
|
||||||
|
StudentName,
|
||||||
|
SnakeGameId,
|
||||||
|
OriginalFile,
|
||||||
|
EditableFile,
|
||||||
|
AssignmentUrl,
|
||||||
|
Password,
|
||||||
|
InstructorID,
|
||||||
|
} = req.body;
|
||||||
|
|
||||||
|
const hashedPassword = await bcrypt.hash(Password, 10);
|
||||||
|
|
||||||
|
const newAssignment = await prisma.assignment.create({
|
||||||
|
data: {
|
||||||
|
CampID,
|
||||||
|
ProgramID,
|
||||||
|
StudentName,
|
||||||
|
SnakeGameId,
|
||||||
|
OriginalFile,
|
||||||
|
EditableFile,
|
||||||
|
AssignmentUrl,
|
||||||
|
PasswordHash: hashedPassword,
|
||||||
|
InstructorID,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json({ message: 'Assignment created successfully', assignment: newAssignment });
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error creating assignment:', err.message);
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get Assignments by InstructorID
|
||||||
|
app.get('/assignments/instructor/:instructorId', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { instructorId } = req.params;
|
||||||
|
|
||||||
|
const assignments = await prisma.assignment.findMany({
|
||||||
|
where: { InstructorID: parseInt(instructorId) },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (assignments.length === 0) {
|
||||||
|
return res.status(404).json({ message: 'No assignments found for this instructor' });
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json(assignments);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching assignments:', err.message);
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Read Assignment
|
||||||
|
app.get('/assignments/:id', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const assignment = await prisma.assignment.findUnique({
|
||||||
|
where: { AssignmentID: parseInt(req.params.id) },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!assignment) {
|
||||||
|
return res.status(404).json({ message: 'Assignment not found' });
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json(assignment);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching assignment:', err.message);
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update Assignment
|
||||||
|
app.put('/assignments/:id', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { id } = req.params;
|
||||||
|
const data = req.body;
|
||||||
|
|
||||||
|
if (data.Password) {
|
||||||
|
data.PasswordHash = await bcrypt.hash(data.Password, 10);
|
||||||
|
delete data.Password;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedAssignment = await prisma.assignment.update({
|
||||||
|
where: { AssignmentID: parseInt(id) },
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json({ message: 'Assignment updated successfully', assignment: updatedAssignment });
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error updating assignment:', err.message);
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Delete Assignment
|
||||||
|
app.delete('/assignments/:id', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { id } = req.params;
|
||||||
|
|
||||||
|
await prisma.assignment.delete({
|
||||||
|
where: { AssignmentID: parseInt(id) },
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json({ message: 'Assignment deleted successfully' });
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error deleting assignment:', err.message);
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(port, () => {
|
||||||
|
console.log(`Server running at http://localhost:${port}`);
|
||||||
|
});
|
||||||
32
assignment-db-service/fly.toml
Normal file
32
assignment-db-service/fly.toml
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
# fly.toml app configuration file generated for db-assignment-service on 2025-04-24T11:13:50-07:00
|
||||||
|
#
|
||||||
|
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
|
||||||
|
#
|
||||||
|
|
||||||
|
app = 'db-assignment-service'
|
||||||
|
primary_region = 'sea'
|
||||||
|
|
||||||
|
[build]
|
||||||
|
|
||||||
|
[http_service]
|
||||||
|
internal_port = 3000
|
||||||
|
force_https = true
|
||||||
|
auto_stop_machines = 'stop'
|
||||||
|
auto_start_machines = true
|
||||||
|
min_machines_running = 0
|
||||||
|
processes = ['app']
|
||||||
|
|
||||||
|
[[services]]
|
||||||
|
protocol = 'tcp'
|
||||||
|
internal_port = 3000
|
||||||
|
ports = []
|
||||||
|
|
||||||
|
[services.concurrency]
|
||||||
|
type = 'requests'
|
||||||
|
hard_limit = 1000
|
||||||
|
soft_limit = 500
|
||||||
|
|
||||||
|
[[vm]]
|
||||||
|
memory = '1gb'
|
||||||
|
cpu_kind = 'shared'
|
||||||
|
cpus = 1
|
||||||
1738
assignment-db-service/package-lock.json
generated
Normal file
1738
assignment-db-service/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
13
assignment-db-service/package.json
Normal file
13
assignment-db-service/package.json
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/client": "^6.1.0",
|
||||||
|
"bcrypt": "^5.1.1",
|
||||||
|
"express": "^5.1.0",
|
||||||
|
"nodemon": "^3.1.9",
|
||||||
|
"prisma": "^6.1.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"dev": "nodemon app.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
22
assignment-db-service/prisma/schema.prisma
Normal file
22
assignment-db-service/prisma/schema.prisma
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
binaryTargets = ["native", "windows"]
|
||||||
|
}
|
||||||
|
|
||||||
|
datasource db {
|
||||||
|
provider = "postgresql"
|
||||||
|
url = env("DATABASE_URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Assignment {
|
||||||
|
AssignmentID Int @id @default(autoincrement())
|
||||||
|
CampID Int?
|
||||||
|
ProgramID Int?
|
||||||
|
StudentName String
|
||||||
|
SnakeGameId String?
|
||||||
|
OriginalFile String?
|
||||||
|
EditableFile String?
|
||||||
|
AssignmentUrl String?
|
||||||
|
PasswordHash String // store bcrypt hash
|
||||||
|
InstructorID Int?
|
||||||
|
}
|
||||||
4
assignment-db-service/readme.md
Normal file
4
assignment-db-service/readme.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
# database microservice
|
||||||
|
- store database tables
|
||||||
|
- issue and re-issue auth tokens (jwt)
|
||||||
|
- handle password reset into db
|
||||||
40
assignment-service/.dockerignore
Normal file
40
assignment-service/.dockerignore
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Node.js specific
|
||||||
|
node_modules/
|
||||||
|
npm-debug.log
|
||||||
|
yarn-debug.log
|
||||||
|
|
||||||
|
# Environment and secrets
|
||||||
|
.env*
|
||||||
|
*.env
|
||||||
|
*.pem
|
||||||
|
*.key
|
||||||
|
*.crt
|
||||||
|
|
||||||
|
# Development artifacts
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
out/
|
||||||
|
|
||||||
|
# Version control
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
tmp/
|
||||||
|
temp/
|
||||||
|
*.tmp
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
*.md
|
||||||
|
README*
|
||||||
|
|
||||||
|
# Docker files
|
||||||
|
Dockerfile*
|
||||||
|
docker-compose*
|
||||||
|
|
||||||
|
# Project-specific
|
||||||
|
package-lock.json
|
||||||
23
assignment-service/Dockerfile
Normal file
23
assignment-service/Dockerfile
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
# Use the official Node.js image as the base image
|
||||||
|
ARG NODE_VERSION=22.13.1
|
||||||
|
FROM node:${NODE_VERSION}-slim AS base
|
||||||
|
|
||||||
|
# Set the working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
# Install dependencies using npm ci for deterministic builds
|
||||||
|
# RUN --mount=type=cache,target=/root/.npm npm ci --production
|
||||||
|
|
||||||
|
# Copy the application source code
|
||||||
|
COPY --link . .
|
||||||
|
|
||||||
|
# Expose the application port
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# Define the command to run the application
|
||||||
|
CMD ["node", "server.js"]
|
||||||
33
assignment-service/README.md
Normal file
33
assignment-service/README.md
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
# Running the Project with Docker
|
||||||
|
|
||||||
|
This section provides instructions to build and run the project using Docker.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Ensure Docker and Docker Compose are installed on your system.
|
||||||
|
- The project requires Node.js version `22.13.1` as specified in the Dockerfile.
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
- If applicable, create a `.env` file in the project root directory to define environment variables. Uncomment the `env_file` line in the `docker-compose.yml` file to enable this.
|
||||||
|
|
||||||
|
## Build and Run Instructions
|
||||||
|
|
||||||
|
1. Build the Docker image and start the services:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose up --build
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Access the application at `http://localhost:8080`.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
- The application runs with a non-root user for enhanced security.
|
||||||
|
- The `NODE_ENV` is set to `production` and `NODE_OPTIONS` is configured for optimized memory usage.
|
||||||
|
|
||||||
|
## Exposed Ports
|
||||||
|
|
||||||
|
- The application service exposes port `8080` to the host system.
|
||||||
|
|
||||||
|
For further details, refer to the provided `Dockerfile` and `docker-compose.yml` files.
|
||||||
42
assignment-service/fly.toml
Normal file
42
assignment-service/fly.toml
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
# fly.toml app configuration file generated for assignment-service on 2025-04-24T11:48:54-07:00
|
||||||
|
#
|
||||||
|
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
|
||||||
|
#
|
||||||
|
|
||||||
|
app = 'assignment-service'
|
||||||
|
primary_region = 'sea'
|
||||||
|
|
||||||
|
[build]
|
||||||
|
|
||||||
|
[env]
|
||||||
|
PORT = '8080'
|
||||||
|
|
||||||
|
[http_service]
|
||||||
|
internal_port = 8080
|
||||||
|
force_https = true
|
||||||
|
auto_stop_machines = 'stop'
|
||||||
|
auto_start_machines = true
|
||||||
|
min_machines_running = 0
|
||||||
|
processes = ['app']
|
||||||
|
|
||||||
|
[[services]]
|
||||||
|
protocol = 'tcp'
|
||||||
|
internal_port = 8080
|
||||||
|
|
||||||
|
[[services.ports]]
|
||||||
|
port = 80
|
||||||
|
handlers = ['http']
|
||||||
|
|
||||||
|
[[services.ports]]
|
||||||
|
port = 443
|
||||||
|
handlers = ['tls', 'http']
|
||||||
|
|
||||||
|
[[services.tcp_checks]]
|
||||||
|
interval = '10s'
|
||||||
|
timeout = '2s'
|
||||||
|
grace_period = '5s'
|
||||||
|
|
||||||
|
[[vm]]
|
||||||
|
memory = '1gb'
|
||||||
|
cpu_kind = 'shared'
|
||||||
|
cpus = 1
|
||||||
1501
assignment-service/package-lock.json
generated
Normal file
1501
assignment-service/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
21
assignment-service/package.json
Normal file
21
assignment-service/package.json
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "auth-service",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "server.js",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "nodemon server.js"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"description": "",
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.8.4",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"dotenv": "^16.5.0",
|
||||||
|
"express": "^5.1.0",
|
||||||
|
"express-session": "^1.18.1",
|
||||||
|
"nodemon": "^3.1.9",
|
||||||
|
"passport": "^0.7.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,7 @@ const axios = require("axios");
|
||||||
|
|
||||||
const { DB_ASSIGNMENT_SERVICE_URL } = process.env.DB_ASSIGNMENT_SERVICE_URL || "http://localhost:3000";
|
const { DB_ASSIGNMENT_SERVICE_URL } = process.env.DB_ASSIGNMENT_SERVICE_URL || "http://localhost:3000";
|
||||||
|
|
||||||
//
|
// This endpoint is for instructors to create a new assignment
|
||||||
intructorRouter.post("/create", passport.authenticate("jwt", { session: false }), async (req, res) => {
|
intructorRouter.post("/create", passport.authenticate("jwt", { session: false }), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(`${DB_ASSIGNMENT_SERVICE_URL}/assignments`, req.body);
|
const response = await axios.post(`${DB_ASSIGNMENT_SERVICE_URL}/assignments`, req.body);
|
||||||
|
|
|
||||||
13
assignment-service/routes/StudentRouter.js
Normal file
13
assignment-service/routes/StudentRouter.js
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
const studentRouter = require("express").Router();
|
||||||
|
const passport = require("passport");
|
||||||
|
const axios = require("axios");
|
||||||
|
|
||||||
|
studentRouter.post("/save", (req, res) => {
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
studentRouter.post("/deploy", (req, res) => {
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = studentRouter;
|
||||||
13
assignment-service/server.js
Normal file
13
assignment-service/server.js
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
require('dotenv').config();
|
||||||
|
|
||||||
|
const express = require("express");
|
||||||
|
const instructorRouter = require("./routes/InstructorRouter");
|
||||||
|
const studentRouter = require("./routes/StudentRouter");
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
app.use("/instructor", instructorRouter);
|
||||||
|
app.use("/student", studentRouter);
|
||||||
|
|
||||||
|
const port = process.env.PORT || 8080;
|
||||||
|
app.listen(port, () => console.log(`Listening on port ${port}...`));
|
||||||
Loading…
Add table
Add a link
Reference in a new issue