Compare commits
3 Commits
974ca80298
...
7abce21aee
Author | SHA1 | Date |
---|---|---|
|
7abce21aee | |
|
279983c64c | |
|
f3ede6f9a6 |
|
@ -1243,7 +1243,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
|
checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "otter"
|
name = "otterd"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
|
|
26
Cargo.toml
26
Cargo.toml
|
@ -1,30 +1,12 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
|
resolver = '2'
|
||||||
members = [
|
members = [
|
||||||
|
'server',
|
||||||
'gpodder',
|
'gpodder',
|
||||||
'gpodder_sqlite'
|
'gpodder_sqlite'
|
||||||
]
|
]
|
||||||
|
|
||||||
[package]
|
[workspace.dependencies]
|
||||||
name = "otter"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
gpodder = { path = "./gpodder" }
|
|
||||||
gpodder_sqlite = { path = "./gpodder_sqlite" }
|
|
||||||
|
|
||||||
axum = { version = "0.8.1", features = ["macros"] }
|
|
||||||
axum-extra = { version = "0.10", features = ["cookie", "typed-header"] }
|
|
||||||
chrono = { version = "0.4.39", features = ["serde"] }
|
|
||||||
clap = { version = "4.5.30", features = ["derive", "env"] }
|
|
||||||
cookie = "0.18.1"
|
|
||||||
figment = { version = "0.10.19", features = ["env", "toml"] }
|
|
||||||
http-body-util = "0.1.3"
|
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
serde = { version = "1.0.218", features = ["derive"] }
|
|
||||||
tokio = { version = "1.43.0", features = ["full"] }
|
|
||||||
tower-http = { version = "0.6.2", features = ["set-header", "trace"] }
|
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
tracing-subscriber = "0.3.19"
|
chrono = "0.4.39"
|
||||||
tera = "1.20.0"
|
|
||||||
axum-range = "0.5.0"
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = { version = "0.4.39", features = ["serde"] }
|
rand = { workspace = true }
|
||||||
|
chrono = { workspace = true }
|
||||||
|
|
||||||
argon2 = "0.5.3"
|
argon2 = "0.5.3"
|
||||||
rand = "0.8.5"
|
|
||||||
|
|
|
@ -9,12 +9,14 @@ harness = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gpodder = { path = "../gpodder" }
|
gpodder = { path = "../gpodder" }
|
||||||
|
|
||||||
|
rand = { workspace = true }
|
||||||
|
tracing = { workspace = true }
|
||||||
|
chrono = { workspace = true, features = ["serde"] }
|
||||||
|
|
||||||
|
libsqlite3-sys = { version = "0.31.0", features = ["bundled"] }
|
||||||
diesel = { version = "2.2.7", features = ["r2d2", "sqlite", "returning_clauses_for_sqlite_3_35"] }
|
diesel = { version = "2.2.7", features = ["r2d2", "sqlite", "returning_clauses_for_sqlite_3_35"] }
|
||||||
diesel_migrations = { version = "2.2.0", features = ["sqlite"] }
|
diesel_migrations = { version = "2.2.0", features = ["sqlite"] }
|
||||||
tracing = "0.1.41"
|
|
||||||
chrono = { version = "0.4.39", features = ["serde"] }
|
|
||||||
rand = "0.8.5"
|
|
||||||
libsqlite3-sys = { version = "0.31.0", features = ["bundled"] }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.5.1"
|
criterion = "0.5.1"
|
||||||
|
|
|
@ -17,10 +17,8 @@ use crate::schema::*;
|
||||||
pub struct Device {
|
pub struct Device {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
pub device_id: String,
|
pub device_id: String,
|
||||||
pub user_id: i64,
|
|
||||||
pub caption: String,
|
pub caption: String,
|
||||||
pub type_: DeviceType,
|
pub type_: DeviceType,
|
||||||
pub sync_group_id: Option<i64>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Insertable)]
|
#[derive(Insertable)]
|
||||||
|
|
|
@ -6,8 +6,6 @@ use crate::schema::*;
|
||||||
#[diesel(table_name = device_subscriptions)]
|
#[diesel(table_name = device_subscriptions)]
|
||||||
#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
|
#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
|
||||||
pub struct DeviceSubscription {
|
pub struct DeviceSubscription {
|
||||||
pub id: i64,
|
|
||||||
pub device_id: i64,
|
|
||||||
pub podcast_url: String,
|
pub podcast_url: String,
|
||||||
pub time_changed: i64,
|
pub time_changed: i64,
|
||||||
pub deleted: bool,
|
pub deleted: bool,
|
||||||
|
|
|
@ -16,9 +16,6 @@ use crate::schema::*;
|
||||||
#[diesel(table_name = episode_actions)]
|
#[diesel(table_name = episode_actions)]
|
||||||
#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
|
#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
|
||||||
pub struct EpisodeAction {
|
pub struct EpisodeAction {
|
||||||
pub id: i64,
|
|
||||||
pub user_id: i64,
|
|
||||||
pub device_id: Option<i64>,
|
|
||||||
pub podcast_url: String,
|
pub podcast_url: String,
|
||||||
pub episode_url: String,
|
pub episode_url: String,
|
||||||
pub time_changed: i64,
|
pub time_changed: i64,
|
||||||
|
|
|
@ -35,7 +35,7 @@ fn test_create_session() {
|
||||||
|
|
||||||
let new_session = Session {
|
let new_session = Session {
|
||||||
id: 123,
|
id: 123,
|
||||||
//
|
user_agent: None,
|
||||||
last_seen: chrono::Utc::now().trunc_subsecs(0),
|
last_seen: chrono::Utc::now().trunc_subsecs(0),
|
||||||
user: users[0].clone(),
|
user: users[0].clone(),
|
||||||
};
|
};
|
||||||
|
@ -56,7 +56,7 @@ fn test_remove_session() {
|
||||||
|
|
||||||
let new_session = Session {
|
let new_session = Session {
|
||||||
id: 123,
|
id: 123,
|
||||||
//
|
user_agent: None,
|
||||||
last_seen: chrono::Utc::now().trunc_subsecs(0),
|
last_seen: chrono::Utc::now().trunc_subsecs(0),
|
||||||
user: users[0].clone(),
|
user: users[0].clone(),
|
||||||
};
|
};
|
||||||
|
@ -86,7 +86,7 @@ fn test_refresh_session() {
|
||||||
|
|
||||||
let mut new_session = Session {
|
let mut new_session = Session {
|
||||||
id: 123,
|
id: 123,
|
||||||
//
|
user_agent: None,
|
||||||
last_seen: chrono::Utc::now().trunc_subsecs(0) - TimeDelta::seconds(10),
|
last_seen: chrono::Utc::now().trunc_subsecs(0) - TimeDelta::seconds(10),
|
||||||
user: users[0].clone(),
|
user: users[0].clone(),
|
||||||
};
|
};
|
||||||
|
@ -124,6 +124,7 @@ fn test_remove_old_sessions() {
|
||||||
store
|
store
|
||||||
.insert_session(&Session {
|
.insert_session(&Session {
|
||||||
id: i as i64,
|
id: i as i64,
|
||||||
|
user_agent: None,
|
||||||
last_seen: ts,
|
last_seen: ts,
|
||||||
user: users[0].clone(),
|
user: users[0].clone(),
|
||||||
})
|
})
|
||||||
|
@ -143,6 +144,7 @@ fn test_remove_old_sessions() {
|
||||||
store.get_session(0).expect("get session shouldn't fail"),
|
store.get_session(0).expect("get session shouldn't fail"),
|
||||||
Some(Session {
|
Some(Session {
|
||||||
id: 0,
|
id: 0,
|
||||||
|
user_agent: None,
|
||||||
last_seen: now,
|
last_seen: now,
|
||||||
user: users[0].clone(),
|
user: users[0].clone(),
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
[package]
|
||||||
|
name = "otterd"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
gpodder = { path = "../gpodder" }
|
||||||
|
gpodder_sqlite = { path = "../gpodder_sqlite" }
|
||||||
|
|
||||||
|
chrono = { workspace = true, features = ["serde"] }
|
||||||
|
rand = { workspace = true }
|
||||||
|
tracing = { workspace = true }
|
||||||
|
|
||||||
|
serde = { version = "1.0.218", features = ["derive"] }
|
||||||
|
figment = { version = "0.10.19", features = ["env", "toml"] }
|
||||||
|
clap = { version = "4.5.30", features = ["derive", "env"] }
|
||||||
|
|
||||||
|
tower-http = { version = "0.6.2", features = ["set-header", "trace"] }
|
||||||
|
axum = { version = "0.8.1", features = ["macros"] }
|
||||||
|
axum-extra = { version = "0.10", features = ["cookie", "typed-header"] }
|
||||||
|
axum-range = "0.5.0"
|
||||||
|
|
||||||
|
cookie = "0.18.1"
|
||||||
|
http-body-util = "0.1.3"
|
||||||
|
tokio = { version = "1.43.0", features = ["full"] }
|
||||||
|
tracing-subscriber = "0.3.19"
|
||||||
|
tera = "1.20.0"
|
|
@ -20,7 +20,7 @@ use crate::server::{
|
||||||
pub fn router() -> Router<Context> {
|
pub fn router() -> Router<Context> {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/{username}/login.json", post(post_login))
|
.route("/{username}/login.json", post(post_login))
|
||||||
.route("/{username}/logout.json", post(post_logout))
|
.route("/{_username}/logout.json", post(post_logout))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn post_login(
|
async fn post_login(
|
||||||
|
@ -85,7 +85,7 @@ async fn post_login(
|
||||||
|
|
||||||
async fn post_logout(
|
async fn post_logout(
|
||||||
State(ctx): State<Context>,
|
State(ctx): State<Context>,
|
||||||
Path(username): Path<String>,
|
Path(_username): Path<String>,
|
||||||
jar: CookieJar,
|
jar: CookieJar,
|
||||||
) -> AppResult<CookieJar> {
|
) -> AppResult<CookieJar> {
|
||||||
if let Some(session_id) = jar.get(SESSION_ID_COOKIE) {
|
if let Some(session_id) = jar.get(SESSION_ID_COOKIE) {
|
|
@ -19,7 +19,10 @@ pub fn router(ctx: Context) -> Router<Context> {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/{username}", get(get_devices))
|
.route("/{username}", get(get_devices))
|
||||||
.route("/{username}/{id}", post(post_device))
|
.route("/{username}/{id}", post(post_device))
|
||||||
.layer(middleware::from_fn_with_state(ctx.clone(), auth_api_middleware))
|
.layer(middleware::from_fn_with_state(
|
||||||
|
ctx.clone(),
|
||||||
|
auth_api_middleware,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_devices(
|
async fn get_devices(
|
|
@ -22,7 +22,10 @@ pub fn router(ctx: Context) -> Router<Context> {
|
||||||
"/{username}/{id}",
|
"/{username}/{id}",
|
||||||
post(post_subscription_changes).get(get_subscription_changes),
|
post(post_subscription_changes).get(get_subscription_changes),
|
||||||
)
|
)
|
||||||
.layer(middleware::from_fn_with_state(ctx.clone(), auth_api_middleware))
|
.layer(middleware::from_fn_with_state(
|
||||||
|
ctx.clone(),
|
||||||
|
auth_api_middleware,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn post_subscription_changes(
|
pub async fn post_subscription_changes(
|
|
@ -21,7 +21,10 @@ pub fn router(ctx: Context) -> Router<Context> {
|
||||||
"/{username}",
|
"/{username}",
|
||||||
get(get_sync_status).post(post_sync_status_changes),
|
get(get_sync_status).post(post_sync_status_changes),
|
||||||
)
|
)
|
||||||
.layer(middleware::from_fn_with_state(ctx.clone(), auth_api_middleware))
|
.layer(middleware::from_fn_with_state(
|
||||||
|
ctx.clone(),
|
||||||
|
auth_api_middleware,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_sync_status(
|
pub async fn get_sync_status(
|
|
@ -34,7 +34,7 @@ impl<'de> Deserialize<'de> for StringWithFormat {
|
||||||
{
|
{
|
||||||
struct StrVisitor;
|
struct StrVisitor;
|
||||||
|
|
||||||
impl<'de> Visitor<'de> for StrVisitor {
|
impl Visitor<'_> for StrVisitor {
|
||||||
type Value = StringWithFormat;
|
type Value = StringWithFormat;
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
@ -18,7 +18,10 @@ pub fn router(ctx: Context) -> Router<Context> {
|
||||||
get(get_device_subscriptions).put(put_device_subscriptions),
|
get(get_device_subscriptions).put(put_device_subscriptions),
|
||||||
)
|
)
|
||||||
.route("/{username}", get(get_user_subscriptions))
|
.route("/{username}", get(get_user_subscriptions))
|
||||||
.layer(middleware::from_fn_with_state(ctx.clone(), auth_api_middleware))
|
.layer(middleware::from_fn_with_state(
|
||||||
|
ctx.clone(),
|
||||||
|
auth_api_middleware,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_device_subscriptions(
|
pub async fn get_device_subscriptions(
|
|
@ -61,7 +61,6 @@ struct LoginForm {
|
||||||
async fn post_login(
|
async fn post_login(
|
||||||
State(ctx): State<Context>,
|
State(ctx): State<Context>,
|
||||||
user_agent: Option<TypedHeader<UserAgent>>,
|
user_agent: Option<TypedHeader<UserAgent>>,
|
||||||
_headers: HeaderMap,
|
|
||||||
jar: CookieJar,
|
jar: CookieJar,
|
||||||
Form(login): Form<LoginForm>,
|
Form(login): Form<LoginForm>,
|
||||||
) -> AppResult<Response> {
|
) -> AppResult<Response> {
|
Loading…
Reference in New Issue