Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
3f42ccd
get campaigns by org and campaign slugs
KavikaPalletenne Aug 13, 2025
098f96e
Update .gitignore
KavikaPalletenne Aug 13, 2025
b965ccc
campaign info page
KavikaPalletenne Aug 13, 2025
24543e4
apply page
KavikaPalletenne Aug 13, 2025
9127e93
Serialize i64 IDs as strings
KavikaPalletenne Aug 13, 2025
1b09a10
update all array_agg of json to include to_jsonb
KavikaPalletenne Aug 13, 2025
1039f86
display application questions
KavikaPalletenne Aug 13, 2025
a22487e
fix types for question data in fe
KavikaPalletenne Aug 13, 2025
788dbc0
Update seeder.rs
KavikaPalletenne Aug 16, 2025
5932c71
fetch and show questions for selected roles
KavikaPalletenne Aug 16, 2025
64aca23
fix width of questions
KavikaPalletenne Aug 16, 2025
9ff1e52
smaller role selection buttons
KavikaPalletenne Aug 16, 2025
bc9f5ab
create or get application on page load
KavikaPalletenne Aug 20, 2025
c45e7ee
fetch answers for common & role questions
KavikaPalletenne Aug 20, 2025
22b8650
get answers
KavikaPalletenne Aug 21, 2025
8971497
fix `AnswerData` enum tagging
KavikaPalletenne Aug 21, 2025
7988c0d
first version of AGENTS.md file
KavikaPalletenne Aug 26, 2025
be528e9
handle answer update & delete
KavikaPalletenne Aug 27, 2025
a8eebfa
fix new answer being deleted
KavikaPalletenne Aug 27, 2025
042c9b3
don't require login for campaign
KavikaPalletenne Sep 23, 2025
345055a
application role preferences & admin review setup
KavikaPalletenne Sep 25, 2025
2bbc28f
Update yarn.lock
KavikaPalletenne Sep 25, 2025
fddd147
update to node 24
KavikaPalletenne Sep 25, 2025
18bce49
endpoints for getting & updating application ratings
KavikaPalletenne Sep 30, 2025
91ae292
Chaos 571 - Add Notion-style form editor (#575)
peternuyn Sep 30, 2025
f13c7de
update lockfile
gyoumi Oct 1, 2025
730e814
integrate application review with the backend
Oct 4, 2025
c10f9cc
fixing subcom application integration with backend including the auto…
Oct 6, 2025
dcaa246
update backend readme to include `libssl-dev` install
KavikaPalletenne Oct 7, 2025
c1a27b1
update frontend lock files
KavikaPalletenne Oct 7, 2025
4fbe202
Email templates (purely frontend) (#579)
remyjelee Oct 9, 2025
c575612
yarn -> bun migration
gyoumi Oct 10, 2025
992a84f
Chaos 577 create campaign be fe (#581)
Plebbaroni Oct 23, 2025
5a3a29e
fix spelling
KavikaPalletenne Oct 23, 2025
39cab2a
feat: added published flag to campaign (#589)
AlexMIaoPU Oct 28, 2025
e52c2af
588 fe dev only buttons for login + Added get_all orgs in BE (#591)
AlexMIaoPU Oct 28, 2025
f8a22a4
documentation fixes
KavikaPalletenne Oct 28, 2025
bc0b319
update authz for application reads
KavikaPalletenne Oct 28, 2025
972cab0
only show answers/questions for submitted applications
KavikaPalletenne Oct 28, 2025
2216db0
fix sql `==` check
KavikaPalletenne Oct 28, 2025
eedfa70
Progress on campaign redesign, (#594)
RyanTan182 Nov 11, 2025
e703f5e
Fixed Migration Error (#596)
RyanTan182 Nov 11, 2025
5e178ea
fixed migration :(((( (
RyanTan182 Nov 11, 2025
f2bdd2a
user names are defaulted to 'user' and have default profile icon (#601)
reganbenedetti Nov 21, 2025
fce6403
Chaos 584 integrate be fe adminmemberfunc (#593)
Plebbaroni Nov 21, 2025
5e20dbe
run rust build workflow on pulls to `CHAOS-571-integrate-be-fe`
KavikaPalletenne Nov 23, 2025
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
2 changes: 1 addition & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Rust

on:
pull_request:
branches: [main, "renovate/*", "CHAOS-224-KHAOS-rewrite"]
branches: [main, "renovate/*", "CHAOS-224-KHAOS-rewrite", "CHAOS-571-integrate-be-fe"]
push:
branches: ["renovate/*"]

Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
**/.DS_Store
**/.DS_Store
**/.vscode/
target/
157 changes: 157 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# Agent Guidelines for Chaos Repository

## Build/Lint/Test Commands

### Frontend (React/TypeScript)
- **Start dev server**: `cd frontend && yarn start`
- **Build**: `cd frontend && yarn build`
- **Lint**: `cd frontend && yarn lint`
- **Lint fix**: `cd frontend && yarn lint:fix`
- **Format**: `cd frontend && yarn format`
- **Type check**: `cd frontend && npx tsc --noEmit`

### Backend (Rust)
- **Build**: `cd backend/server && cargo build`
- **Run**: `cd backend/server && cargo run`
- **Format**: `cd backend/server && cargo fmt`
- **Check**: `cd backend/server && cargo check`
- **Test**: `cd backend/server && cargo test`

### Database
- **Run migrations**: Run `sqlx migrate run` in `backend` directory
- **Create new migrations**: Run `sqlx migrate add <name>` e.g. `sqlx migrate add user_settings` in `backend` directory. This will create a file of the format `<time>_name.sql` in `backend/migrations` directory

## Architecture Overview

### Backend (Rust/Axum)
The backend follows a clean architecture pattern with three main layers:

**Handler Layer** (`backend/server/src/handler/`):
- Contains HTTP request handlers organized by domain (user, application, campaign, etc.)
- Each handler module contains structs with methods that process HTTP requests
- Handlers extract data from requests, call service layer methods, and return responses
- All handler functions must be documented with `///` comments explaining purpose, parameters, and return values
- Example: `UserHandler` has methods like `get()`, `update_name()`, `update_pronouns()`

**Service Layer** (`backend/server/src/service/`):
- Contains business logic and database operations
- Each service module handles the core functionality for its domain
- Services interact directly with the database using SQLx
- All service functions must be documented with `///` comments
- **Database Optimization**: Minimize DB queries per function to reduce round trips
- **Complex Queries**: Use large SQL queries with nested one-to-many objects as vectors of `sqlx::Json`
- Includes authentication (JWT, OAuth2), email handling, and file storage

**Model Layer** (`backend/server/src/models/`):
- Contains data structures and database interaction logic
- Each model represents a database entity with serialization/deserialization
- Models use SQLx traits like `FromRow` for database mapping
- All structs and their fields must be documented with `///` comments
- Includes error types, authentication structs, and utility types

**Key Patterns**:
- Database transactions are handled via `DBTransaction` wrapper
- Authentication uses JWT tokens with Google OAuth2 integration
- File storage uses S3-compatible services
- Email functionality via Lettre library
- ID generation using Snowflake algorithm
- **Database Query Optimization**: Minimize DB round trips by using complex queries with nested data:
```sql
-- Example: Get campaign with all roles and questions in one query
SELECT
campaigns.*,
COALESCE(json_agg(DISTINCT roles.*) FILTER (WHERE roles.id IS NOT NULL), '[]') as roles,
COALESCE(json_agg(DISTINCT questions.*) FILTER (WHERE questions.id IS NOT NULL), '[]') as questions
FROM campaigns
LEFT JOIN roles ON roles.campaign_id = campaigns.id
LEFT JOIN questions ON questions.campaign_id = campaigns.id
WHERE campaigns.id = $1
GROUP BY campaigns.id
```
- **i64 ID Serialization**: All i64 IDs must use `#[serde(serialize_with = "crate::models::serde_string::serialize")]` and `#[serde(deserialize_with = "crate::models::serde_string::deserialize")]` to convert between i64 and string representations for JavaScript compatibility
- **API Documentation**: All handler endpoints must be documented in `backend/api.yaml` with:
- `operationId`: Unique identifier for the operation
- `description`: Clear description of what the endpoint does
- `tags`: Appropriate categorization (e.g., "User", "Auth", "Campaign")
- Request/response schemas with examples
- Error response definitions

### Frontend (React/TypeScript)
The frontend follows a component-based architecture with clear separation of concerns:

**Component Structure** (`frontend/src/components/`):
- Reusable UI components organized by feature (AdminSideBar, CampaignCard, etc.)
- Styled using twin.macro (Tailwind CSS + Emotion)
- Components use TypeScript with strict typing
- Follows atomic design principles with ui/ folder for base components

**Page Structure** (`frontend/src/pages/`):
- Route-based page components with lazy loading
- Organized by feature areas (admin, application_page, dashboard, etc.)
- Each page handles its own state management and API calls

**Context Management** (`frontend/src/contexts/`):
- React contexts for global state (UserContext, MessagePopupContext)
- Centralized state management for user authentication and UI state

**API Layer** (`frontend/src/api/`):
- Centralized API client using fetch with custom error handling
- Handles large integer serialization to avoid JavaScript precision issues
- Cookie-based authentication with backend
- **Large Integer Processing**: Automatically converts i64 IDs from strings to preserve precision:
```typescript
// Regex-based processing in API client converts large integers to strings
const processedText = text.replace(/"id":(\d{16,})/g, '"id":"$1"')
```

**Key Patterns**:
- Custom hooks for data fetching and state management
- Toast notifications for user feedback
- Form handling with react-hook-form and Zod validation
- Responsive design with Tailwind CSS
- Component composition over inheritance

## Code Style Guidelines

### TypeScript/React (Frontend)
- **Imports**: Group by builtin → external → internal → parent → sibling → index → object → type. Alphabetize within groups.
- **Naming**: camelCase for variables/functions, PascalCase for types/components, UPPER_CASE for constants
- **Types**: Strict TypeScript with `strict: true`, consistent type imports (`import type`)
- **Components**: Arrow function components, .tsx/.ts extensions only
- **Formatting**: Prettier with 2-space indentation, 80 char width, double quotes, semicolons
- **Linting**: Airbnb + TypeScript rules, no console logs in production
- **ID Field Types**: All ID fields must be `string` type to handle i64 integers from backend:
```typescript
export type User = {
id: string; // Not number - handles large i64 values
campaign_id: string;
organisation_id: string;
// ... other fields
}
```

### Rust (Backend)
- **Formatting**: Standard rustfmt (enforced by pre-commit)
- **Naming**: Standard Rust conventions (snake_case for functions/variables, PascalCase for types)
- **Error handling**: Use `anyhow` and `thiserror` for consistent error types
- **Async**: Use `tokio` runtime with async/await patterns
- **Documentation**: All functions and structs must be documented with `///` comments
- **API Documentation**: All new handler functions must be documented in `backend/api.yaml` using OpenAPI 3.0.0 specification
- **Database Optimization**: Minimize DB queries per function to reduce round trips:
- Use complex SQL queries with JOINs and aggregations
- Return nested one-to-many relationships as `Vec<sqlx::Json<T>>`
- Prefer single queries over multiple round trips when possible
- **i64 ID Handling**: All i64 IDs must be serialized as strings for JavaScript compatibility:
```rust
#[serde(serialize_with = "crate::models::serde_string::serialize")]
#[serde(deserialize_with = "crate::models::serde_string::deserialize")]
pub id: i64,
```
- Use the existing `serde_string` module functions for serialization
- Frontend TypeScript types must use `string` type for all ID fields
- This prevents JavaScript Number precision issues with large integers

### General
- **Pre-commit**: Run `pre-commit install` to enable automatic formatting/linting
- **No unused vars**: Prefix with `_` to ignore, or remove if truly unused
- **Security**: Never commit secrets, use environment variables via `.env` files
16 changes: 12 additions & 4 deletions backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,22 @@ CHAOS' backend is implemented in Rust and for data persistence, we use PostgreSQ

## Dev Setup

### Backend Development
To run the backend in a dev/testing environment:
1. Install `docker-compose` (see [official installation guide](https://docs.docker.com/compose/install/)).
2. Navigate to the directory this file is in (`backend`) in your terminal (not `backend/server`).
3. Possibly terminate any running instances of postgres, as the dockerized postgres we will spawn uses the same default port, so the two might interefere with each other.
4. Run `./setup-dev-env.sh` (you might have to make it executable before with `chmod +x setup-dev-env.sh`), which should drop you into a new shell that has the required tools installed.
5. Now, you can `cd server` and should be able to `cargo build` successfully.
6. Once you exit out of the newly created shell (e.g. type `exit`, or kill the terminal), the dockerized postgres instance should automatically be torn down, so it's not unnecessarily running in the background all the time.

4. If you are using WSL/Linux, install the OpenSSL development package with `sudo apt install libssl-dev`.
5. Run `./setup-dev-env.sh` (you might have to make it executable before with `chmod +x setup-dev-env.sh`), which should drop you into a new shell that has the required tools installed. This will install the SQLx CLI (for managing database transactions) and start a Postgres container in Docker.
6. Seed the database with demo data by running `cargo run` in the `backend/database-seeding` folder.
7. Now, you can `cd server` and should be able to `cargo build` successfully.
8. Once you exit out of the newly created shell (e.g. type `exit`, or kill the terminal), the dockerized postgres instance should automatically be torn down, so it's not unnecessarily running in the background all the time.

### Authentication
Some routes are only accessible by Users/Admins/SuperAdmins. To login your browser with a respective User/Admin/SuperAdmin cookie, seed your database as above (step 5), and then call one of the following routes in your browser:
- **Normal User:** `/api/v1/dev/user_login`
- **Organisation Admin User:** `/api/v1/dev/org_admin_login`
- **Super Admin User:** `/api/v1/dev/super_admin_login`

## Code Structure

Expand Down
8 changes: 4 additions & 4 deletions backend/api.json
Original file line number Diff line number Diff line change
Expand Up @@ -1716,7 +1716,7 @@
},
"finalised": {
"type": "boolean",
"description": "Whether this role has been finalised (e.g. max avaliable number)",
"description": "Whether this role has been finalised (e.g. max available number)",
"example": false
}
}
Expand Down Expand Up @@ -1936,7 +1936,7 @@
},
"finalised": {
"type": "boolean",
"description": "Whether this role has been finalised (e.g. max avaliable number)",
"description": "Whether this role has been finalised (e.g. max available number)",
"example": false
}
}
Expand Down Expand Up @@ -2002,7 +2002,7 @@
},
"finalised": {
"type": "boolean",
"description": "Whether this role has been finalised (e.g. max avaliable number)",
"description": "Whether this role has been finalised (e.g. max available number)",
"example": true
}
}
Expand Down Expand Up @@ -2638,7 +2638,7 @@
},
"finalised": {
"type": "boolean",
"description": "Whether this role has been finalised (e.g. max avaliable number)",
"description": "Whether this role has been finalised (e.g. max available number)",
"example": false
}
}
Expand Down
78 changes: 75 additions & 3 deletions backend/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1163,6 +1163,37 @@ paths:
"307":
$ref: "#/components/responses/NotLoggedIn"

/campaign/{id}/apply:
post:
operationId: createOrGetApplication
parameters:
- name: id
in: path
description: Campaign ID
required: true
schema:
type: integer
format: int64
description: Create a new application or get existing application for the specified campaign
tags:
- Campaign
responses:
"200":
description: OK
content:
application/json:
schema:
properties:
application_id:
type: integer
format: int64
description: ID of the created or existing application
example: 1541815603606036480
"401":
$ref: "#/components/responses/NotLoggedIn"
"307":
$ref: "#/components/responses/NotLoggedIn"

/campaign/{id}/applications:
get:
operationId: getCampaignApplications
Expand Down Expand Up @@ -1984,6 +2015,30 @@ paths:
$ref: "#/components/responses/NotLoggedIn"

/application/{application_id}/roles:
get:
operationId: getApplicationRoles
parameters:
- name: application_id
in: path
description: Application ID
required: true
schema:
type: integer
format: int64
description: Get all roles for a specific application
tags:
- Campaign
responses:
"200":
description: OK
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/ApplicationRole"
"401":
$ref: "#/components/responses/NotLoggedIn"
patch:
operationId: updateApplicationRoles
parameters:
Expand Down Expand Up @@ -2485,6 +2540,23 @@ components:
type: string
example: 2024-03-15T18:25:43.511Z

ApplicationRole:
type: object
properties:
id:
type: string
example: "6996987893965227483"
application_id:
type: string
example: "6996987893965227484"
campaign_role_id:
type: string
example: "6996987893965227485"
preference:
type: integer
format: int32
example: 1

NewAnswer:
type: object
properties:
Expand Down Expand Up @@ -2582,7 +2654,7 @@ components:
example: 3
finalised:
type: boolean
description: Whether this role has been finalised (e.g. max avaliable number)
description: Whether this role has been finalised (e.g. max available number)
example: False

RoleDetails:
Expand Down Expand Up @@ -2610,7 +2682,7 @@ components:
example: 3
finalised:
type: boolean
description: Whether this role has been finalised (e.g. max avaliable number)
description: Whether this role has been finalised (e.g. max available number)
example: False

User:
Expand Down Expand Up @@ -2939,7 +3011,7 @@ components:
example: 3
finalised:
type: boolean
description: Whether this role has been finalised (e.g. max avaliable number)
description: Whether this role has been finalised (e.g. max available number)
example: False

NewApplication:
Expand Down
Loading
Loading