### Description
There are several environment variables should be set:
- Frontend
- `PUBLIC_SENTRY_DSN`
- `SENTRY_AUTH_TOKEN`
- Backend
- `SENTRY_DSN`
If the dsn isn't set, errors won't be sent to Sentry.
### Package Changes
_No response_
### Screenshots

### Reference
Resolves#90
### Checklist
- [x] A milestone is set
- [x] The related issuse has been linked to this branch
Reviewed-on: #120
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
### Description
This PR integrates the **`utoipa`** and **`utoipa-redoc`** crates to automatically generate OpenAPI-compliant API documentation for the backend project.
#### Overview
To improve development efficiency and API maintainability, this change introduces `utoipa` to automate the API documentation process. By adding specific attribute macros to the source code, we can generate detailed API specifications directly and serve them through an interactive UI provided by `utoipa-redoc`.
#### Key Changes
* **Dependencies Added**
* Added `utoipa`, `utoipa-gen`, and `utoipa-redoc` to `Cargo.toml`.
* `utoipa` is used to define OpenAPI objects.
* `utoipa-redoc` is used to serve the ReDoc documentation UI.
* **Code Refactoring**
* **HTTP handler logic** in each feature (`auth`, `image`, `post`) has been extracted from the `..._web_routes.rs` files into their own dedicated files (e.g., `get_post_by_id_handler.rs`). This makes the code structure cleaner and simplifies adding documentation attributes to each handler.
* Renamed the `PostController` method from `get_full_post` to `get_post_by_id` for a more RESTful-compliant naming convention.
* **API Doc Annotation**
* Added `#[derive(ToSchema)]` or `#[derive(IntoParams)]` to all DTOs (Data Transfer Objects) so they can be recognized by `utoipa` to generate the corresponding schemas.
* Added the `#[utoipa::path]` macro to all HTTP handler functions, describing the API's path, HTTP method, tags, summary, expected responses, and security settings.
* **Doc Aggregation & Serving**
* Added an `..._api_doc.rs` file in each feature module to aggregate all API paths within that module.
* Added a new `api_doc.rs` file in the `server` crate to merge the OpenAPI documents from all features, set global information (like title, version, and the OAuth2 security scheme), and serve the documentation page on the `/redoc` route using `Redoc::with_url`.
### Package Changes
```toml
utoipa = { version = "5.4.0", features = ["actix_extras"] }
utoipa-redoc = { version = "6.0.0", features = ["actix-web"] }
```
### Screenshots

### Reference
Resolves#103
### Checklist
- [x] A milestone is set
- [x] The related issuse has been linked to this branch
Reviewed-on: #106
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
### Description
- Login with configured OIDC issuer, and then save the logged in information in server session.
- Endpoints:
- GET `/auth/login`
- GET `/auth/callback`
- GET `/auth/logout`
### Package Changes
```toml
actix-session = { version = "0.10.1", features = ["redis-session"] }
hex = "0.4.3"
openidconnect = { version = "4.0.1", features = [
"reqwest",
"reqwest-blocking",
] }
```
### Screenshots
<video src="attachments/8b15b576-61db-41b9-8587-b4b885018c93" title="Screencast From 2025-07-30 03-34-26.mp4" controls></video>
### Reference
Resolves#85
### Checklist
- [x] A milestone is set
- [x] The related issuse has been linked to this branch
Reviewed-on: #93
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
### Description
- Add some endpoints about image:
- POST `/image/upload`
- GET `/image/{id}`
> [!NOTE]
> Since there isn't identity authentication, the `/image` endpoints should be restricted to private network in nginx.
> [!NOTE]
> Volume for backend should be configured in `pod.yaml`.
### Package Changes
```toml
actix-multipart = "0.7.2"
```
### Screenshots
_No response_
### Reference
Resolves#78
### Checklist
- [x] A milestone is set
- [x] The related issuse has been linked to this branch
Reviewed-on: #84
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
### Description
- Implement the content page
- Parse markdown formant content to html by `markdown-it`
- Use `sanitize-html` to prevent from XSS attack
- Style the html with `tailwindcss-typography`
- Fix the issue when backend parse the password to url
- Fix and make the post info list from backend always sorted by id
### Package Changes
### Rust
```toml
percent-encoding = "2.3.1"
```
### Node
```json
{
"@types/markdown-it": "^14.1.2",
"@types/sanitize-html": "^2.16.0",
"markdown-it": "^14.1.0",
"sanitize-html": "^2.17.0"
}
```
### Screenshots
|Desktop|Mobile|
|-|-|
|||
### Reference
Resolves#45
### Checklist
- [x] A milestone is set
- [x] The related issuse has been linked to this branch
Reviewed-on: #67
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
### Description
- `GET` `/post_info`
Get all the info of the posts.
- `200` Without any post
```json
[]
```
- `200` With posts
```json
[
{
"description": "This is the first post.",
"id": 1,
"labels": [
{
"color": "#FF666666",
"id": 2,
"name": "Rust"
}
],
"preview_image_url": "https://squidspirit.com/icon/logo-light.svg",
"published_time": null,
"title": "The First Post"
}
]
```
- `GET` `/post/{id}`
Get the full post content with the given `id`
- `200` With result
```json
{
"content": "Hello! I'm Squid!!",
"id": 1,
"info": {
"description": "This is the first post.",
"id": 1,
"labels": [
{
"color": "#FF666666",
"id": 2,
"name": "Rust"
}
],
"preview_image_url": "https://squidspirit.com/icon/logo-light.svg",
"published_time": null,
"title": "The First Post"
}
}
```
- `404` There is no post with the `id`
### Package Changes
```toml
[workspace.package]
version = "0.1.1"
edition = "2024"
[workspace.dependencies]
actix-web = "4.10.2"
async-trait = "0.1.88"
chrono = "0.4.41"
dotenv = "0.15.0"
env_logger = "0.11.8"
futures = "0.3.31"
log = "0.4.27"
serde = { version = "1.0.219", features = ["derive"] }
sqlx = { version = "0.8.5", features = [
"chrono",
"macros",
"postgres",
"runtime-tokio-rustls",
] }
tokio = { version = "1.45.0", features = ["full"] }
```
### Screenshots
_No response_
### Reference
Resolves#43
### Checklist
- [x] A milestone is set
- [x] The related issuse has been linked to this branch
Reviewed-on: #55
Reviewed-by: zoe <zoe@noreply.localhost>
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>