switch to private cookies + make them last 1 month
This commit is contained in:
82
src/main.rs
82
src/main.rs
@@ -1,13 +1,20 @@
|
||||
use crate::model::client::Client;
|
||||
use axum::{http::HeaderMap, routing::get, Router};
|
||||
use axum_extra::extract::{cookie::Cookie, CookieJar};
|
||||
use axum_extra::extract::{
|
||||
cookie::{Cookie, Expiration, Key},
|
||||
PrivateCookieJar,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{pool::PoolOptions, sqlite::SqliteConnectOptions, SqlitePool};
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
fmt::Display,
|
||||
fs,
|
||||
path::Path,
|
||||
str::FromStr,
|
||||
sync::{Arc, LazyLock},
|
||||
};
|
||||
use time::{Duration, OffsetDateTime};
|
||||
use tower_http::services::ServeDir;
|
||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
|
||||
use uuid::Uuid;
|
||||
@@ -140,7 +147,7 @@ mod tests {
|
||||
}
|
||||
|
||||
impl Backend {
|
||||
async fn client(&self, cookies: CookieJar) -> (CookieJar, Client) {
|
||||
async fn client(&self, cookies: PrivateCookieJar) -> (PrivateCookieJar, Client) {
|
||||
let existing_uuid = cookies
|
||||
.get("client_id")
|
||||
.and_then(|cookie| Uuid::parse_str(cookie.value()).ok());
|
||||
@@ -149,14 +156,24 @@ impl Backend {
|
||||
Some(uuid) => (cookies, self.get_client(&uuid).await),
|
||||
None => {
|
||||
let new_id = Uuid::new_v4();
|
||||
let updated_cookies = cookies.add(Cookie::new("client_id", new_id.to_string()));
|
||||
let expiration_date = OffsetDateTime::now_utc() + Duration::days(30);
|
||||
let mut cookie = Cookie::new("client_id", new_id.to_string());
|
||||
cookie.set_expires(Expiration::DateTime(expiration_date.into()));
|
||||
cookie.set_http_only(true);
|
||||
cookie.set_secure(true);
|
||||
|
||||
let updated_cookies = cookies.add(cookie);
|
||||
(updated_cookies, self.get_client(&new_id).await)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Combined method for getting both client and language
|
||||
async fn client_full(&self, cookies: CookieJar, headers: &HeaderMap) -> (CookieJar, Req) {
|
||||
async fn client_full(
|
||||
&self,
|
||||
cookies: PrivateCookieJar,
|
||||
headers: &HeaderMap,
|
||||
) -> (PrivateCookieJar, Req) {
|
||||
let (cookies, client) = self.client(cookies).await;
|
||||
let lang = language::language(&cookies, headers);
|
||||
(cookies, Req { client, lang })
|
||||
@@ -190,6 +207,55 @@ impl Backend {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AppState {
|
||||
pub(crate) backend: Arc<Backend>,
|
||||
pub key: Key,
|
||||
}
|
||||
|
||||
impl axum::extract::FromRef<AppState> for Key {
|
||||
fn from_ref(state: &AppState) -> Self {
|
||||
state.key.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl axum::extract::FromRef<AppState> for Arc<Backend> {
|
||||
fn from_ref(state: &AppState) -> Self {
|
||||
state.backend.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Config {
|
||||
key: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
fn generate() -> Self {
|
||||
Self {
|
||||
key: Key::generate().master().to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_or_create_key() -> Result<Key, Box<dyn std::error::Error>> {
|
||||
let config_path = "config.toml";
|
||||
|
||||
// Try to read existing config
|
||||
if Path::new(config_path).exists() {
|
||||
let content = fs::read_to_string(config_path)?;
|
||||
let config: Config = toml::from_str(&content)?;
|
||||
return Ok(Key::from(&config.key));
|
||||
}
|
||||
|
||||
// Create new config if file doesn't exist
|
||||
let config = Config::generate();
|
||||
let toml_string = toml::to_string(&config)?;
|
||||
fs::write(config_path, toml_string)?;
|
||||
|
||||
Ok(Key::from(&config.key))
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
tracing_subscriber::registry()
|
||||
@@ -203,11 +269,17 @@ async fn main() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let key = load_or_create_key().unwrap();
|
||||
let state = AppState {
|
||||
backend: Arc::new(Backend::Sqlite(db)),
|
||||
key,
|
||||
};
|
||||
|
||||
let app = Router::new()
|
||||
.route("/", get(index::index))
|
||||
.nest_service("/static", ServeDir::new("./static/serve"))
|
||||
.merge(game::routes())
|
||||
.with_state(Arc::new(Backend::Sqlite(db)));
|
||||
.with_state(state);
|
||||
|
||||
// run our app with hyper, listening globally on port 3000
|
||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
|
||||
|
Reference in New Issue
Block a user