+26
-10
README.md
+26
-10
README.md
···
1
1
# SimpleLink
2
2
3
-
A very performant and light (2MB in memory) link shortener and tracker. Written in Rust and React and uses Postgres.
3
+
A very performant and light (2MB in memory) link shortener and tracker. Written in Rust and React and uses Postgres or SQLite.
4
4
5
5

6
6
···
10
10
11
11
### From Docker:
12
12
13
-
```Bash
13
+
```bash
14
14
docker run -p 8080:8080 \
15
15
-e JWT_SECRET=change-me-in-production \
16
+
-e SIMPLELINK_USER=admin@example.com \
17
+
-e SIMPLELINK_PASS=your-secure-password \
16
18
-v simplelink_data:/data \
17
19
ghcr.io/waveringana/simplelink:v2
18
20
```
19
21
20
-
Find the admin-setup-token pasted into the terminal output, or in admin-setup-token.txt in the container's root.
22
+
### Environment Variables
21
23
22
-
This is needed to register with the frontend. (TODO, register admin account with ENV)
24
+
- `JWT_SECRET`: Required. Used for JWT token generation
25
+
- `SIMPLELINK_USER`: Optional. If set along with SIMPLELINK_PASS, creates an admin user on first run
26
+
- `SIMPLELINK_PASS`: Optional. Admin user password
27
+
- `DATABASE_URL`: Optional. Postgres connection string. If not set, uses SQLite
28
+
- `INITIAL_LINKS`: Optional. Semicolon-separated list of initial links in format "url,code;url2,code2"
29
+
- `SERVER_HOST`: Optional. Default: "127.0.0.1"
30
+
- `SERVER_PORT`: Optional. Default: "8080"
31
+
32
+
If `SIMPLELINK_USER` and `SIMPLELINK_PASS` are not passed, an admin-setup-token is pasted to the console and as a text file in the project root.
23
33
24
34
### From Docker Compose:
25
35
26
-
Edit the docker-compose.yml file. It comes included with a postgressql db for use
36
+
Edit the docker-compose.yml file. It comes included with a PostgreSQL db configuration.
27
37
28
38
## Build
29
39
···
31
41
32
42
First configure .env.example and save it to .env
33
43
34
-
If DATABASE_URL is set, it will connect to a Postgres DB. If blank, it will use an sqlite db in /data
35
-
36
44
```bash
37
45
git clone https://github.com/waveringana/simplelink && cd simplelink
38
46
./build.sh
39
47
cargo run
40
48
```
41
49
42
-
On an empty database, an admin-setup-token.txt is created as well as pasted into the terminal output. This is needed to make the admin account.
43
-
44
-
Alternatively if you want a binary form
50
+
Alternatively for a binary build:
45
51
46
52
```bash
47
53
./build.sh --binary
···
55
61
docker build -t simplelink .
56
62
docker run -p 8080:8080 \
57
63
-e JWT_SECRET=change-me-in-production \
64
+
-e SIMPLELINK_USER=admin@example.com \
65
+
-e SIMPLELINK_PASS=your-secure-password \
58
66
-v simplelink_data:/data \
59
67
simplelink
60
68
```
···
62
70
### From Docker Compose
63
71
64
72
Adjust the included docker-compose.yml to your liking; it includes a postgres config as well.
73
+
74
+
## Features
75
+
76
+
- Support for both PostgreSQL and SQLite databases
77
+
- Initial links can be configured via environment variables
78
+
- Admin user can be created on first run via environment variables
79
+
- Link click tracking and statistics
80
+
- Lightweight and performant
+56
-1
src/main.rs
+56
-1
src/main.rs
···
5
5
use simplelink::check_and_generate_admin_token;
6
6
use simplelink::{create_db_pool, run_migrations};
7
7
use simplelink::{handlers, AppState};
8
-
use tracing::info;
8
+
use sqlx::{Postgres, Sqlite};
9
+
use tracing::{error, info};
9
10
11
+
#[derive(Parser, Debug)]
12
+
#[command(author, version, about, long_about = None)]
10
13
#[derive(RustEmbed)]
11
14
#[folder = "static/"]
12
15
struct Asset;
···
34
37
// Create database connection pool
35
38
let pool = create_db_pool().await?;
36
39
run_migrations(&pool).await?;
40
+
41
+
// First check if admin credentials are provided in environment variables
42
+
let admin_credentials = match (
43
+
std::env::var("SIMPLELINK_USER"),
44
+
std::env::var("SIMPLELINK_PASS"),
45
+
) {
46
+
(Ok(user), Ok(pass)) => Some((user, pass)),
47
+
_ => None,
48
+
};
49
+
50
+
if let Some((email, password)) = admin_credentials {
51
+
// Now check for existing users
52
+
let user_count = match &pool {
53
+
DatabasePool::Postgres(pool) => {
54
+
let mut tx = pool.begin().await?;
55
+
let count =
56
+
sqlx::query_as::<Postgres, (i64,)>("SELECT COUNT(*)::bigint FROM users")
57
+
.fetch_one(&mut *tx)
58
+
.await?
59
+
.0;
60
+
tx.commit().await?;
61
+
count
62
+
}
63
+
DatabasePool::Sqlite(pool) => {
64
+
let mut tx = pool.begin().await?;
65
+
let count = sqlx::query_as::<Sqlite, (i64,)>("SELECT COUNT(*) FROM users")
66
+
.fetch_one(&mut *tx)
67
+
.await?
68
+
.0;
69
+
tx.commit().await?;
70
+
count
71
+
}
72
+
};
73
+
74
+
if user_count == 0 {
75
+
info!("No users found, creating admin user: {}", email);
76
+
match create_admin_user(&pool, &email, &password).await {
77
+
Ok(_) => info!("Successfully created admin user"),
78
+
Err(e) => {
79
+
error!("Failed to create admin user: {}", e);
80
+
return Err(anyhow::anyhow!("Failed to create admin user: {}", e));
81
+
}
82
+
}
83
+
}
84
+
} else {
85
+
info!(
86
+
"No admin credentials provided in environment variables, skipping admin user creation"
87
+
);
88
+
}
89
+
90
+
// Create initial links from environment variables
91
+
create_initial_links(&pool).await?;
37
92
38
93
let admin_token = check_and_generate_admin_token(&pool).await?;
39
94