Skip to content

Commit 0fa07dc

Browse files
committed
add ci/cd with deployment fot gh action
1 parent 227282f commit 0fa07dc

File tree

9 files changed

+324
-294
lines changed

9 files changed

+324
-294
lines changed

.github/workflows/ci-cd.yml

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
name: Leptos Blog CI/CD
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
workflow_dispatch:
9+
10+
env:
11+
CARGO_TERM_COLOR: always
12+
SQLX_OFFLINE: true
13+
14+
jobs:
15+
# Quick quality checks
16+
quality-check:
17+
name: Code Quality
18+
runs-on: ubuntu-latest
19+
20+
steps:
21+
- name: Checkout code
22+
uses: actions/checkout@v4
23+
24+
- name: Install Rust nightly
25+
uses: dtolnay/rust-toolchain@nightly
26+
with:
27+
components: rustfmt, clippy
28+
29+
- name: Cache cargo
30+
uses: actions/cache@v4
31+
with:
32+
path: |
33+
~/.cargo/registry/index/
34+
~/.cargo/registry/cache/
35+
~/.cargo/git/db/
36+
target/
37+
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
38+
39+
- name: Check formatting
40+
run: cargo fmt --all -- --check
41+
42+
- name: Run clippy
43+
run: cargo clippy --all-targets --features ssr -- -D warnings
44+
45+
# Build and test using your existing setup
46+
build-and-test:
47+
name: Build & Test
48+
runs-on: ubuntu-latest
49+
needs: quality-check
50+
51+
steps:
52+
- name: Checkout code
53+
uses: actions/checkout@v4
54+
55+
- name: Install Rust nightly
56+
uses: dtolnay/rust-toolchain@nightly
57+
with:
58+
targets: wasm32-unknown-unknown
59+
60+
- name: Cache cargo
61+
uses: actions/cache@v4
62+
with:
63+
path: |
64+
~/.cargo/registry/index/
65+
~/.cargo/registry/cache/
66+
~/.cargo/git/db/
67+
target/
68+
key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }}
69+
70+
- name: Install system dependencies
71+
run: |
72+
sudo apt-get update
73+
sudo apt-get install -y pkg-config libssl-dev
74+
75+
- name: Install cargo-leptos and sqlx-cli
76+
run: |
77+
curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
78+
cargo binstall cargo-leptos sqlx-cli --no-confirm --log-level warn
79+
80+
- name: Run tests
81+
run: cargo test --features ssr
82+
83+
- name: Build application
84+
run: cargo leptos build --release
85+
86+
# Deploy using your existing fly.toml configuration
87+
deploy:
88+
name: Deploy to Fly.io
89+
runs-on: ubuntu-latest
90+
needs: build-and-test
91+
if: github.ref == 'refs/heads/main'
92+
93+
steps:
94+
- name: Checkout code
95+
uses: actions/checkout@v4
96+
97+
- name: Setup Fly.io CLI
98+
uses: superfly/flyctl-actions/setup-flyctl@master
99+
100+
- name: Deploy to Fly.io
101+
run: flyctl deploy --remote-only
102+
env:
103+
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}

README.md

Lines changed: 217 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,238 @@
11
# Willian's Personal Blog
22

3-
Visit: [williannguyen.com](https://www.williannguyen.com)
3+
The blog is at [wiiliannguyen.com](https://www.williannguyen.com)
44

5-
## Overview
5+
## Architecture Overview
66

7-
A fast, modern blog built with full-stack Rust:
7+
This application follows a **full-stack Rust architecture** using:
88

9-
- **Frontend:** Leptos (SSR + hydration)
10-
- **Backend:** Axum + PostgreSQL
11-
- **Content:** Markdown posts, syntax highlighting
12-
- **Build:** Static content generation at compile time
9+
- **Frontend**: Leptos with hydration for interactive client-side features
10+
- **Backend**: Axum web framework with PostgreSQL database
11+
- **Rendering**: Server-side rendering (SSR) with client-side hydration
12+
- **Content**: Markdown-based blog posts with syntax highlighting
13+
- **Build System**: Custom build script for static content generation
1314

14-
## Features
15+
## Tech Stack
1516

16-
- Server-side rendering for speed
17-
- WASM-optimized client bundle
18-
- Markdown-based posts
19-
- Syntax highlighting for code
20-
- Multi-tier caching for assets and APIs
21-
- Security: rate limiting, CORS, CSRF, security headers, cache
22-
- Newsletter subscription
17+
### Core Framework
2318

24-
## Structure
19+
- **[Leptos 0.8](https://leptos.dev/)** - Reactive web framework for Rust
20+
- **[Axum 0.8](https://docs.rs/axum/)** - Modern async web framework
21+
- **[Tokio](https://tokio.rs/)** - Async runtime
22+
23+
### Database & ORM
24+
25+
- **[PostgreSQL](https://www.postgresql.org/)** - Primary database
26+
- **[SQLx 0.8](https://docs.rs/sqlx/)** - Async SQL toolkit with compile-time checked queries
27+
- **Database Migrations** - Version-controlled schema management
28+
29+
### Content Processing
30+
31+
- **[pulldown-cmark](https://docs.rs/pulldown-cmark/)** - Markdown parser
32+
- **[syntect](https://docs.rs/syntect/)** - Syntax highlighting for code blocks
33+
- **Static Content Generation** - Build-time markdown processing
34+
35+
### Middleware & Security
36+
37+
- **Rate Limiting** - Per-IP request throttling using Governor
38+
- **CORS** - Cross-Origin Resource Sharing configuration
39+
- **CSRF Protection** - Token-based CSRF mitigation
40+
- **Security Headers** - Comprehensive HTTP security headers
41+
- **Request Timeout** - Configurable request timeouts
42+
- **Compression** - Brotli compression for responses
43+
44+
### Development & Deployment
45+
46+
- **[cargo-leptos](https://crates.io/crates/cargo-leptos)** - Leptos build tool
47+
- **Hot Reload** - Development server with live reloading
48+
- **WASM Optimization** - Size-optimized WebAssembly builds
49+
- **End-to-End Testing** - Playwright integration
50+
51+
## Project Structure
52+
53+
### 🌐 Frontend (Leptos)
54+
55+
The implementation is obtained from cargo-leptos axum template provided from. For more detail, please follow the the [instruction](https://github.com/leptos-rs/start-axum).
2556

2657
```text
2758
src/
28-
app.rs # Main app & routing
29-
app/
30-
components/ # UI components (nav, footer, theme toggle, etc.)
31-
pages/ # Route-level pages (home, post, poem)
32-
helpers.rs # Shared utilities
33-
server.rs # Axum server setup
34-
server/ # Backend modules (db, middleware, models, routes, handlers,services, repositories,...)
35-
build.rs # Static content build script
36-
contents/ # Markdown posts
59+
├── app.rs # App component and routing
60+
├── client.rs # Client-side hydration entry
61+
├── app/
62+
│ ├── helpers.rs # UI utility functions
63+
│ ├── components.rs # Component module definitions
64+
│ ├── components/
65+
│ ├── pages.rs
66+
│ └── pages/ # Page module definitions
3767
```
3868

39-
## Build & Deploy
69+
### ⚙️ Backend (Axum) Architecture
70+
71+
The backend structure seen below is over-engineered for the purpose of personal blog with only public newsletter subcriber feature.
72+
73+
However, the industry-graded architecture is purposely used to study fullstack technology with Rust. The architecture is learnt from the book [FullStack Rust with Axum from Martin Fabio](https://www.amazon.com/FullStack-Rust-Axum-Server-Rendered-High-Performance-ebook/dp/B0FM6XF8YX)
74+
75+
```text
76+
├── main.rs # Application entry point
77+
├── server.rs # Server orchestration & middleware stack
78+
└── server/ # Modular backend architecture
79+
├── db.rs # Database module coordinator
80+
├── db/
81+
│ ├── config.rs # Database URL & connection config
82+
│ ├── pool.rs # PgPool initialization & management
83+
│ ├── state.rs # AppState with shared resources
84+
│ └── error.rs # Database-specific error handling
85+
├── middleware.rs # Middleware module coordinator
86+
├── middleware/
87+
│ ├── cache.rs # HTTP caching strategies
88+
│ ├── governor.rs # Rate limiting (IP-based)
89+
│ ├── csrf.rs # CSRF token protection
90+
│ ├── throttle.rs # Request throttling
91+
│ ├── global_layer.rs # Middleware layer coordinator
92+
│ └── global_layer/
93+
│ ├── cors.rs # Cross-Origin Resource Sharing
94+
│ └── security_headers.rs # Security headers middleware
95+
├── models.rs # Data model coordinator
96+
├── models/
97+
│ └── subscriber.rs # Newsletter subscriber model
98+
├── repositories.rs # Data access coordinator
99+
├── repositories/
100+
│ └── subscriber.rs # Database queries & data access
101+
├── services.rs # Business logic coordinator
102+
├── services/
103+
│ └── subscriber.rs # Newsletter business logic
104+
├── handlers.rs # Request handler coordinator
105+
├── handlers/
106+
│ └── subscriber.rs # HTTP request/response handling
107+
├── routes.rs # API route coordinator
108+
└── routes/
109+
└── subscriber.rs # Newsletter API endpoints
110+
```
111+
112+
### 🏘️ Backend Layer Relationships
113+
114+
<div style="display: flex; gap: 2em;">
115+
<div style="flex: 1; min-width: 300px;">
116+
117+
```mermaid
118+
graph TD
119+
Middleware["🧩 Middleware Stack<br/>(server.rs & routes/)"]
120+
Middleware -->|Applied globally and per route| Routes["🛣️ Routes"]
121+
Routes -->|Use| Handlers["👐 Handlers"]
122+
Handlers -->|Use| Services["🛎️ Services"]
123+
Services -->|Use| Repositories["📦 Repositories"]
124+
Repositories -->|Use| Models["📄 Models"]
125+
Repositories -->|Use| DB["🗄️ DB"]
126+
```
127+
128+
</div>
129+
<div style="flex: 1; min-width: 300px;">
130+
<b>Middleware & Security Features</b>
131+
<ul>
132+
<li>Rate Limiting: Per-IP request throttling using Governor</li>
133+
<li>CORS: Cross-Origin Resource Sharing configuration</li>
134+
<li>CSRF Protection: Token-based CSRF mitigation</li>
135+
<li>Security Headers: Comprehensive HTTP security headers</li>
136+
<li>Request Timeout: Configurable request timeouts</li>
137+
<li>Compression: Brotli compression for responses</li>
138+
</ul>
139+
<b>Middleware Implementation</b>
140+
<ul>
141+
<li>Global: Applied in server.rs (compression, timeout, CORS, security headers)</li>
142+
<li>Route-specific: Applied in routes/ modules. For example, subscriber routes apply no_cache, governor, throttle, and CSRF layers.</li>
143+
</ul>
144+
<b>Layer Responsibilities</b>
145+
<ul>
146+
<li>Routes: HTTP endpoints + middleware application, delegate to handlers</li>
147+
<li>Handlers: HTTP request/response processing, input validation</li>
148+
<li>Services: Business logic, orchestration, transaction management</li>
149+
<li>Repositories: Data access queries, DB operations using models</li>
150+
<li>Models: Data structures, serialization, validation rules</li>
151+
<li>DB: Connection pooling, configuration, state management</li>
152+
</ul>
153+
</div>
154+
</div>
155+
156+
## Build System
157+
158+
The project uses a custom build script (`build.rs`) that:
40159

41-
- Build with `cargo-leptos`
42-
- Deploy to Fly.io with managed Postgres
160+
1. **Processes Markdown Files**: Reads blog posts from `contents/posts/`
161+
2. **Syntax Highlighting**: Applies code highlighting using Syntect
162+
3. **Static Generation**: Converts markdown to HTML at build time
163+
4. **Optimized Output**: Generates Rust code with static post data
164+
165+
## Performance Features
166+
167+
- **Server-Side Rendering (SSR)**: Fast initial page loads
168+
- **Hydration**: Interactive client-side features without full SPA overhead
169+
- **Static Content**: Build-time markdown processing reduces runtime overhead
170+
- **Compression**: Brotli compression for smaller payload sizes
171+
- **Connection Pooling**: Efficient database connection management
172+
- **Request Timeout**: Prevents long-running requests from blocking resources
173+
- **HTTP Caching Strategy**: Multi-tier caching system for optimal performance
174+
- **WASM Optimization**: Aggressive size optimization for client-side bundles
175+
176+
## WASM Bundle Optimization
177+
178+
The WebAssembly build process includes several standard optimization techniques to minimize bundle size:
179+
180+
### Optimization Techniques
181+
182+
- Size-focused compilation (opt-level = 'z')
183+
- Link-time optimization (LTO)
184+
- Strip debug symbols (strip = true)
185+
- Abort on panic (panic = "abort")
186+
- Single codegen unit
187+
- Use wee_alloc for smaller WASM allocator
188+
189+
### Benchmark Results
190+
191+
Below information is obtained from the actual implementation on the project and get benchmark to show the efficency of these technuques.
192+
193+
| Metric | Before Optimization | After Optimization | Improvement |
194+
|--------|--------------------|--------------------|-------------|
195+
| **Bundle Size** | 8.5MB | 1.5MB | **82.4% smaller** |
196+
| **Gzipped Size** | ~2.1MB | ~400-600KB | ~75% smaller |
197+
| **Load Time Impact** | Baseline | Significantly improved | 5.6x smaller |
198+
199+
### Impact
200+
201+
- **Faster page loads**: 82% smaller WASM bundles load much faster
202+
- **Reduced bandwidth**: Significant savings in data transfer
203+
- **Better mobile experience**: Smaller bundles improve performance on slower connections
204+
- **Production ready**: Size is now within reasonable limits for web deployment
205+
206+
## Caching
207+
208+
Deploy standard industry practices
209+
210+
- **Static Assets:**
211+
- Uses `Cache-Control: public, max-age=31536000` for 1-year caching.
212+
- Assets are versioned for cache busting, ensuring users get updates when files change.
213+
- **API Responses:**
214+
- Uses `Cache-Control: public, max-age=60` for short-term caching (1 minute).
215+
- Improves performance for read-only endpoints and reduces database load.
216+
- **Sensitive/Dynamic Endpoints:**
217+
- Uses `Cache-Control: no-store` to prevent caching of user actions and sensitive data.
218+
219+
## Future Features
220+
221+
- Centralized Error Handling (**Status**: Planned)
222+
223+
Will add a middleware layer to catch errors, log them, and provide consistent user-friendly responses. Implementation is postponed until the app grows in complexity.
224+
225+
- Modulized global layer (**Status**: Planned)
226+
227+
This was planned out at the begginning with `tower` crate 's ServiceBuilder as a global layer which is then called into server.rs.
228+
229+
However, refactoring this seperated out from server.rs run is more troublesome than expected.
230+
231+
Until the project expands further, it is placed directly in server.rs.
43232

44233
## Reference
45234

46235
- Book [FullStack Rust with Axum from Martin Fabio](https://www.amazon.com/FullStack-Rust-Axum-Server-Rendered-High-Performance-ebook/dp/B0FM6XF8YX)
47236
- [Leptos resources](https://leptos.dev/)
48237
- [awesome-leptos repo](https://github.com/leptos-rs/awesome-leptos)
49238
- The wild internet, AIs and various other sources...
50-
51-
## Reflection
52-
53-
A personal reflection on this project can be found [on this blog post](https://williannguyen.com/posts/4).

contents/posts/0004-20250905-deploy-my-own-blog-finally.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ The reasons are:
2727

2828
Your best guess?
2929

30-
- Available solutions are really scarce.
30+
- At the time this blog is built, available solutions are really scarce. Please, take a look at the repo [awesome-leptos](https://github.com/leptos-rs/awesome-leptos) ans start counting to confirm my statement.
3131
- Material exposure is limited to AI models.
3232
- Even if there are, they might not function under the fast-paced progress of the Rust-lang and Leptos framework.
3333

0 commit comments

Comments
 (0)