create admin user, partially rebrand to login template
This commit is contained in:
parent
a97ee0586f
commit
59d0ed12c1
|
@ -0,0 +1,8 @@
|
||||||
|
POSTGRES_DB=fossil_postgres
|
||||||
|
POSTGRES_PASSWORD=password
|
||||||
|
|
||||||
|
ROCKET_SECRET_KEY=openssl rand -base64 32
|
||||||
|
|
||||||
|
ADMIN_USERNAME=admin
|
||||||
|
ADMIN_PASSWORD=password
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
/target
|
/target
|
||||||
docker-compose.yml
|
|
||||||
db/password.txt
|
db/password.txt
|
||||||
|
.env
|
||||||
|
|
10
README.md
10
README.md
|
@ -1,9 +1,7 @@
|
||||||
# fossil
|
# login template
|
||||||
|
|
||||||
Free and Open Source Software - Library
|
this is pretty much just some shitty code that'll handle logging into and out of a website, with the default features being a blog, but it should be easy to change to something else
|
||||||
|
|
||||||
yeah yeah there's no i in the name, i just wanted the acronym to be an actual word
|
setup: remove .example from `.env.example`
|
||||||
|
\nfill it with the correct values (for `ROCKET_SECRET_KEY`, run that command to generate it)
|
||||||
setup: remove .example from `docker-compose.yml.example` and `db/password.txt.example`
|
|
||||||
\nfill out those files with the correct values
|
|
||||||
\nrun `docker compose build` to build it, `docker compose run` to run, and `docker compose run -d` to run it in the background
|
\nrun `docker compose build` to build it, `docker compose run` to run, and `docker compose run -d` to run it in the background
|
||||||
|
|
|
@ -15,14 +15,16 @@ services:
|
||||||
ports:
|
ports:
|
||||||
- 8000:8000
|
- 8000:8000
|
||||||
environment:
|
environment:
|
||||||
# name in main.rs db.user POSTGRES_PASSWORD POSTGRES_DB
|
- ROCKET_DATABASES={fossil_postgres={url="postgres://postgres:$POSTGRES_PASSWORD@db/$POSTGRES_DB", max_connections=10, connect_timeout=10}}
|
||||||
- ROCKET_DATABASES={diesel_postgres={url="postgres://postgres:passwordpasswordpassword@db/example"}}
|
|
||||||
|
|
||||||
- ROCKET_ADDRESS=0.0.0.0
|
- ROCKET_ADDRESS=0.0.0.0
|
||||||
- ROCKET_PORT=8000
|
- ROCKET_PORT=$PORT
|
||||||
- RUST_LOG=debug
|
- RUST_LOG=debug
|
||||||
|
|
||||||
- ROCKET_SECRET_KEY=openssl rand -base64 32
|
- ROCKET_SECRET_KEY=$ROCKET_SECRET_KEY
|
||||||
|
|
||||||
|
- ADMIN_USERNAME=$ADMIN_USERNAME
|
||||||
|
- ADMIN_PASSWORD=$ADMIN_PASSWORD
|
||||||
volumes:
|
volumes:
|
||||||
- type: bind
|
- type: bind
|
||||||
source: ./web
|
source: ./web
|
||||||
|
@ -40,13 +42,11 @@ services:
|
||||||
image: postgres
|
image: postgres
|
||||||
restart: always
|
restart: always
|
||||||
user: postgres
|
user: postgres
|
||||||
secrets:
|
|
||||||
- db-password
|
|
||||||
volumes:
|
volumes:
|
||||||
- db-data:/var/lib/postgresql/data
|
- db-data:/var/lib/postgresql/data
|
||||||
environment:
|
environment:
|
||||||
- POSTGRES_DB=example
|
- POSTGRES_DB=$POSTGRES_DB
|
||||||
- POSTGRES_PASSWORD=passwordpasswordpassword
|
- POSTGRES_PASSWORD=$POSTGRES_PASSWORD
|
||||||
expose:
|
expose:
|
||||||
- 5432
|
- 5432
|
||||||
healthcheck:
|
healthcheck:
|
||||||
|
@ -56,6 +56,3 @@ services:
|
||||||
retries: 5
|
retries: 5
|
||||||
volumes:
|
volumes:
|
||||||
db-data:
|
db-data:
|
||||||
secrets:
|
|
||||||
db-password:
|
|
||||||
file: db/password.txt
|
|
80
src/main.rs
80
src/main.rs
|
@ -69,34 +69,18 @@ async fn createuser(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/getuser/<username>")]
|
|
||||||
async fn getuser(db: Connection<Db>, username: String) -> String {
|
|
||||||
let user = User::get_by_username(db, &username).await;
|
|
||||||
match user {
|
|
||||||
Some(user) => format!(
|
|
||||||
"id: {}\nusername: {}\nhashed password: {}\ntoken: {}",
|
|
||||||
user.id,
|
|
||||||
user.username,
|
|
||||||
user.password,
|
|
||||||
match user.token {
|
|
||||||
Some(t) => t,
|
|
||||||
None => "no token".to_string()
|
|
||||||
}
|
|
||||||
),
|
|
||||||
None => format!("User {} doesn't exist.", &username),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/account")]
|
#[get("/account")]
|
||||||
async fn account(db: Connection<Db>, cookies: &CookieJar<'_>) -> String {
|
async fn account(db: Connection<Db>, cookies: &CookieJar<'_>) -> String {
|
||||||
let token = cookies.get_private("token");
|
let token = cookies.get_private("token");
|
||||||
match token {
|
match token {
|
||||||
Some(t) => {
|
Some(t) => {
|
||||||
let user = User::get_by_token(db, t.to_string().split("=").collect::<Vec<&str>>()[1].to_string() /*GOD I LOVE RUST*/).await;
|
match User::get_by_token(db, t.to_string().split('=').collect::<Vec<&str>>()[1].to_string() /*GOD I LOVE RUST*/).await {
|
||||||
format!("Username: {}", user.username)
|
Some(user) => format!("Username: {}", user.username),
|
||||||
|
None => "User doesn't exist.".to_string()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
format!("Not logged in")
|
"Not logged in".to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,12 +89,8 @@ async fn account(db: Connection<Db>, cookies: &CookieJar<'_>) -> String {
|
||||||
async fn login(db: Connection<Db>, db2: Connection<Db>, info: Json<LoginInfo>, cookies: &CookieJar<'_>) -> String {
|
async fn login(db: Connection<Db>, db2: Connection<Db>, info: Json<LoginInfo>, cookies: &CookieJar<'_>) -> String {
|
||||||
let token = cookies.get_private("token");
|
let token = cookies.get_private("token");
|
||||||
match token {
|
match token {
|
||||||
Some(t) => {
|
Some(_) => {
|
||||||
if User::get_by_token(db, t.to_string()).await.exists() /*god i fucking love rust, this function literally just returns true*/ {
|
"already logged in".to_string()
|
||||||
"logged in with token".to_string()
|
|
||||||
} else {
|
|
||||||
"unknown token".to_string()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
match User::get_by_username(db, &info.username).await {
|
match User::get_by_username(db, &info.username).await {
|
||||||
|
@ -140,12 +120,12 @@ async fn login(db: Connection<Db>, db2: Connection<Db>, info: Json<LoginInfo>, c
|
||||||
}
|
}
|
||||||
#[post("/logout")]
|
#[post("/logout")]
|
||||||
async fn logout(cookies: &CookieJar<'_>) -> &'static str {
|
async fn logout(cookies: &CookieJar<'_>) -> &'static str {
|
||||||
let token = cookies.get_private("token");
|
match cookies.get_private("token") {
|
||||||
if token.is_some() {
|
Some(_) => {
|
||||||
cookies.remove_private("token");
|
cookies.remove_private("token");
|
||||||
"logged out"
|
"Logged out."
|
||||||
} else {
|
},
|
||||||
"not logged in"
|
None => "Not logged in."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,7 +172,7 @@ async fn migrate(rocket: Rocket<Build>) -> Rocket<Build> {
|
||||||
)",
|
)",
|
||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
let _2 = conn
|
let _ = conn
|
||||||
.fetch_one(sqlx::query(
|
.fetch_one(sqlx::query(
|
||||||
"CREATE TABLE IF NOT EXISTS users (
|
"CREATE TABLE IF NOT EXISTS users (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
|
@ -203,6 +183,36 @@ async fn migrate(rocket: Rocket<Build>) -> Rocket<Build> {
|
||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
match conn.fetch_one(sqlx::query("SELECT * from users WHERE username = $1").bind(std::env::var("ADMIN_USERNAME").expect("make ADMIN_USERNAME env var"))).await {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(_) => {
|
||||||
|
// yes, User::create() exists. no, conn isn't the right type. no, i won't even attempt
|
||||||
|
// to fix it. yes, this works just as well as that. fuck you.
|
||||||
|
let username = std::env::var("ADMIN_USERNAME").expect("make ADMIN_USERNAME env var");
|
||||||
|
let password = sha256::digest(std::env::var("ADMIN_PASSWORD").expect("make ADMIN_PASSWORD env var"));
|
||||||
|
|
||||||
|
eprintln!("{username}\n{password}");
|
||||||
|
|
||||||
|
conn.fetch_one(sqlx::query(
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
r#"
|
||||||
|
INSERT INTO users (username, password)
|
||||||
|
VALUES ($1, $2);
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(username)
|
||||||
|
.bind(password)
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.expect("couldn't create admin user");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rocket
|
rocket
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +224,7 @@ async fn main() {
|
||||||
.attach(AdHoc::on_ignite("DB Migrations", migrate))
|
.attach(AdHoc::on_ignite("DB Migrations", migrate))
|
||||||
.mount(
|
.mount(
|
||||||
"/",
|
"/",
|
||||||
routes![hello, get_book, delay, login, logout, dbtest, dbcreate, createuser, getuser, account],
|
routes![hello, get_book, delay, login, logout, dbtest, dbcreate, createuser, account],
|
||||||
)
|
)
|
||||||
.register("/", catchers![default_catcher])
|
.register("/", catchers![default_catcher])
|
||||||
.mount("/login", FileServer::from("/srv/web"))
|
.mount("/login", FileServer::from("/srv/web"))
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use rand::{SeedableRng, RngCore};
|
use rand::{RngCore, SeedableRng};
|
||||||
use rocket_db_pools::sqlx::Executor;
|
use rocket_db_pools::sqlx::Executor;
|
||||||
use rocket_db_pools::Connection;
|
use rocket_db_pools::Connection;
|
||||||
use sqlx::FromRow;
|
use sqlx::FromRow;
|
||||||
|
@ -82,42 +82,44 @@ impl User {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn get_by_id(mut db: Connection<Db>, id: i32) -> User {
|
pub async fn get_by_id(mut db: Connection<Db>, id: i32) -> Option<User> {
|
||||||
let res = db
|
match db
|
||||||
.fetch_one(sqlx::query("SELECT * FROM users WHERE id = $1;").bind(id))
|
.fetch_one(sqlx::query("SELECT * FROM users WHERE id = $1;").bind(id))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
{
|
||||||
User {
|
Ok(user) => Some(User {
|
||||||
id: res.get::<i32, _>("id"),
|
id: user.get::<i32, _>("id"),
|
||||||
username: res.get::<String, _>("username"),
|
username: user.get::<String, _>("username"),
|
||||||
password: res.get::<String, _>("password"),
|
password: user.get::<String, _>("password"),
|
||||||
token: res.get::<Option<String>, _>("token"),
|
token: user.get::<Option<String>, _>("token"),
|
||||||
|
}),
|
||||||
|
Err(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn get_by_token(mut db: Connection<Db>, token: String) -> User {
|
pub async fn get_by_token(mut db: Connection<Db>, token: String) -> Option<User> {
|
||||||
let res = db
|
match db
|
||||||
.fetch_one(sqlx::query("SELECT * FROM users WHERE token = $1;").bind(token))
|
.fetch_one(sqlx::query("SELECT * FROM users WHERE token = $1;").bind(token))
|
||||||
.await
|
.await
|
||||||
// TODO: this errors sometimes i dont know why
|
{
|
||||||
.unwrap();
|
Ok(user) => Some(User {
|
||||||
User {
|
id: user.get::<i32, _>("id"),
|
||||||
id: res.get::<i32, _>("id"),
|
username: user.get::<String, _>("username"),
|
||||||
username: res.get::<String, _>("username"),
|
password: user.get::<String, _>("password"),
|
||||||
password: res.get::<String, _>("password"),
|
token: user.get::<Option<String>, _>("token"),
|
||||||
token: res.get::<Option<String>, _>("token"),
|
}),
|
||||||
|
Err(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn get_by_username(mut db: Connection<Db>, username: &String) -> Option<User> {
|
pub async fn get_by_username(mut db: Connection<Db>, username: &String) -> Option<User> {
|
||||||
let res = db
|
match db
|
||||||
.fetch_one(sqlx::query("SELECT * FROM users WHERE username = $1;").bind(username))
|
.fetch_one(sqlx::query("SELECT * FROM users WHERE username = $1;").bind(username))
|
||||||
.await;
|
.await
|
||||||
|
{
|
||||||
match res {
|
Ok(user) => Some(User {
|
||||||
Ok(res) => Some(User {
|
id: user.get::<i32, _>("id"),
|
||||||
id: res.get::<i32, _>("id"),
|
username: user.get::<String, _>("username"),
|
||||||
username: res.get::<String, _>("username"),
|
password: user.get::<String, _>("password"),
|
||||||
password: res.get::<String, _>("password"),
|
token: user.get::<Option<String>, _>("token"),
|
||||||
token: res.get::<Option<String>, _>("token"),
|
|
||||||
}),
|
}),
|
||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
}
|
}
|
||||||
|
@ -128,9 +130,16 @@ impl User {
|
||||||
|
|
||||||
let token = sha256::digest(format!("{token_start}-{token_end}"));
|
let token = sha256::digest(format!("{token_start}-{token_end}"));
|
||||||
|
|
||||||
match db.fetch_one(sqlx::query("UPDATE users SET token = $1 WHERE id = $2").bind(&token).bind(self.id)).await {
|
match db
|
||||||
|
.fetch_one(
|
||||||
|
sqlx::query("UPDATE users SET token = $1 WHERE id = $2")
|
||||||
|
.bind(&token)
|
||||||
|
.bind(self.id),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
Ok(_) => Ok(token),
|
Ok(_) => Ok(token),
|
||||||
Err(why) => Err(why.to_string())
|
Err(why) => Err(why.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue