No description
| prisma | ||
| src | ||
| .env.example | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| README.md | ||
| sync_schema.sh | ||
| sync_schema_helper.sh | ||
Backend Rewrite - Rust Migration
This is the Rust rewrite of the YACPS backend, migrated from NestJS.
Key Changes
1. Bitwise Permission System
- Migrated from array-based permissions to bitwise flags (like Discord)
- Uses
bitflagscrate for efficient permission checks - Implements Allow/Inherit/Deny logic using dual bitfields (allow/deny)
- Permission resolution follows hierarchy: Problem -> Category -> Global
2. Session-Based Authentication
- Changed from JWT to session-based auth using database-backed sessions
- Sessions stored in PostgreSQL with expiration
- Session ID stored in HTTP-only cookies
3. Technology Stack
- Framework: Axum (async, high-performance)
- ORM: SeaORM (with Prisma for migrations)
- Database: PostgreSQL
- Auth: Session-based with cookie storage
- Permissions: Bitwise flags with hierarchical resolution
Project Structure
src/
├── client/ # Client-facing API modules
│ ├── auth.rs
│ ├── users.rs
│ ├── problems.rs
│ ├── submissions.rs
│ ├── contests.rs
│ ├── categories.rs
│ ├── announcements.rs
│ ├── roles.rs
│ ├── sessions.rs
│ ├── types.rs
│ └── judge.rs
├── judge/ # Judge system modules
│ ├── submission_queue.rs
│ ├── judge_manager.rs
│ └── dmoj_bridge.rs
├── entities/ # SeaORM entities (auto-generated)
├── services/ # Business logic services
│ ├── permissions.rs
│ └── sessions.rs
├── middleware/ # Axum middleware
│ └── auth.rs
├── config.rs # Configuration management
├── error.rs # Error types and handling
├── permissions.rs # Permission definitions and logic
└── main.rs # Application entry point
Setup
- Copy
.env.exampleto.envand configure:
cp .env.example .env
- Install dependencies:
cargo build
- Run Prisma migrations:
cd prisma
npx prisma migrate deploy
- Generate SeaORM entities:
./sync_schema.sh
- Run the server:
cargo run
Development
Running in development mode:
cargo watch -x run
Running tests:
cargo test
Permission System
The permission system uses bitwise operations for efficiency:
// Check single permission
let can_edit = permission_service.resolve_permission(
Some(user_id),
Permission::PROBLEM_EDIT,
AidResourceType::Problem,
problem_id
).await?;
// Get all permissions for context
let perms = permission_service.get_user_context_permissions(
Some(user_id),
AidResourceType::Problem,
problem_id
).await?;
// Check if user can manage
let can_manage = permission_service.can_manage_problem(
Some(user_id),
problem_id,
Some(Permission::PROBLEM_EDIT)
).await?;
Session Management
// Create session
let session = session_service.create_session(
user_id,
Some(ip_address),
Some(user_agent)
).await?;
// Validate session
let (session, user) = session_service.get_session_with_user(&session_id).await?;
// Delete session (logout)
session_service.delete_session(&session_id).await?;
API Routes
All routes are prefixed with /client:
/client/auth- Authentication endpoints/client/users- User management/client/problems- Problem management/client/submissions- Submission handling/client/contests- Contest management/client/categories- Category management/client/announcements- Announcements/client/roles- Role management/client/sessions- Session management/client/types- Type management/client/judge- Judge system
Migration Notes
From NestJS to Rust:
- Controllers → Axum Handlers: NestJS controllers are now Axum handler functions
- Services → Services: Service layer structure remains similar
- Guards → Middleware: Auth guards are now Axum extractors
- DTOs → Structs: Request/response DTOs are now Rust structs with serde
- Decorators → Extractors: Parameter decorators are now Axum extractors
Permission Array → Bitwise:
Before (NestJS):
permissions: ['problem.edit', 'problem.delete']
After (Rust):
let perms = Permission::PROBLEM_EDIT | Permission::PROBLEM_DELETE;
// Stored as: 0x...0011000...
JWT → Sessions:
Before (NestJS):
const token = jwt.sign({ userId }, secret);
res.cookie('token', token);
After (Rust):
let session = session_service.create_session(user_id, ip, user_agent).await?;
// Session stored in DB, ID returned as cookie