Skip to content

Commit fbfdd30

Browse files
committed
feat: add PostgreSQL backend support
Dispatch the model layer between sqlx-mysql and sqlx-postgres based on the connection URL scheme, and split schema DDL into per-backend files (mysql_schema.rs / postgres_schema.rs). Config gains a `[database]` section with a `backend = "mysql" | "postgres"` selector for the split -field path; `[mysql]` is still accepted as a compatibility fallback. Rename the preferred environment variable from MYSQL_CONNECTION_URL to DATABASE_URL across the Fly.io workflow and docker-compose, while still accepting the legacy name at the config layer so existing deployments keep working. Signed-off-by: JmPotato <github@ipotato.me>
1 parent 5f3e6c8 commit fbfdd30

File tree

17 files changed

+2208
-729
lines changed

17 files changed

+2208
-729
lines changed

.github/workflows/fly-deploy.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ jobs:
2121
curl -L https://fly.io/install.sh | sh
2222
echo "$HOME/.fly/bin" >> "$GITHUB_PATH"
2323
- name: Set secrets
24-
run: fly secrets set --stage MYSQL_CONNECTION_URL="${MYSQL_CONNECTION_URL}" UMAMI_ID="${UMAMI_ID}"
24+
run: fly secrets set --stage DATABASE_URL="${DATABASE_URL}" UMAMI_ID="${UMAMI_ID}"
2525
env:
2626
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
27-
MYSQL_CONNECTION_URL: ${{ secrets.MYSQL_CONNECTION_URL }}
27+
DATABASE_URL: ${{ secrets.DATABASE_URL }}
2828
UMAMI_ID: ${{ secrets.UMAMI_ID }}
2929
- name: Deploy
3030
run: fly deploy --remote-only

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@ target/
2121
#.idea/
2222

2323
# Others
24-
.DS_Store
24+
.DS_Store
25+
.env*

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ serde = { version = "1.0.210", features = ["derive"] }
1818
sqlx = { version = "0.8.2", features = [
1919
"runtime-tokio",
2020
"mysql",
21+
"postgres",
2122
"chrono",
2223
"tls-rustls",
2324
] }

README.md

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,29 +19,49 @@ rsomhaP is still a simple ready-to-use blog engine inheriting a lot from its pre
1919
But also introduces some new features:
2020

2121
- More secure admin authentication.
22-
- Use any MySQL-compatible database to store your blog data.
22+
- Use either MySQL or PostgreSQL to store your blog data.
2323
- Easy to deploy with a single command or a simple [Dockerfile](https://github.com/JmPotato/rsomhaP/blob/main/Dockerfile).
2424

2525
## Deployment
2626

2727
Edit your [`config.toml`](https://github.com/JmPotato/rsomhaP/blob/main/config.toml) to your liking, then run:
2828

29+
When you use the split fields under `[database]`, set `backend = "mysql"` or `backend = "postgres"`.
30+
If you provide `connection_url` in the config or `DATABASE_URL` in the environment, that full URL wins and its scheme still selects the driver:
31+
32+
- `mysql://...` for MySQL
33+
- `postgres://...` / `postgresql://...` for PostgreSQL
34+
35+
Upgrade note:
36+
37+
- `DATABASE_URL` is the preferred environment variable.
38+
- Legacy `MYSQL_CONNECTION_URL` is still accepted as a compatibility fallback.
39+
- Legacy `[mysql]` config still parses, but `[database]` is the preferred section name going forward.
40+
- The default sample config binds to `0.0.0.0` so Docker and public deployments work out of the box. If you want local-only access, change `[deploy].host` to `127.0.0.1`.
41+
2942
```sh
3043
cargo run --release
3144
```
3245

33-
Or build a Docker image and run it:
46+
Or build a Docker image and run it. The container still needs a reachable
47+
database; the simplest path is to pass `DATABASE_URL` explicitly:
3448

3549
```sh
3650
docker build -t rsomhap .
37-
docker run -p 5299:5299 rsomhap
51+
docker run -p 5299:5299 \
52+
-e DATABASE_URL='postgres://user:pass@host:5432/dbname' \
53+
rsomhap
3854
```
3955

40-
Access the admin page at "http://{your-deployment-url}/admin" to manage your blog. The initial password is the same as the username configured in [`config.toml`](https://github.com/JmPotato/rsomhaP/blob/62dd746dfd6f7413d161a1fde79b82a0589b241b/config.toml#L14), **which you should change after the first login as soon as possible.**.
56+
If you prefer the split fields in `config.toml`, make sure the container can
57+
actually reach that host. The default `127.0.0.1:4000` example works for a
58+
local host process, but inside Docker `127.0.0.1` means the container itself.
59+
60+
Access the admin page at "http://{your-deployment-url}/admin" to manage your blog. The initial password is the same as the username configured in [`config.toml`](./config.toml), **which you should change after the first login as soon as possible.**.
4161

4262
Technically, you can deploy rsomhaP with modern SaaS infrastructures entirely free from scratch. For example:
4363

44-
- Use [TiDB Serverless](https://www.pingcap.com/tidb-serverless) as the MySQL-compatible database.
64+
- Use [TiDB Serverless](https://www.pingcap.com/tidb-serverless) for a MySQL deployment, or any managed PostgreSQL service for a Postgres deployment.
4565
- Use [fly.io](https://fly.io) as the hosting service.
4666
- Use [Cloudflare R2](https://www.cloudflare.com/developer-platform/r2) as the image hosting service.
4767
- Use [WebP Cloud Services](https://webp.se) as the image proxy service.

config.toml

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,21 @@ article_per_page = 15
2424
# - `Solarized (dark)` and `Solarized (light)`
2525
code_syntax_highlight_theme = "base16-eighties.dark"
2626

27-
[mysql]
28-
# If `connection_url` is set, other connection-related configs will be ignored.
27+
[database]
28+
# Which backend to use when building a connection URL from the split fields
29+
# below. Accepted values: `mysql`, `postgres`.
30+
backend = "mysql"
31+
#
32+
# If `connection_url` is set, it is used as-is and its own scheme still
33+
# selects the actual driver:
34+
# - mysql://user:pass@host:port/db
35+
# - postgres://user:pass@host:port/db (or postgresql://)
36+
# Examples:
2937
# connection_url = "mysql://root:password@127.0.0.1:4000/rsomhaP"
38+
# connection_url = "postgres://postgres:password@127.0.0.1:5432/rsomhaP"
39+
#
40+
# If you use the split fields below instead of `connection_url`, `backend`
41+
# controls whether `mysql://` or `postgres://` is generated.
3042
username = "root"
3143
password = "password"
3244
host = "127.0.0.1"

docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ services:
77
ports:
88
- "5299:5299"
99
environment:
10-
- MYSQL_CONNECTION_URL
10+
- DATABASE_URL
1111
- UMAMI_ID
1212
healthcheck:
1313
test: ["CMD-SHELL", "curl -sf http://localhost:5299/ping || exit 1"]

src/app.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use crate::{
2929
handler_edit_page_get, handler_edit_post, handler_feed, handler_home, handler_login_get,
3030
handler_login_post, handler_logout, handler_page, handler_ping, handler_tag, handler_tags,
3131
},
32-
models::{create_tables_within_transaction, Article, Page, User},
32+
models::{init_schema, Article, DbPool, Page, User},
3333
};
3434

3535
const TEMPLATES_DIR: &str = "templates";
@@ -42,7 +42,7 @@ const CONFIG_FILE_PATH: &str = "config.toml";
4242
pub struct AppState {
4343
pub config: Config,
4444
pub env: Environment<'static>,
45-
pub db: sqlx::MySqlPool,
45+
pub db: DbPool,
4646
// Cache the feed content to reduce the database query.
4747
pub feed_cache: Arc<RwLock<(DateTime<Utc>, Option<String>)>>,
4848
// Cache the page titles to avoid querying the database on every template render.
@@ -55,11 +55,12 @@ impl AppState {
5555
let config = Config::new(CONFIG_FILE_PATH)?;
5656

5757
info!("connecting to the database");
58-
// connect to the database.
59-
let db = sqlx::MySqlPool::connect(&config.mysql_connection_url()?).await?;
58+
// connect to the database. The URL scheme selects the backend
59+
// (`mysql://` or `postgres://`).
60+
let db = DbPool::connect(&config.database_url()?).await?;
6061
info!("initializing the database");
6162
// create the tables if they don't exist.
62-
create_tables_within_transaction(&db).await?;
63+
init_schema(&db).await?;
6364
// init the admin user.
6465
let admin_username = config.admin_username();
6566
User::insert(

0 commit comments

Comments
 (0)