Skip to content
Merged
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
8 changes: 8 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
FROM mcr.microsoft.com/devcontainers/base:ubuntu

# Setup packages
RUN apt update && export DEBIAN_FRONTEND=noninteractive \
&& apt -y install --no-install-recommends libssl-dev pkgconf build-essential postgresql-client

# Note: Rust, Node.js, and Bun are installed via devcontainer.json features.
# sqlx-cli will be installed in post-create.sh to ensure it uses the correct cargo environment.
27 changes: 27 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "Chaos Dev Container",
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspaces/chaos",
"features": {
"ghcr.io/devcontainers/features/node:1": {
"version": "lts"
},
"ghcr.io/devcontainers/features/rust:1": {
"version": "latest"
},
"ghcr.io/michidk/devcontainers-features/bun:1": {
"version": "latest"
}
},
"customizations": {
"vscode": {
"extensions": [
"rust-lang.rust-analyzer",
"DBCode.dbcode"
]
}
},
"postCreateCommand": "bash .devcontainer/post-create.sh",
"remoteUser": "vscode"
}
29 changes: 29 additions & 0 deletions .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
services:
app:
build:
context: .
dockerfile: Dockerfile
volumes:
- ..:/workspaces/chaos:cached
# Overrides default command so things don't shut down after the process ends.
command: sleep infinity
# Use the same network as the db service to access it via localhost
network_mode: service:db

db:
image: postgres:17
restart: unless-stopped
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: chaos
volumes:
- chaos-postgres-data:/var/lib/postgresql/data
# Forward ports: 5432 (db), 3000 (frontend), 8080 (backend)
ports:
- "5433:5432"
- "3000:3000"
- "8080:8080"

volumes:
chaos-postgres-data:
46 changes: 46 additions & 0 deletions .devcontainer/post-create.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/bin/bash
set -e

echo "Starting post-create setup..."

# Create backend/.env
echo "Creating backend/.env..."
cat << 'EOF' > backend/.env
DATABASE_URL="postgres://postgres:password@localhost:5432/chaos"
JWT_SECRET="test_secret"
GOOGLE_CLIENT_ID="test"
GOOGLE_CLIENT_SECRET="test"
GOOGLE_REDIRECT_URI="http://localhost:3000/auth/callback"
S3_BUCKET_NAME="chaos-storage"
S3_ACCESS_KEY="test_access_key"
S3_SECRET_KEY="test_secret_key"
S3_ENDPOINT="https://chaos-storage.s3.ap-southeast-1.amazonaws.com"
S3_REGION_NAME="ap-southeast-1"
DEV_ENV="dev"
SMTP_USERNAME="test_username"
SMTP_PASSWORD="test_password"
SMTP_HOST="smtp.example.com"
EOF

# Install sqlx-cli if not present
if ! command -V sqlx &> /dev/null; then
echo "Installing sqlx-cli..."
cargo install sqlx-cli --no-default-features --features native-tls,postgres
fi

# Wait for Postgres
echo "Waiting for Postgres..."
until PGPASSWORD=password psql -h "db" -U "postgres" -d "chaos" -c '\q' 2>/dev/null; do
echo "Waiting for postgres at db:5432..."
sleep 2
done

# Setup DB
echo "Setting up database..."
cd backend

# Create database if not exists
sqlx database create || true
sqlx migrate run

echo "Setup complete!"
38 changes: 38 additions & 0 deletions .github/workflows/frontend-nextjs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Frontend (Next.js)

on:
pull_request:
branches: [main, "renovate/*", "CHAOS-224-KHAOS-rewrite", "CHAOS-571-integrate-be-fe", "CHAOS-598-nextjs"]
paths:
- "frontend-nextjs/**"
- ".github/workflows/frontend-nextjs.yml"
push:
branches: ["renovate/*"]
paths:
- "frontend-nextjs/**"
- ".github/workflows/frontend-nextjs.yml"

jobs:
build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./frontend-nextjs

steps:
- uses: actions/checkout@v4

- name: Setup Bun
uses: oven-sh/setup-bun@v1
with:
bun-version: latest

- name: Install dependencies
run: bun install

- name: Build
run: bun run build
env:
NEXT_PUBLIC_APP_URL: http://localhost:3000
NEXT_OAUTH_CALLBACK_URL: http://localhost:8080/auth/google
NEXT_API_BASE_URL: https://localhost:8080
8 changes: 7 additions & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@ name: Rust

on:
pull_request:
branches: [main, "renovate/*", "CHAOS-224-KHAOS-rewrite", "CHAOS-571-integrate-be-fe"]
branches: [main, "renovate/*", "CHAOS-224-KHAOS-rewrite", "CHAOS-571-integrate-be-fe", "CHAOS-598-nextjs"]
paths:
- "backend/**"
- ".github/workflows/rust.yml"
push:
branches: ["renovate/*"]
paths:
- "backend/**"
- ".github/workflows/rust.yml"

env:
CARGO_TERM_COLOR: always
Expand Down
12 changes: 7 additions & 5 deletions backend/database-seeding/src/seeder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,14 +325,16 @@ pub async fn seed_database(mut seeder: Seeder) {
.await.expect("Failed seeding Rating 3");

let template =
"Hello {{name}},
"
Hello {{name}},

Congratulations! You have been selected for the role of {{role}} at {{organisation_name}} for our {{campaign_name}}.
Congratulations! You have been selected for the role of {{role}} at {{organisation_name}} for our {{campaign_name}}.

Please confirm your acceptance by {{expiry_date}}.
Please confirm your acceptance by {{expiry_date}}.

Best regards,
The {{organisation_name}} Team".to_string();
Best regards,
The {{organisation_name}} Team
".to_string();


let email_template_id = Organisation::create_email_template(
Expand Down
1 change: 1 addition & 0 deletions backend/server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ chrono = { version = "0.4", features = ["serde"] }
oauth2 = "4.4"
log = "0.4"
uuid = { version = "1.5", features = ["serde", "v4"] }
nanoid = "0.4.0"
rust-s3 = "0.34.0"
rs-snowflake = "0.6"
jsonwebtoken = "9.1"
Expand Down
3 changes: 3 additions & 0 deletions backend/server/src/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub const NANOID_ALPHABET: [char; 16] = [
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f'
];
6 changes: 3 additions & 3 deletions backend/server/src/handler/answer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
//! - Managing role-specific answers

use crate::models::answer::{Answer, NewAnswer};
use crate::models::app::AppState;
use crate::models::app::{AppMessage, AppState};
use crate::models::auth::{AnswerOwner, ApplicationOwner, ApplicationOwnerOrReviewer};
use crate::models::error::ChaosError;
use crate::models::transaction::DBTransaction;
Expand Down Expand Up @@ -141,7 +141,7 @@ impl AnswerHandler {

transaction.tx.commit().await?;

Ok((StatusCode::OK, "Successfully updated answer"))
Ok(AppMessage::OkMessage("Successfully updated answer"))
}

/// Deletes an answer.
Expand Down Expand Up @@ -169,6 +169,6 @@ impl AnswerHandler {

transaction.tx.commit().await?;

Ok((StatusCode::OK, "Successfully deleted answer"))
Ok(AppMessage::OkMessage("Successfully deleted answer"))
}
}
8 changes: 4 additions & 4 deletions backend/server/src/handler/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ impl ApplicationHandler {
) -> Result<impl IntoResponse, ChaosError> {
Application::set_status(application_id, data, &mut transaction.tx).await?;
transaction.tx.commit().await?;
Ok((StatusCode::OK, "Status successfully updated"))
Ok(AppMessage::OkMessage("Status successfully updated"))
}

/// Updates the private status of an application.
Expand All @@ -137,7 +137,7 @@ impl ApplicationHandler {
) -> Result<impl IntoResponse, ChaosError> {
Application::set_private_status(application_id, data, &mut transaction.tx).await?;
transaction.tx.commit().await?;
Ok((StatusCode::OK, "Private Status successfully updated"))
Ok(AppMessage::OkMessage("Private Status successfully updated"))
}

/// Retrieves all applications for the current user.
Expand Down Expand Up @@ -209,7 +209,7 @@ impl ApplicationHandler {
) -> Result<impl IntoResponse, ChaosError> {
Application::update_roles(application_id, data.roles, &mut transaction.tx).await?;
transaction.tx.commit().await?;
Ok((StatusCode::OK, "Successfully updated application roles"))
Ok(AppMessage::OkMessage("Successfully updated application roles"))
}

/// Submits an application for review.
Expand All @@ -235,7 +235,7 @@ impl ApplicationHandler {
) -> Result<impl IntoResponse, ChaosError> {
Application::submit(application_id, &mut transaction.tx).await?;
transaction.tx.commit().await?;
Ok((StatusCode::OK, "Successfully submitted application"))
Ok(AppMessage::OkMessage("Successfully submitted application"))
}

/// Retrieves the rating for an application given by the current user.
Expand Down
14 changes: 7 additions & 7 deletions backend/server/src/handler/campaign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
//! - Banner image handling

use crate::models;
use crate::models::app::AppState;
use crate::models::app::{AppMessage, AppState};
use crate::models::application::Application;
use crate::models::application::NewApplication;
use crate::models::auth::AuthUser;
Expand Down Expand Up @@ -116,7 +116,7 @@ impl CampaignHandler {
) -> Result<impl IntoResponse, ChaosError> {
Campaign::update(id, request_body, &mut transaction.tx).await?;
transaction.tx.commit().await?;
Ok((StatusCode::OK, "Successfully updated campaign"))
Ok(AppMessage::OkMessage("Successfully updated campaign"))
}

/// Publishes a campaign by setting its published field to true.
Expand All @@ -139,7 +139,7 @@ impl CampaignHandler {
) -> Result<impl IntoResponse, ChaosError> {
Campaign::publish(id, &mut transaction.tx).await?;
transaction.tx.commit().await?;
Ok((StatusCode::OK, "Successfully published campaign"))
Ok(AppMessage::OkMessage("Successfully published campaign"))
}

/// Updates a campaign's banner image.
Expand Down Expand Up @@ -188,7 +188,7 @@ impl CampaignHandler {
) -> Result<impl IntoResponse, ChaosError> {
Campaign::delete(id, &mut transaction.tx).await?;
transaction.tx.commit().await?;
Ok((StatusCode::OK, "Successfully deleted campaign"))
Ok(AppMessage::OkMessage("Successfully deleted campaign"))
}

/// Creates a new role in a campaign.
Expand All @@ -215,7 +215,7 @@ impl CampaignHandler {
) -> Result<impl IntoResponse, ChaosError> {
Role::create(id, data, &mut transaction.tx, &mut state.snowflake_generator).await?;
transaction.tx.commit().await?;
Ok((StatusCode::OK, "Successfully created role"))
Ok(AppMessage::OkMessage("Successfully created role"))
}

/// Retrieves all roles in a campaign.
Expand Down Expand Up @@ -274,7 +274,7 @@ impl CampaignHandler {
)
.await?;
transaction.tx.commit().await?;
Ok((StatusCode::OK, "Successfully created application"))
Ok(AppMessage::OkMessage("Successfully created application"))
}

/// Retrieves all applications for a campaign.
Expand Down Expand Up @@ -334,7 +334,7 @@ impl CampaignHandler {
.await?;
transaction.tx.commit().await?;

Ok((StatusCode::OK, "Successfully created offer"))
Ok(AppMessage::OkMessage("Successfully created offer"))
}

/// Retrieves all offers for a campaign.
Expand Down
32 changes: 29 additions & 3 deletions backend/server/src/handler/email_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ use crate::models::auth::EmailTemplateAdmin;
use crate::models::email_template::EmailTemplate;
use crate::models::error::ChaosError;
use crate::models::transaction::DBTransaction;
use axum::extract::{Json, Path};
use axum::extract::{Json, Path, State};
use axum::http::StatusCode;
use axum::response::IntoResponse;
use crate::models::app::{AppMessage, AppState};

/// Handler for email template-related HTTP requests.
pub struct EmailTemplateHandler;
Expand Down Expand Up @@ -70,7 +71,7 @@ impl EmailTemplateHandler {
.await?;

transaction.tx.commit().await?;
Ok((StatusCode::OK, "Successfully updated email template"))
Ok(AppMessage::OkMessage("Successfully updated email template"))
}

/// Deletes an email template.
Expand All @@ -94,6 +95,31 @@ impl EmailTemplateHandler {
EmailTemplate::delete(id, &mut transaction.tx).await?;

transaction.tx.commit().await?;
Ok((StatusCode::OK, "Successfully delete email template"))
Ok(AppMessage::OkMessage("Successfully deleted email template"))
}

/// Duplicates an email template.
///
/// This handler allows email template admins to duplicate templates.
///
/// # Arguments
///
/// * `_user` - The authenticated user (must be an email template admin)
/// * `id` - The ID of the template to delete
/// * `state` - The application state
///
/// # Returns
///
/// * `Result<impl IntoResponse, ChaosError>` - Success message or error
pub async fn duplicate(
_user: EmailTemplateAdmin,
Path(id): Path<i64>,
State(mut state): State<AppState>,
mut transaction: DBTransaction<'_>,
) -> Result<impl IntoResponse, ChaosError> {
EmailTemplate::duplicate(id, &mut transaction.tx, &mut state.snowflake_generator).await?;

transaction.tx.commit().await?;
Ok(AppMessage::OkMessage("Successfully duplicated email template"))
}
}
Loading
Loading