pushing to edit on desktop
This commit is contained in:
+2
-1
@@ -8,10 +8,11 @@ argon2 = "0.5.3"
|
||||
chrono = { version = "0.4.42", features = ["serde"] }
|
||||
futures-util = "0.3.31"
|
||||
rand = "0.9.2"
|
||||
redis = { version = "0.25.4", features = ["tokio-comp"] }
|
||||
reqwest = { version = "0.12.23", features = ["json"] }
|
||||
rocket = { version = "0.5.1", features = ["json", "secrets"] }
|
||||
rocket_cors = "0.6.0"
|
||||
rocket_db_pools = { version = "0.2.0", features = ["sqlx_macros", "sqlx_postgres"] }
|
||||
rocket_db_pools = { version = "0.2.0", features = ["deadpool_redis", "sqlx_macros", "sqlx_postgres"] }
|
||||
rocket_dyn_templates = { version = "0.2.0", features = ["tera"] }
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
sha2 = "0.10.9"
|
||||
|
||||
@@ -6,6 +6,9 @@ port = 8000
|
||||
[default.databases.postgres_db]
|
||||
url = "postgresql://chatapp:chatapp@100.118.108.58:5432/chatapp"
|
||||
|
||||
[default.databases.redis_cache]
|
||||
url = "redis://chatapp_redis:6379"
|
||||
|
||||
[default] # run inside a docker container or pod
|
||||
address = "0.0.0.0"
|
||||
port = 8000
|
||||
|
||||
@@ -9,7 +9,7 @@ use rocket_dyn_templates::{Template, context};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{auth::session::Session, db::DbConn};
|
||||
use crate::{auth::session::Session, db::Postgres};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct SignupCredentials {
|
||||
@@ -34,7 +34,7 @@ pub async fn signup_page() -> Template {
|
||||
pub async fn signup(
|
||||
cred: Json<SignupCredentials>,
|
||||
jar: &CookieJar<'_>,
|
||||
mut db: Connection<DbConn>,
|
||||
mut db: Connection<Postgres>,
|
||||
) -> Result<Redirect, BadRequest<String>> {
|
||||
println!("phase 1 {}", cred.access_token);
|
||||
let token_id = AccessToken::validate(&cred.access_token, &mut db).await?;
|
||||
@@ -73,7 +73,7 @@ pub async fn login_page() -> Template {
|
||||
|
||||
#[post("/login", data = "<cred>")]
|
||||
pub async fn login(
|
||||
mut db: Connection<DbConn>,
|
||||
mut db: Connection<Postgres>,
|
||||
jar: &CookieJar<'_>,
|
||||
cred: Json<LoginCredentials>,
|
||||
) -> Result<Redirect, Status> {
|
||||
@@ -115,7 +115,7 @@ pub async fn invite_page(_s: Session) -> Template {
|
||||
#[post("/invite", data = "<form>")]
|
||||
pub async fn generate_invite(
|
||||
session: Session,
|
||||
mut db: Connection<DbConn>,
|
||||
mut db: Connection<Postgres>,
|
||||
form: Json<AccessTokenForm>,
|
||||
) -> Result<String, Status> {
|
||||
if form.start_date > form.expiry_date {
|
||||
@@ -148,7 +148,7 @@ pub struct AccessToken {
|
||||
impl AccessToken {
|
||||
pub async fn validate(
|
||||
token: &str,
|
||||
db: &mut Connection<DbConn>,
|
||||
db: &mut Connection<Postgres>,
|
||||
) -> Result<AccessToken, BadRequest<String>> {
|
||||
match sqlx::query!(
|
||||
"SELECT id FROM access_codes
|
||||
@@ -169,7 +169,7 @@ impl AccessToken {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn use_token(&self, db: &mut Connection<DbConn>) -> Result<(), BadRequest<String>> {
|
||||
pub async fn use_token(&self, db: &mut Connection<Postgres>) -> Result<(), BadRequest<String>> {
|
||||
sqlx::query!(
|
||||
"UPDATE access_codes SET uses = uses + 1 WHERE id = $1",
|
||||
self.id
|
||||
|
||||
@@ -10,7 +10,7 @@ use rocket_db_pools::Connection;
|
||||
use sha2::{Digest, Sha256};
|
||||
use sqlx::postgres::PgQueryResult;
|
||||
|
||||
use crate::db::DbConn;
|
||||
use crate::db::Postgres;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Session {
|
||||
@@ -30,7 +30,10 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn commit(&self, db: &mut Connection<DbConn>) -> Result<PgQueryResult, sqlx::Error> {
|
||||
pub async fn commit(
|
||||
&self,
|
||||
db: &mut Connection<Postgres>,
|
||||
) -> Result<PgQueryResult, sqlx::Error> {
|
||||
sqlx::query!(
|
||||
"INSERT INTO sessions (user_id, token) VALUES ($1, $2)",
|
||||
self.user_id as i32,
|
||||
@@ -47,7 +50,7 @@ impl<'r> FromRequest<'r> for Session {
|
||||
|
||||
async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
||||
if let Some(c) = request.cookies().get_private("session") {
|
||||
let mut pool = match request.guard::<Connection<DbConn>>().await {
|
||||
let mut pool = match request.guard::<Connection<Postgres>>().await {
|
||||
Outcome::Success(pool) => pool,
|
||||
_ => return Outcome::Error((Status::Unauthorized, ())),
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@ use rocket::{
|
||||
http::Status,
|
||||
outcome::{Outcome, try_outcome},
|
||||
request::{self, FromRequest},
|
||||
response::status::{self, BadRequest},
|
||||
serde::json::Json,
|
||||
};
|
||||
use rocket_db_pools::Connection;
|
||||
@@ -10,7 +11,7 @@ use rocket_dyn_templates::{Template, context};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use totp_rs::{Algorithm, Secret, TOTP};
|
||||
|
||||
use crate::{auth::session::Session, db::DbConn};
|
||||
use crate::{auth::session::Session, db::Postgres};
|
||||
|
||||
// Utility methods
|
||||
|
||||
@@ -36,36 +37,42 @@ pub async fn mfa_page(_session: Session) -> Template {
|
||||
|
||||
// api
|
||||
|
||||
#[post("/totp", data = "<totp>")]
|
||||
#[post("/totp", data = "<form>")]
|
||||
pub async fn confirm_totp(
|
||||
mfa: TOTPSecret,
|
||||
totp: Json<TOTPSixDigitCode>,
|
||||
mut db: Connection<DbConn>,
|
||||
) -> Status {
|
||||
if totp.code.len() == 6
|
||||
&& let Ok(code) = totp.code.parse::<usize>()
|
||||
{
|
||||
let totp = totp_gen(mfa.user_id, mfa.secret.as_bytes()).unwrap();
|
||||
|
||||
println!("input: {} {}", code, totp.generate_current().unwrap());
|
||||
|
||||
if totp.check_current(&format!("{}", mfa.user_id)).unwrap() {
|
||||
if sqlx::query!(
|
||||
"UPDATE users SET twofa_enabled = true WHERE id = $1",
|
||||
mfa.user_id as i32
|
||||
)
|
||||
.execute(&mut **db)
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
return Status::InternalServerError;
|
||||
};
|
||||
}
|
||||
form: Json<TOTPSixDigitCode>,
|
||||
mut db: Connection<Postgres>,
|
||||
) -> Result<(), status::Custom<&'static str>> {
|
||||
if form.code.len() != 6 && form.code.parse::<usize>().is_err() {
|
||||
return Err(status::Custom(Status::BadRequest, "Invalid 6-digit code"));
|
||||
}
|
||||
|
||||
println!("ok!");
|
||||
println!("valid");
|
||||
|
||||
return Status::Ok;
|
||||
let totp = totp_gen(mfa.user_id, mfa.secret.as_bytes()).unwrap();
|
||||
if !totp.check_current(&format!("{}", form.code)).unwrap() {
|
||||
return Err(status::Custom(Status::BadRequest, "Invalid 6-digit code"));
|
||||
}
|
||||
|
||||
println!("correct");
|
||||
|
||||
if sqlx::query!(
|
||||
"UPDATE users SET twofa_enabled = true WHERE id = $1",
|
||||
mfa.user_id as i32
|
||||
)
|
||||
.execute(&mut **db)
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
return Err(status::Custom(
|
||||
Status::InternalServerError,
|
||||
"unable to enable 2fa",
|
||||
));
|
||||
};
|
||||
|
||||
println!("enabled");
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
#[get("/totp.jpg")]
|
||||
@@ -101,7 +108,7 @@ impl<'r> FromRequest<'r> for TOTPSecret {
|
||||
|
||||
async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
||||
let user = try_outcome!(request.guard::<Session>().await);
|
||||
let mut pool = match request.guard::<Connection<DbConn>>().await {
|
||||
let mut pool = match request.guard::<Connection<Postgres>>().await {
|
||||
Outcome::Success(pool) => pool,
|
||||
_ => return Outcome::Error((Status::Unauthorized, ())),
|
||||
};
|
||||
@@ -141,7 +148,7 @@ impl<'r> FromRequest<'r> for TOTPSecret {
|
||||
}
|
||||
|
||||
impl TOTPSecret {
|
||||
pub async fn enable(&self, db: &mut Connection<DbConn>) -> Result<(), ()> {
|
||||
pub async fn enable(&self, db: &mut Connection<Postgres>) -> Result<(), ()> {
|
||||
match sqlx::query!(
|
||||
"UPDATE users SET twofa_enabled = true WHERE id = $1",
|
||||
self.user_id as i32,
|
||||
|
||||
+6
-2
@@ -1,5 +1,9 @@
|
||||
use rocket_db_pools::Database;
|
||||
use rocket_db_pools::{Database, deadpool_redis};
|
||||
|
||||
#[derive(Database)]
|
||||
#[database("postgres_db")]
|
||||
pub struct DbConn(sqlx::PgPool);
|
||||
pub struct Postgres(sqlx::PgPool);
|
||||
|
||||
#[derive(Database)]
|
||||
#[database("redis_cache")]
|
||||
pub struct Redis(deadpool_redis::Pool);
|
||||
|
||||
+5
-4
@@ -12,7 +12,7 @@ use rocket_dyn_templates::Template;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::auth::Session;
|
||||
use crate::db::DbConn;
|
||||
use crate::db::{Postgres, Redis};
|
||||
use crate::messages::ChatBroadcaster;
|
||||
|
||||
pub mod auth;
|
||||
@@ -23,7 +23,7 @@ pub mod llm;
|
||||
pub mod messages;
|
||||
|
||||
#[get("/users", rank = 2)]
|
||||
async fn users(_ag: Session, mut db: Connection<DbConn>) -> Json<Vec<i32>> {
|
||||
async fn users(_ag: Session, mut db: Connection<Postgres>) -> Json<Vec<i32>> {
|
||||
sqlx::query!("SELECT id FROM users")
|
||||
.fetch_all(&mut **db)
|
||||
.await
|
||||
@@ -35,7 +35,7 @@ async fn users(_ag: Session, mut db: Connection<DbConn>) -> Json<Vec<i32>> {
|
||||
}
|
||||
|
||||
#[get("/users/<id>", rank = 1)]
|
||||
async fn display_name(id: usize, _ag: Session, mut db: Connection<DbConn>) -> String {
|
||||
async fn display_name(id: usize, _ag: Session, mut db: Connection<Postgres>) -> String {
|
||||
sqlx::query!(
|
||||
"SELECT display_name, username FROM users WHERE id = $1",
|
||||
id as i32
|
||||
@@ -63,7 +63,8 @@ fn rocket() -> Rocket<Build> {
|
||||
rocket::build()
|
||||
.manage(chat)
|
||||
.attach(cors.to_cors().unwrap())
|
||||
.attach(DbConn::init())
|
||||
.attach(Postgres::init())
|
||||
.attach(Redis::init())
|
||||
.attach(Template::fairing())
|
||||
.mount("/static", FileServer::from("static"))
|
||||
.mount("/cdn", cdn::routes())
|
||||
|
||||
+31
-4
@@ -1,5 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use redis::cmd;
|
||||
use rocket::{
|
||||
Shutdown,
|
||||
response::stream::{Event, EventStream},
|
||||
@@ -12,7 +13,11 @@ use serde::{Deserialize, Serialize};
|
||||
use sqlx::prelude::FromRow;
|
||||
use tokio::{select, sync::broadcast};
|
||||
|
||||
use crate::{auth::Session, db::DbConn, llm::LlmWorker};
|
||||
use crate::{
|
||||
auth::Session,
|
||||
db::{Postgres, Redis},
|
||||
llm::LlmWorker,
|
||||
};
|
||||
|
||||
/// ---------- shared broadcaster ----------
|
||||
pub struct ChatBroadcaster {
|
||||
@@ -47,7 +52,7 @@ pub struct ChatMsg {
|
||||
pub async fn post_message(
|
||||
mut msg: Json<ChatMsg>,
|
||||
chat: &rocket::State<Arc<ChatBroadcaster>>,
|
||||
mut db: Connection<DbConn>,
|
||||
mut db: Connection<Postgres>,
|
||||
session: Session,
|
||||
) -> Result<(), String> {
|
||||
const CHANNEL_ID: i32 = 1;
|
||||
@@ -104,7 +109,7 @@ pub async fn post_message(
|
||||
}
|
||||
|
||||
#[get("/messages")]
|
||||
pub async fn get_messages(mut db: Connection<DbConn>, _session: Session) -> Json<Vec<ChatMsg>> {
|
||||
pub async fn get_messages(mut db: Connection<Postgres>, _session: Session) -> Json<Vec<ChatMsg>> {
|
||||
Json(
|
||||
sqlx::query!(
|
||||
"SELECT u.username, u.display_name, u.id, m.content, m.created_at FROM messages m JOIN users u ON m.user_id = u.id ORDER BY m.created_at DESC LIMIT 100"
|
||||
@@ -128,7 +133,7 @@ pub async fn get_messages(mut db: Connection<DbConn>, _session: Session) -> Json
|
||||
#[get("/events")]
|
||||
pub async fn event_stream(
|
||||
chat: &rocket::State<Arc<ChatBroadcaster>>,
|
||||
db: Connection<DbConn>,
|
||||
db: Connection<Postgres>,
|
||||
ag: Session,
|
||||
mut shutdown: Shutdown,
|
||||
) -> EventStream![] {
|
||||
@@ -166,3 +171,25 @@ pub async fn chat_page(session: Session) -> Template {
|
||||
pub async fn chat_page_preview(session: Session) -> Template {
|
||||
Template::render("chatpreview", context!(user_id: session.user_id))
|
||||
}
|
||||
|
||||
pub struct UserCache {}
|
||||
|
||||
impl UserCache {
|
||||
pub async fn username(&mut self, id: usize, redis_conn: &mut Connection<Redis>) -> String {
|
||||
if let Ok(val) = cmd("GET")
|
||||
.arg(&[format!("users:{id}")])
|
||||
.query_async(&mut **redis_conn)
|
||||
.await
|
||||
{
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn insert(id: usize, username: &str, conn: &mut Connection<Redis>) {
|
||||
cmd("SET")
|
||||
.arg(&[format!("users:{id}"), username.to_owned()])
|
||||
.query_async(&mut **conn)
|
||||
.await
|
||||
.expect("failed to insert key")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user