Compare commits

..

2 Commits

Author SHA1 Message Date
6c57e960aa web: frontend: Switch to express 2025-11-11 15:19:34 -06:00
4c34931b34 web: Prepare containers 2025-11-11 14:46:16 -06:00
9 changed files with 95 additions and 9 deletions

View File

@@ -1 +1,2 @@
TODO.md
jobs jobs

16
web/backend/Dockerfile Normal file
View File

@@ -0,0 +1,16 @@
# Use official Julia image as base
FROM julia:1.11
WORKDIR /app
# Install project & dependencies
COPY Project.toml /app/Backend/
COPY src /app/Backend/src
RUN julia --project=. -e 'using Pkg; Pkg.develop(path="Backend"); Pkg.add("HiGHS"); Pkg.precompile()'
COPY docker/startup.jl ./
# Set default environment variables
ENV UCJL_HOST="0.0.0.0"
ENV UCJL_PORT="9000"
# Run the server
CMD ["julia", "--project=.", "startup.jl"]

5
web/backend/Makefile Normal file
View File

@@ -0,0 +1,5 @@
docker-build:
docker build . -t ucjl-backend
docker-run:
docker run --publish 9000:9000 --rm -it ucjl-backend

View File

@@ -0,0 +1,30 @@
#!/usr/bin/env julia
# UnitCommitment.jl: Optimization Package for Security-Constrained Unit Commitment
# Copyright (C) 2025, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.
# Load required packages
using HiGHS
using Backend
const UCJL_HOST = get(ENV, "HOST", "0.0.0.0")
const UCJL_PORT = parse(Int, get(ENV, "PORT", "9000"))
println("Starting UnitCommitment Backend Server...")
println("Host: $UCJL_HOST")
println("Port: $UCJL_PORT")
println("Press Ctrl+C to stop the server")
server = Backend.start_server(UCJL_HOST, UCJL_PORT; optimizer = HiGHS.Optimizer)
try
wait()
catch e
if e isa InterruptException
println("\nShutting down server...")
Backend.stop(server)
println("Server stopped")
else
rethrow(e)
end
end

View File

@@ -54,9 +54,9 @@ function submit(req, processor::JobProcessor)
end end
function jobs_view(req) function jobs_view(req)
# Extract job_id from URL path /jobs/{job_id}/view # Extract job_id from URL path /api/jobs/{job_id}/view
path_parts = split(req.target, '/') path_parts = split(req.target, '/')
job_id = path_parts[3] # /jobs/{job_id}/view -> index 3 job_id = path_parts[4]
# Construct job directory path # Construct job directory path
job_dir = joinpath(basedir, "jobs", job_id) job_dir = joinpath(basedir, "jobs", job_id)
@@ -132,10 +132,10 @@ function start_server(host, port; optimizer)
) )
# Register /submit endpoint # Register /submit endpoint
HTTP.register!(router, "POST", "/submit", req -> submit(req, processor)) HTTP.register!(router, "POST", "/api/submit", req -> submit(req, processor))
# Register job/*/view endpoint # Register job/*/view endpoint
HTTP.register!(router, "GET", "/jobs/*/view", jobs_view) HTTP.register!(router, "GET", "/api/jobs/*/view", jobs_view)
server = HTTP.serve!(router, host, port; verbose = false) server = HTTP.serve!(router, host, port; verbose = false)
return ServerHandle(server, processor) return ServerHandle(server, processor)

View File

@@ -1,2 +1,2 @@
FAST_REFRESH=false FAST_REFRESH=false
REACT_APP_BACKEND_URL=http://localhost:9000 REACT_APP_BACKEND_URL=http://localhost:9000/api

View File

@@ -7,7 +7,10 @@ COPY . .
RUN npm run build RUN npm run build
# Production Stage # Production Stage
FROM nginx:stable-alpine AS production FROM node:18-alpine AS production
COPY --from=build /app/build /usr/share/nginx/html WORKDIR /app
EXPOSE 80 COPY --from=build /app/build ./build
CMD ["nginx", "-g", "daemon off;"] COPY server.js ./
RUN npm install --production express
EXPOSE 3000
CMD ["node", "server.js"]

5
web/frontend/Makefile Normal file
View File

@@ -0,0 +1,5 @@
docker-build:
docker build . -t ucjl-frontend
docker-run:
docker run -e REACT_APP_BACKEND_URL=http://localhost:9000/api --publish 3000:3000 --rm -it ucjl-frontend

26
web/frontend/server.js Normal file
View File

@@ -0,0 +1,26 @@
const express = require('express');
const path = require('path');
const app = express();
const PORT = process.env.PORT || 3000;
// Serve static files from the build directory
app.use(express.static(path.join(__dirname, 'build')));
// Handle client-side routing - serve index.html for all routes
app.get('/*splat', (req, res) => {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
const server = app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
// Graceful shutdown on CTRL+C
process.on('SIGINT', () => {
console.log('\nShutting down gracefully...');
server.close(() => {
console.log('Server closed');
process.exit(0);
});
});