Skip to content

Commit

Permalink
Merge pull request #10 from zayman2045/auto-deploy
Browse files Browse the repository at this point in the history
Enhance CI/CD Pipeline, Backend Error Handling, and Configuration Updates
  • Loading branch information
zayman2045 authored Jan 7, 2025
2 parents 26e31d3 + 6f7c9ab commit be1043b
Show file tree
Hide file tree
Showing 9 changed files with 202 additions and 91 deletions.
143 changes: 143 additions & 0 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
name: CI/CD Pipeline

on:
pull_request:
push:
branches:
- main

permissions:
id-token: write
contents: read

jobs:
test-frontend:
runs-on: ubuntu-20.04

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
target: wasm32-unknown-unknown

- name: Install wasm-pack
uses: jetli/wasm-pack-action@v0.4.0

- name: Setup Chrome and ChromeDriver
uses: browser-actions/setup-chrome@v1
with:
chrome-version: stable
install-chromedriver: true
install-dependencies: true

- name: Build and test frontend
working-directory: ./frontend
run: wasm-pack test --chrome --headless

test-backend:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Rust
uses: actions-rust-lang/setup-rust-toolchain@v1

- name: Build and test backend
working-directory: ./backend
run: cargo test
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
JWT_SECRET: ${{ secrets.JWT_SECRET }}

deploy-to-aws:
runs-on: ubuntu-20.04
needs: [test-frontend, test-backend]

env:
FRONTEND_IMAGE: public.ecr.aws/m8r7m6j8/portfolio-frontend-1:latest
BACKEND_IMAGE: public.ecr.aws/m8r7m6j8/portfolio-backend-1:latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::637423446384:role/github-actions-role
aws-region: us-east-1

- name: Login to Amazon ECR Public
id: login-ecr-public
uses: aws-actions/amazon-ecr-login@v2
with:
registry-type: public

- name: Build, tag, and push frontend Docker image
run: |
docker buildx build --platform linux/amd64 -t $FRONTEND_IMAGE --push frontend
- name: Build, tag, and push backend Docker image
run: |
docker buildx build --platform linux/amd64 -t $BACKEND_IMAGE --push backend
- name: Upload Frontend Dockerrun.aws.json.zip to S3
run: |
aws s3 cp frontend/Dockerrun.aws.json.zip s3://elasticbeanstalk-us-east-2-637423446384/PortfolioWebsite-Frontend-env/Dockerrun.aws.json.zip
- name: Generate version labels
run: |
echo "FRONTEND_VERSION_LABEL=frontend-$(date +%s)" >> $GITHUB_ENV &&
echo "BACKEND_VERSION_LABEL=backend-$(date +%s)" >> $GITHUB_ENV
- name: Create application version for frontend
env:
FRONTEND_VERSION_LABEL: ${{ env.FRONTEND_VERSION_LABEL }}
run: |
aws elasticbeanstalk create-application-version \
--application-name "Portfolio Website" \
--version-label $FRONTEND_VERSION_LABEL \
--source-bundle S3Bucket="elasticbeanstalk-us-east-2-637423446384",S3Key="PortfolioWebsite-Frontend-env/Dockerrun.aws.json.zip" \
--region us-east-2
- name: Update environment for frontend
env:
FRONTEND_VERSION_LABEL: ${{ env.FRONTEND_VERSION_LABEL }}
run: |
aws elasticbeanstalk update-environment \
--application-name "Portfolio Website" \
--environment-name "PortfolioWebsite-Frontend-env" \
--version-label $FRONTEND_VERSION_LABEL \
--region us-east-2
- name: Upload Backend Dockerrun.aws.json.zip to S3
run: |
aws s3 cp backend/Dockerrun.aws.json.zip s3://elasticbeanstalk-us-east-2-637423446384/PortfolioWebsite-Backend-env/Dockerrun.aws.json.zip
- name: Create application version for backend
env:
BACKEND_VERSION_LABEL: ${{ env.BACKEND_VERSION_LABEL }}
run: |
aws elasticbeanstalk create-application-version \
--application-name "Portfolio Website" \
--version-label $BACKEND_VERSION_LABEL \
--source-bundle S3Bucket="elasticbeanstalk-us-east-2-637423446384",S3Key="PortfolioWebsite-Backend-env/Dockerrun.aws.json.zip" \
--region us-east-2
- name: Update environment for backend
env:
BACKEND_VERSION_LABEL: ${{ env.BACKEND_VERSION_LABEL }}
run: |
aws elasticbeanstalk update-environment \
--application-name "Portfolio Website" \
--environment-name "PortfolioWebsite-Backend-env" \
--version-label $BACKEND_VERSION_LABEL \
--region us-east-2
26 changes: 0 additions & 26 deletions .github/workflows/test-backend.yml

This file was deleted.

35 changes: 0 additions & 35 deletions .github/workflows/test-frontend.yml

This file was deleted.

2 changes: 1 addition & 1 deletion backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ WORKDIR /usr/src/backend
RUN cargo init
COPY ./src ./src
COPY ./Cargo.toml ./Cargo.toml
ENV API_BASE_URL=http://xog-frontend-v2.us-east-2.elasticbeanstalk.com
ENV API_BASE_URL=http://www.xaviergriffith.com
RUN cargo build --release

# Production Runtime Stage
Expand Down
Binary file modified backend/Dockerrun.aws.json.zip
Binary file not shown.
62 changes: 39 additions & 23 deletions backend/src/routes/guards.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
//! Route guarding middleware.
use crate::{entities::{prelude::*, users}, utils::jwt::validate_jwt};
use std::sync::Arc;

use crate::{
entities::{prelude::*, users},
utils::jwt::validate_jwt,
};
use axum::{
headers::{authorization::Bearer, Authorization, HeaderMapExt},
http::Request,
Expand All @@ -10,42 +15,53 @@ use axum::{
use hyper::StatusCode;
use sea_orm::{ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter};

/// Checks if the user is logged in and inserts the user into the request extensions.
/// Intercepts requests to validate the JWT token. If the token is valid, the user is looked up in the database and inserted into the request extensions.
pub async fn token_guard<T>(
mut request: Request<T>,
next: Next<T>,
) -> Result<Response, StatusCode> {
// Get the token from the request header
let token = request
.headers()
.typed_get::<Authorization<Bearer>>()
.ok_or({
eprintln!("Failed to get token from request");
StatusCode::BAD_REQUEST})?
.token()
.to_owned();
let token = match request.headers().typed_get::<Authorization<Bearer>>() {
Some(auth) => {
auth.token().to_owned()
}
None => {
eprintln!("Failed to get token from request.");
return Err(StatusCode::BAD_REQUEST);
}
};

// Validate the token
validate_jwt(&token)?;

// Get the database connection
let database = request
.extensions()
.get::<DatabaseConnection>()
.ok_or({
eprintln!("Failed to get database connection from request");
StatusCode::INTERNAL_SERVER_ERROR})?;
let database = match request.extensions().get::<Arc<DatabaseConnection>>() {
Some(db) => {
db
}
None => {
eprintln!("Failed to get database connection from request.");
return Err(StatusCode::INTERNAL_SERVER_ERROR);
}
};

// Look the user up in the database using the token
let Some(user) = Users::find()
let user = match Users::find()
.filter(users::Column::Token.eq(Some(token.clone())))
.one(database)
.one(&**database)
.await
.map_err(|_e| {
eprintln!("Failed to find user in database");
StatusCode::INTERNAL_SERVER_ERROR})?
else {
return Err(StatusCode::UNAUTHORIZED);
{
Ok(Some(user)) => {
user
}
Ok(None) => {
eprintln!("User not found.");
return Err(StatusCode::UNAUTHORIZED);
}
Err(e) => {
eprintln!("Failed to find user in database: {:?}", e);
return Err(StatusCode::INTERNAL_SERVER_ERROR);
}
};

// Insert the user into the extensions
Expand Down
23 changes: 18 additions & 5 deletions backend/src/routes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,28 @@ pub async fn create_router(database: DatabaseConnection) -> Router {
let database = Arc::new(database);

// Import the API base URL from the environment
let api_base_url = std::env::var("API_BASE_URL")
.unwrap_or("http://localhost:8080".to_string())
.parse::<HeaderValue>()
.expect("Parse");
let env_api_base_url = match std::env::var("API_BASE_URL") {
Ok(val) => {
val.parse::<HeaderValue>().expect("Parse")
},
Err(_) => {
eprintln!("API_BASE_URL not set, defaulting to http://localhost:8080");
"http://localhost:8080".parse::<HeaderValue>().expect("Parse")
}
};

// Define the allowed origins
let origins = [
env_api_base_url,
"http://xaviergriffith.com".parse().expect("Failed to parse HeaderValue"),
"http://www.xaviergriffith.com".parse().expect("Failed to parse HeaderValue"),
"http://xog-frontend-v2.us-east-2.elasticbeanstalk.com".parse().expect("Failed to parse HeaderValue"),
];

// Enable CORS, allowing GET, POST and DELETE requests
let cors = CorsLayer::new()
.allow_methods([Method::GET, Method::POST, Method::DELETE])
.allow_origin(api_base_url)
.allow_origin(origins)
.allow_headers(vec![CONTENT_TYPE, AUTHORIZATION]);

// Define the routes, assign handlers, and attaches layers
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ services:
JWT_SECRET: mysecret

database:
image: postgres
image: postgres:16
environment:
POSTGRES_PASSWORD: mysecretpassword
volumes:
Expand Down
Binary file modified frontend/Dockerrun.aws.json.zip
Binary file not shown.

0 comments on commit be1043b

Please sign in to comment.