Merge pull request #4 from JBB0807/assignment

Assignment
This commit is contained in:
JB Balahadia 2025-04-25 14:05:57 -07:00 committed by GitHub
commit 8c5fe4266f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 3646 additions and 1 deletions

View 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"]

View 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}`);
});

View 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

File diff suppressed because it is too large Load diff

View 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"
}
}

View 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?
}

View file

@ -0,0 +1,4 @@
# database microservice
- store database tables
- issue and re-issue auth tokens (jwt)
- handle password reset into db

View 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

View 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"]

View 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.

View 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

File diff suppressed because it is too large Load diff

View 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"
}
}

View file

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

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

View 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}...`));