Compare commits

..

No commits in common. "8a627243345e6d69277a2ebb33402ed5b4c56cb1" and "bab52687c7619f3ab67c1ab61c726a6a89f20326" have entirely different histories.

12 changed files with 1 additions and 373 deletions

92
koucha/Cargo.lock generated
View file

@ -73,70 +73,6 @@ dependencies = [
"fs_extra",
]
[[package]]
name = "axum"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8"
dependencies = [
"axum-core",
"axum-macros",
"bytes",
"form_urlencoded",
"futures-util",
"http",
"http-body",
"http-body-util",
"hyper",
"hyper-util",
"itoa",
"matchit",
"memchr",
"mime",
"percent-encoding",
"pin-project-lite",
"serde_core",
"serde_json",
"serde_path_to_error",
"serde_urlencoded",
"sync_wrapper",
"tokio",
"tower",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "axum-core"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1"
dependencies = [
"bytes",
"futures-core",
"http",
"http-body",
"http-body-util",
"mime",
"pin-project-lite",
"sync_wrapper",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "axum-macros"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "base64"
version = "0.22.1"
@ -790,12 +726,6 @@ version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]]
name = "httpdate"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hyper"
version = "1.8.1"
@ -810,7 +740,6 @@ dependencies = [
"http",
"http-body",
"httparse",
"httpdate",
"itoa",
"pin-project-lite",
"pin-utils",
@ -1070,12 +999,9 @@ dependencies = [
name = "koucha"
version = "0.1.0"
dependencies = [
"axum",
"chrono",
"reqwest",
"rss",
"serde",
"serde_json",
"sqlx",
"tokio",
]
@ -1150,12 +1076,6 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
[[package]]
name = "matchit"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
[[package]]
name = "md-5"
version = "0.10.6"
@ -1788,17 +1708,6 @@ dependencies = [
"zmij",
]
[[package]]
name = "serde_path_to_error"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457"
dependencies = [
"itoa",
"serde",
"serde_core",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
@ -2312,7 +2221,6 @@ dependencies = [
"tokio",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]

View file

@ -9,6 +9,3 @@ rss = "2.0.12"
tokio = { version = "1.49.0", features = ["full"] }
sqlx = { version = "0.8.6", features = [ "runtime-tokio", "sqlite" ] }
chrono = "0.4.43"
axum = { version= "0.8.8", features = [ "macros" ] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.149"

0
koucha/src/bin/server.rs Normal file
View file

View file

@ -1,43 +0,0 @@
use std::collections::HashMap;
use koucha::{
Adapter, AdapterBuilder, db::{User, UserKey as DbUserKey}
};
use crate::types::UserKey;
mod routes;
mod types;
#[derive(Clone)]
struct AppState {
pub adapter: Adapter,
// TODO: Set up UserKey expirations
auth_map: HashMap<UserKey, DbUserKey>,
}
impl AppState {
fn create_user_key(&mut self, db_user_key: DbUserKey) -> UserKey {
let key = UserKey::new();
self.auth_map.insert(key.clone(), db_user_key);
key
}
#[allow(dead_code)]
fn user_from_key(&self, key: &UserKey) -> Option<DbUserKey> {
self.auth_map.get(key).map(DbUserKey::clone)
}
}
#[tokio::main]
async fn main() {
let db_url = std::env::var("DATABASE_URL").unwrap();
let adapter = AdapterBuilder::new()
.database_url(&db_url)
.create().await.unwrap();
let auth_map = HashMap::new();
let app = routes::router().with_state(AppState { adapter, auth_map });
let listener = tokio::net::TcpListener::bind("0.0.0.0:4142").await.unwrap();
axum::serve(listener, app).await.unwrap();
}

View file

@ -1,41 +0,0 @@
use axum::{Json, extract::State};
use koucha::db::User as DbUser;
use reqwest::StatusCode;
use serde::{Serialize, Deserialize};
use crate::{
AppState, routes::{ApiError, ApiResult, ApiResponse}, types::UserKey
};
#[derive(Deserialize)]
pub struct Input {
pub user_name: String,
}
#[derive(Serialize, Deserialize)]
pub struct Output {
user_key: UserKey,
}
pub async fn handler(
State(mut state): State<AppState>,
Json(body): Json<Input>,
) -> ApiResult<Output> {
let dbuser = DbUser::temporary_auth(
state.adapter.get_pool(),
&body.user_name
).await.map_err(|_e| {
// TODO: Logging
ApiError {
status: StatusCode::INTERNAL_SERVER_ERROR,
error: "InternalError",
message: String::from(
"Error authentiating user ".to_owned() + &body.user_name
),
}
})?;
let key = state.create_user_key(dbuser.key());
Ok(ApiResponse(StatusCode::OK, Output { user_key: key }))
}

View file

@ -1,37 +0,0 @@
use axum::{Json, extract::State, http::HeaderMap};
use koucha::db::User as DbUser;
use reqwest::StatusCode;
use serde::{Serialize, Deserialize};
use crate::{
AppState, routes::{ApiError, ApiResult, ApiResponse}, types::User
};
#[derive(Deserialize)]
pub struct Input { }
#[derive(Serialize, Deserialize)]
pub struct Output {
users: Vec<User>,
}
pub async fn handler(
headers: HeaderMap,
State(state): State<AppState>,
Json(_body): Json<Input>,
) -> ApiResult<Output> {
let dbusers = DbUser::get_all(state.adapter.get_pool())
.await.map_err(|_e| {
// TODO: Logging
ApiError {
status: StatusCode::INTERNAL_SERVER_ERROR,
error: "InternalError",
message: "Error getting all users from DB.".to_string(),
}
})?;
let users: Vec<User> = dbusers.iter().map(|u| User {
name: u.name().to_string(),
}).collect();
Ok(ApiResponse(StatusCode::OK, Output { users }))
}

View file

@ -1,36 +0,0 @@
use axum::{Json, extract::State};
use koucha::db::User as DbUser;
use reqwest::StatusCode;
use serde::{Serialize, Deserialize};
use crate::{
AppState, routes::{ApiError, ApiResult, ApiResponse}, types::User
};
#[derive(Deserialize)]
pub struct Input { }
#[derive(Serialize, Deserialize)]
pub struct Output {
users: Vec<User>,
}
pub async fn handler(
State(state): State<AppState>,
Json(_body): Json<Input>,
) -> ApiResult<Output> {
let dbusers = DbUser::get_all(state.adapter.get_pool())
.await.map_err(|_e| {
// TODO: Logging
ApiError {
status: StatusCode::INTERNAL_SERVER_ERROR,
error: "InternalError",
message: "Error getting all users from DB.".to_string(),
}
})?;
let users: Vec<User> = dbusers.iter().map(|u| User {
name: u.name().to_string(),
}).collect();
Ok(ApiResponse(StatusCode::OK, Output { users }))
}

View file

@ -1,49 +0,0 @@
use serde::Serialize;
use serde_json::json;
use axum::{
Json,
Router,
response::IntoResponse,
routing::{
get,
post,
},
};
use reqwest::StatusCode;
mod get_feeds;
mod create_session;
mod get_users;
mod new_user;
use crate::AppState;
pub type ApiResult<T> = Result<ApiResponse<T>, ApiError>;
pub struct ApiResponse<T>(pub StatusCode, pub T);
pub struct ApiError {
pub status: StatusCode,
pub error: &'static str,
pub message: String,
}
pub fn router() -> Router<AppState> {
Router::new()
.route("/create_session", post(create_session::handler))
.route("/get_feeds", get(get_feeds::handler))
.route("/get_users", get(get_users::handler))
.route("/new_user", post(new_user::handler))
}
impl IntoResponse for ApiError {
fn into_response(self) -> axum::response::Response {
let body = json!({ "error": self.error, "message": self.message });
(self.status, Json(body)).into_response()
}
}
impl<T: Serialize> IntoResponse for ApiResponse<T> {
fn into_response(self) -> axum::response::Response {
(self.0, Json(self.1)).into_response()
}
}

View file

@ -1,40 +0,0 @@
use axum::{Json, extract::State};
use koucha::db::User as DbUser;
use reqwest::StatusCode;
use serde::{Serialize, Deserialize};
use crate::{
AppState, routes::{ApiError, ApiResult, ApiResponse}, types::User
};
#[derive(Deserialize)]
pub struct Input {
user_name: String,
}
#[derive(Serialize, Deserialize)]
pub struct Output {
user: User,
}
pub async fn handler(
State(state): State<AppState>,
Json(body): Json<Input>,
) -> ApiResult<Output> {
let dbuser = DbUser::create(
state.adapter.get_pool(),
&body.user_name
).await.map_err(|_e| {
// TODO: Logging
ApiError {
status: StatusCode::INTERNAL_SERVER_ERROR,
error: "InternalError",
message: String::from(
"Error creating user ".to_owned() + &body.user_name
),
}
})?;
let user = User { name: dbuser.name().to_string() };
Ok(ApiResponse(StatusCode::OK, Output { user }))
}

View file

@ -1,17 +0,0 @@
use chrono::Utc;
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
pub struct User {
pub name: String,
}
#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct UserKey(String);
impl UserKey {
pub fn new() -> Self {
UserKey(
Utc::now().to_rfc3339()
)
}
}

View file

@ -29,17 +29,6 @@ pub struct User {
impl User {
pub fn key(&self) -> UserKey { self.key }
pub fn name(&self) -> &str { &self.name }
pub async fn temporary_auth(
pool: &AdapterPool,
name: &str,
) -> Result<Self> {
sqlx::query_as!(
UnparsedUser,
"SELECT id as `id!`, name FROM users WHERE name = ?",
name
).fetch_one(&pool.0).await?.parse()
}
pub async fn get(pool: &AdapterPool, key: UserKey) -> Result<Self> {
sqlx::query_as!(

View file

@ -1,6 +1,6 @@
use std::error::Error;
pub type Result<T> = std::result::Result<T, Box<dyn Error>>;
type Result<T> = std::result::Result<T, Box<dyn Error>>;
pub mod db;
pub mod fetch;
@ -9,9 +9,7 @@ pub mod score;
#[cfg(test)]
pub mod test_utils;
#[derive(Clone)]
pub struct AdapterPool(sqlx::SqlitePool);
#[derive(Clone)]
pub struct AdapterClient(reqwest::Client);
pub struct AdapterBuilder {
@ -40,7 +38,6 @@ impl AdapterBuilder {
}
}
#[derive(Clone)]
pub struct Adapter {
db: AdapterPool,
client: AdapterClient,