Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Database
POSTGRES_DB=codesce
POSTGRES_USER=codesce
POSTGRES_PASSWORD=changeme
DB_PORT=5432

# Backend
BACKEND_PORT=6767
GIN_MODE=debug
DATABASE_URL=postgres://codesce:changeme@database:5432/codesce?sslmode=disable

# Frontend
FRONTEND_PORT=3000
VITE_API_URL=http://localhost:6767
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
.cursor/
.env
26 changes: 26 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Backend Dockerfile (Go/Gin)

# --- Build stage ---
FROM golang:1.25-alpine AS build

WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY . .

RUN go build -o /bin/server ./cmd/server

# --- Production stage ---
FROM alpine:3.20 AS production

RUN apk add --no-cache ca-certificates

WORKDIR /app

COPY --from=build /bin/server /bin/server

EXPOSE 6767

CMD ["/bin/server"]
15 changes: 15 additions & 0 deletions Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Backend Dev Dockerfile (Go/Gin)

FROM golang:1.25-alpine

WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY . .

EXPOSE 6767

CMD ["go", "run", "./cmd/server"]
# Note: app should listen on port 6767
23 changes: 23 additions & 0 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"github.com/gin-gonic/gin"
"CodeSCE/internal/handlers"
)

func main() {
Expand All @@ -11,5 +12,27 @@ func main() {
"message": "pong",
})
})

api := r.Group("/api")
api.POST("/assessments", handlers.CreateAssessment)
api.GET("/assessments", handlers.ListAssessments)
api.GET("/assessments/:id", handlers.GetAssessment)
api.POST("/assessments/:id/questions", handlers.AddQuestion)
api.POST("/assessments/:id/questions/:qid/test-cases", handlers.AddTestCases)

api.POST("/assessments/:id/invites", handlers.CreateInvite)
api.GET("/invites", handlers.ListInvites)
api.GET("/invites/:token", handlers.ValidateInvite)
api.POST("/invites/:token/start", handlers.StartAttempt)

api.GET("/attempts", handlers.ListAttempts)
api.GET("/attempts/:id", handlers.GetAttempt)
api.GET("/attempts/:id/questions", handlers.GetAttemptQuestions)
api.POST("/attempts/:id/answers", handlers.SaveAnswers)
api.POST("/attempts/:id/submit", handlers.SubmitAttempt)

api.POST("/attempts/:id/questions/:qid/submissions", handlers.CreateSubmission)
api.GET("/submissions/:id", handlers.GetSubmission)
api.POST("/attempts/:id/questions/:qid/run", handlers.RunCode)
r.Run(":6767")
}
38 changes: 38 additions & 0 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
services:
database:
ports:
- "${DB_PORT:-5432}:5432"

backend:
build:
context: .
dockerfile: Dockerfile.dev
environment:
GIN_MODE: debug
PORT: ${BACKEND_PORT:-6767}
DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@database:5432/${POSTGRES_DB}?sslmode=disable
volumes:
- .:/app
- go-mod-cache:/go/pkg/mod
ports:
- "${BACKEND_PORT:-6767}:6767"
depends_on:
database:
condition: service_healthy

frontend:
build:
context: ./frontend
dockerfile: Dockerfile.dev
environment:
VITE_API_URL: http://localhost:${BACKEND_PORT:-6767}
volumes:
- ./frontend:/app
- /app/node_modules
ports:
- "${FRONTEND_PORT:-3000}:5173"
depends_on:
- backend

volumes:
go-mod-cache:
52 changes: 52 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
services:
database:
image: postgres:16-alpine
container_name: codesce-db
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
ports:
- "${DB_PORT:-5432}:5432"
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 5

backend:
build:
context: .
dockerfile: Dockerfile
container_name: codesce-backend
environment:
DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@database:5432/${POSTGRES_DB}?sslmode=disable
GIN_MODE: ${GIN_MODE:-release}
PORT: ${BACKEND_PORT:-6767}
ports:
- "${BACKEND_PORT:-6767}:6767"
depends_on:
database:
condition: service_healthy

frontend:
build:
context: ./frontend
dockerfile: Dockerfile
container_name: codesce-frontend
environment:
VITE_API_URL: ${VITE_API_URL:-http://backend:6767}
ports:
- "${FRONTEND_PORT:-3000}:80"
depends_on:
- backend

volumes:
pgdata:

networks:
default:
external: true
name: sce
23 changes: 23 additions & 0 deletions frontend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Frontend Dockerfile (React/Vite)

# --- Build stage ---
FROM node:20-alpine AS build

WORKDIR /app

COPY package.json package-lock.json ./

RUN npm ci

COPY . .

RUN npm run build

# --- Production stage ---
FROM nginx:alpine AS production

COPY --from=build /app/dist /usr/share/nginx/html

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]
15 changes: 15 additions & 0 deletions frontend/Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Frontend Dev Dockerfile (React/Vite)

FROM node:20-alpine

WORKDIR /app

COPY package.json package-lock.json ./

RUN npm ci

COPY . .

EXPOSE 5173

CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]
36 changes: 36 additions & 0 deletions internal/handlers/assessments.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package handlers

import (
"net/http"
"github.com/gin-gonic/gin"
)

func CreateAssessment(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "create assessment route wired"})
}

func ListAssessments(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "list assessments route wired"})
}

func GetAssessment(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "get assessment route wired",
"id": c.Param("id"),
})
}

func AddQuestion(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "add question route wired",
"id": c.Param("id"),
})
}

func AddTestCases(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "add test cases route wired",
"id": c.Param("id"),
"qid": c.Param("qid"),
})
}
49 changes: 49 additions & 0 deletions internal/handlers/attempts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package handlers

import (
"net/http"
"github.com/gin-gonic/gin"
)

func ListAttempts(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "list attempts route wired",
"assessmentId": c.Query("assessmentId"),
})
}

func GetAttempt(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "get attempt route wired",
"id": c.Param("id"),
})
}

func GetAttemptQuestions(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "get attempt questions route wired",
"id": c.Param("id"),
})
}

func SaveAnswers(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "save answers route wired",
"id": c.Param("id"),
})
}

func SubmitAttempt(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "submit attempt route wired",
"id": c.Param("id"),
})
}

func RunCode(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"attempt_id": c.Param("id"),
"question_id": c.Param("qid"),
"passed_test_cases": []int{},
})
}
31 changes: 31 additions & 0 deletions internal/handlers/invites.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package handlers

import (
"net/http"
"github.com/gin-gonic/gin"
)

func CreateInvite(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "create invite route wired",
"id": c.Param("id"),
})
}

func ListInvites(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "list invites route wired"})
}

func ValidateInvite(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "validate invite route wired",
"token": c.Param("token"),
})
}

func StartAttempt(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "start attempt route wired",
"token": c.Param("token"),
})
}
23 changes: 23 additions & 0 deletions internal/handlers/submissions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package handlers

import (
"net/http"
"github.com/gin-gonic/gin"
)

func CreateSubmission(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "create submission route wired",
"attempt_id": c.Param("id"),
"question_id": c.Param("qid"),
"submission_id": "mock-submission-id",
})
}

func GetSubmission(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "get submission route wired",
"id": c.Param("id"),
"status": "queued",
})
}