Merge branch 'inappropriation'

This commit is contained in:
Joe Ardent 2023-07-08 21:26:55 -07:00
commit b593711d11
27 changed files with 98 additions and 101 deletions

68
Cargo.lock generated
View file

@ -2424,6 +2424,40 @@ dependencies = [
"webpki", "webpki",
] ]
[[package]]
name = "what2watch"
version = "0.0.1"
dependencies = [
"argon2",
"askama",
"askama_axum",
"async-session",
"axum",
"axum-login",
"axum-macros",
"axum-test",
"chrono",
"clap",
"justerror",
"optional_optional_user",
"password-hash",
"rand",
"rand_distr",
"serde",
"serde_test",
"sqlx",
"thiserror",
"tokio",
"tokio-retry",
"tokio-stream",
"tower",
"tower-http 0.4.1",
"tracing",
"tracing-subscriber",
"ulid",
"unicode-segmentation",
]
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"
@ -2521,40 +2555,6 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "witch_watch"
version = "0.0.1"
dependencies = [
"argon2",
"askama",
"askama_axum",
"async-session",
"axum",
"axum-login",
"axum-macros",
"axum-test",
"chrono",
"clap",
"justerror",
"optional_optional_user",
"password-hash",
"rand",
"rand_distr",
"serde",
"serde_test",
"sqlx",
"thiserror",
"tokio",
"tokio-retry",
"tokio-stream",
"tower",
"tower-http 0.4.1",
"tracing",
"tracing-subscriber",
"ulid",
"unicode-segmentation",
]
[[package]] [[package]]
name = "zeroize" name = "zeroize"
version = "1.6.0" version = "1.6.0"

View file

@ -1,8 +1,8 @@
[package] [package]
name = "witch_watch" name = "what2watch"
version = "0.0.1" version = "0.0.1"
edition = "2021" edition = "2021"
default-run = "witch_watch" default-run = "what2watch"
[dependencies] [dependencies]
# local proc macro # local proc macro

View file

@ -1,12 +1,12 @@
-- indices -- indices
drop index if exists witch_dex; drop index if exists user_dex;
drop index if exists watch_dex; drop index if exists watch_dex;
drop index if exists ww_dex; drop index if exists ww_dex;
drop index if exists note_dex; drop index if exists note_dex;
-- tables -- tables
drop table if exists witch_watch; drop table if exists watch_quest;
drop table if exists watch_notes; drop table if exists watch_notes;
drop table if exists covens; drop table if exists covens;
drop table if exists witches; drop table if exists users;
drop table if exists watches; drop table if exists watches;

View file

@ -4,7 +4,7 @@
-- Dates are ints, unix epoch style -- Dates are ints, unix epoch style
-- users -- users
create table if not exists witches ( create table if not exists users (
id blob not null primary key, id blob not null primary key,
username text not null unique, username text not null unique,
displayname text, displayname text,
@ -24,12 +24,12 @@ create table if not exists watches (
release_date int, release_date int,
added_by blob not null, -- ID of the user that added it added_by blob not null, -- ID of the user that added it
last_updated int not null default (unixepoch()), last_updated int not null default (unixepoch()),
foreign key (added_by) references witches (id) foreign key (added_by) references users (id)
); );
-- table of what people want to watch -- table of what people want to watch
create table if not exists witch_watch ( create table if not exists watch_quest (
witch blob not null, user blob not null,
watch blob not null, watch blob not null,
priority int, -- 1-5 how much do you want to watch it priority int, -- 1-5 how much do you want to watch it
public boolean not null default true, public boolean not null default true,
@ -37,37 +37,37 @@ create table if not exists witch_watch (
when_added int not null default (unixepoch()), when_added int not null default (unixepoch()),
when_watched int, when_watched int,
last_updated int not null default (unixepoch()), last_updated int not null default (unixepoch()),
foreign key (witch) references witches (id) on delete cascade on update no action, foreign key (user) references users (id) on delete cascade on update no action,
foreign key (watch) references watches (id) on delete cascade on update no action, foreign key (watch) references watches (id) on delete cascade on update no action,
primary key (witch, watch) primary key (user, watch)
) without rowid; ) without rowid;
-- friend lists; this should really be a graph db, maybe the whole thing should be -- friend lists; this should really be a graph db, maybe the whole thing should be
-- TODO: look into replacing sqlite with https://www.cozodb.org/ -- TODO: look into replacing sqlite with https://www.cozodb.org/
create table if not exists covens ( create table if not exists covens (
witch blob not null primary key, user blob not null primary key,
coven blob, -- possibly empty friends list in some app-specific format coven blob, -- possibly empty friends list in some app-specific format
last_updated int not null default (unixepoch()), last_updated int not null default (unixepoch()),
foreign key (witch) references witches (id) on delete cascade on update no action foreign key (user) references users (id) on delete cascade on update no action
); );
create table if not exists watch_notes ( create table if not exists watch_notes (
id blob not null primary key, id blob not null primary key,
witch blob not null, user blob not null,
watch blob not null, watch blob not null,
note blob, note blob,
public boolean not null, public boolean not null,
last_updated int not null default (unixepoch()), last_updated int not null default (unixepoch()),
foreign key (witch) references witches (id) on delete cascade on update no action, foreign key (user) references users (id) on delete cascade on update no action,
foreign key (watch) references watches (id) on delete cascade on update no action foreign key (watch) references watches (id) on delete cascade on update no action
); );
-- indices, not needed for covens -- indices, not needed for covens
create index if not exists witch_username_dex on witches (username); create index if not exists user_username_dex on users (username);
create index if not exists witch_email_dex on witches (email); create index if not exists user_email_dex on users (email);
create index if not exists watch_title_dex on watches (title); create index if not exists watch_title_dex on watches (title);
create index if not exists watch_added_by_dex on watches (added_by); create index if not exists watch_added_by_dex on watches (added_by);
create index if not exists ww_witch_dex on witch_watch (witch); create index if not exists ww_user_dex on watch_quest (user);
create index if not exists ww_watch_dex on witch_watch (watch); create index if not exists ww_watch_dex on watch_quest (watch);
create index if not exists note_witch_dex on watch_notes (witch); create index if not exists note_user_dex on watch_notes (user);
create index if not exists note_watch_dex on watch_notes (watch); create index if not exists note_watch_dex on watch_notes (watch);

View file

@ -1,5 +1,5 @@
drop trigger if exists update_last_updated_witches; drop trigger if exists update_last_updated_users;
drop trigger if exists update_last_updated_watches; drop trigger if exists update_last_updated_watches;
drop trigger if exists update_last_updated_witch_watch; drop trigger if exists update_last_updated_watch_quest;
drop trigger if exists update_last_updated_covens; drop trigger if exists update_last_updated_covens;
drop trigger if exists update_last_updated_watch_notes; drop trigger if exists update_last_updated_watch_notes;

View file

@ -1,8 +1,8 @@
create trigger if not exists update_last_updated_witches create trigger if not exists update_last_updated_users
after update on witches after update on users
when OLD.last_updated = NEW.last_updated or OLD.last_updated is null when OLD.last_updated = NEW.last_updated or OLD.last_updated is null
BEGIN BEGIN
update witches set last_updated = (select unixepoch()) where id=NEW.id; update users set last_updated = (select unixepoch()) where id=NEW.id;
END; END;
create trigger if not exists update_last_updated_watches create trigger if not exists update_last_updated_watches
@ -12,11 +12,11 @@ BEGIN
update watches set last_updated = (select unixepoch()) where id=NEW.id; update watches set last_updated = (select unixepoch()) where id=NEW.id;
END; END;
create trigger if not exists update_last_updated_witch_watch create trigger if not exists update_last_updated_watch_quest
after update on witch_watch after update on watch_quest
when OLD.last_updated = NEW.last_updated or OLD.last_updated is null when OLD.last_updated = NEW.last_updated or OLD.last_updated is null
BEGIN BEGIN
update witch_watch set last_updated = (select unixepoch()) where id=NEW.id; update watch_quest set last_updated = (select unixepoch()) where id=NEW.id;
END; END;
create trigger if not exists update_last_updated_covens create trigger if not exists update_last_updated_covens

View file

@ -1,3 +0,0 @@
-rw-r--r-- 1 ardent ardent 16M Jul 6 13:51 /home/ardent/.witch-watch.db
6 seconds to add 99,612 watch quests.

View file

@ -2,7 +2,7 @@ use std::{ffi::OsString, time::Duration};
use clap::Parser; use clap::Parser;
use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions}; use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions};
use witch_watch::{get_db_pool, import_utils::add_omega_watches}; use what2watch::{get_db_pool, import_utils::add_omega_watches};
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
struct Cli { struct Cli {

View file

@ -9,7 +9,7 @@ use sqlx::{
}; };
use tokio::task::JoinSet; use tokio::task::JoinSet;
use tokio_retry::Retry; use tokio_retry::Retry;
use witch_watch::{ use what2watch::{
get_db_pool, get_db_pool,
import_utils::{add_omega_watches, add_users, add_watch_quests}, import_utils::{add_omega_watches, add_users, add_watch_quests},
DbId, User, WatchQuest, DbId, User, WatchQuest,
@ -49,7 +49,7 @@ async fn main() {
add_quests(user, movies, &ww_db, rng, normal).await; add_quests(user, movies, &ww_db, rng, normal).await;
} }
let end = std::time::Instant::now(); let end = std::time::Instant::now();
let rows: i32 = sqlx::query_scalar("select count(*) from witch_watch") let rows: i32 = sqlx::query_scalar("select count(*) from watch_quest")
.fetch_one(&ww_db) .fetch_one(&ww_db)
.await .await
.unwrap(); .unwrap();

View file

@ -26,7 +26,7 @@ pub async fn get_db_pool() -> SqlitePool {
{ {
let home = let home =
std::env::var("HOME").expect("Could not determine $HOME for finding db file"); std::env::var("HOME").expect("Could not determine $HOME for finding db file");
format!("{home}/.witch-watch.db") format!("{home}/.what2watch.db")
} }
#[cfg(test)] #[cfg(test)]
{ {
@ -107,7 +107,7 @@ pub async fn auth_layer(
pool: SqlitePool, pool: SqlitePool,
secret: &[u8], secret: &[u8],
) -> AuthLayer<SqlxStore<SqlitePool, User>, DbId, User> { ) -> AuthLayer<SqlxStore<SqlitePool, User>, DbId, User> {
const QUERY: &str = "select * from witches where id = $1"; const QUERY: &str = "select * from users where id = $1";
let store = SqliteStore::<User>::new(pool).with_query(QUERY); let store = SqliteStore::<User>::new(pool).with_query(QUERY);
AuthLayer::new(store, secret) AuthLayer::new(store, secret)
} }
@ -121,7 +121,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn it_migrates_the_db() { async fn it_migrates_the_db() {
let db = super::get_db_pool().await; let db = super::get_db_pool().await;
let r = sqlx::query("select count(*) from witches") let r = sqlx::query("select count(*) from users")
.fetch_one(&db) .fetch_one(&db)
.await; .await;
assert!(r.is_ok()); assert!(r.is_ok());

View file

@ -2,7 +2,7 @@ use sqlx::{query_scalar, SqlitePool};
use crate::{db_id::DbId, util::year_to_epoch, ShowKind, User, Watch, WatchQuest}; use crate::{db_id::DbId, util::year_to_epoch, ShowKind, User, Watch, WatchQuest};
const USER_EXISTS_QUERY: &str = "select count(*) from witches where id = $1"; const USER_EXISTS_QUERY: &str = "select count(*) from users where id = $1";
const MOVIE_QUERY: &str = "select * from movies order by random() limit 10000"; const MOVIE_QUERY: &str = "select * from movies order by random() limit 10000";
@ -52,7 +52,7 @@ impl From<&ImportMovieOmega> for Watch {
// utility functions for building CLI tools, currently just for benchmarking // utility functions for building CLI tools, currently just for benchmarking
//-************************************************************************ //-************************************************************************
pub async fn add_watch_quests(pool: &SqlitePool, quests: &[WatchQuest]) -> Result<(), ()> { pub async fn add_watch_quests(pool: &SqlitePool, quests: &[WatchQuest]) -> Result<(), ()> {
let mut builder = sqlx::QueryBuilder::new("insert into witch_watch (witch, watch) "); let mut builder = sqlx::QueryBuilder::new("insert into watch_quest (user, watch) ");
builder.push_values(quests, |mut b, quest| { builder.push_values(quests, |mut b, quest| {
let user = quest.user; let user = quest.user;
let watch = quest.watch; let watch = quest.watch;
@ -70,7 +70,7 @@ pub async fn add_watch_quests(pool: &SqlitePool, quests: &[WatchQuest]) -> Resul
pub async fn add_users(db_pool: &SqlitePool, users: &[User]) -> Result<(), ()> { pub async fn add_users(db_pool: &SqlitePool, users: &[User]) -> Result<(), ()> {
let mut builder = let mut builder =
sqlx::QueryBuilder::new("insert into witches (id, username, displayname, email, pwhash) "); sqlx::QueryBuilder::new("insert into users (id, username, displayname, email, pwhash) ");
builder.push_values(users.iter(), |mut b, user| { builder.push_values(users.iter(), |mut b, user| {
b.push_bind(user.id) b.push_bind(user.id)

View file

@ -2,14 +2,14 @@ use std::net::SocketAddr;
use rand::{thread_rng, RngCore}; use rand::{thread_rng, RngCore};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
use witch_watch::get_db_pool; use what2watch::get_db_pool;
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
tracing_subscriber::registry() tracing_subscriber::registry()
.with( .with(
tracing_subscriber::EnvFilter::try_from_default_env() tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "witch_watch=debug,axum::routing=info".into()), .unwrap_or_else(|_| "what2watch=debug,axum::routing=info".into()),
) )
.with(tracing_subscriber::fmt::layer()) .with(tracing_subscriber::fmt::layer())
.init(); .init();
@ -23,7 +23,7 @@ async fn main() {
bytes bytes
}; };
let app = witch_watch::app(pool, &secret).await; let app = what2watch::app(pool, &secret).await;
let addr: SocketAddr = ([0, 0, 0, 0], 3000).into(); let addr: SocketAddr = ([0, 0, 0, 0], 3000).into();
tracing::debug!("binding to {addr:?}"); tracing::debug!("binding to {addr:?}");

View file

@ -14,8 +14,8 @@ use unicode_segmentation::UnicodeSegmentation;
use crate::{util::empty_string_as_none, DbId, SignupPage, SignupSuccessPage, User}; use crate::{util::empty_string_as_none, DbId, SignupPage, SignupSuccessPage, User};
pub(crate) const CREATE_QUERY: &str = pub(crate) const CREATE_QUERY: &str =
"insert into witches (id, username, displayname, email, pwhash) values ($1, $2, $3, $4, $5)"; "insert into users (id, username, displayname, email, pwhash) values ($1, $2, $3, $4, $5)";
const ID_QUERY: &str = "select * from witches where id = $1"; const ID_QUERY: &str = "select * from users where id = $1";
//-************************************************************************ //-************************************************************************
// Error types for user creation // Error types for user creation
@ -138,7 +138,7 @@ pub async fn get_signup_success(
let mut resp = SignupSuccessPage(user.clone()).into_response(); let mut resp = SignupSuccessPage(user.clone()).into_response();
if user.username.is_empty() || id.is_nil() { if user.username.is_empty() || id.is_nil() {
// redirect to front page if we got here without a valid witch ID // redirect to front page if we got here without a valid user ID
*resp.status_mut() = StatusCode::SEE_OTHER; *resp.status_mut() = StatusCode::SEE_OTHER;
resp.headers_mut().insert("Location", "/".parse().unwrap()); resp.headers_mut().insert("Location", "/".parse().unwrap());
} }

View file

@ -32,7 +32,7 @@ pub async fn server() -> TestServer {
.await .await
.unwrap(); .unwrap();
let r = sqlx::query("select count(*) from witches") let r = sqlx::query("select count(*) from users")
.fetch_one(&pool) .fetch_one(&pool)
.await; .await;
assert!(r.is_ok()); assert!(r.is_ok());
@ -49,7 +49,7 @@ pub async fn server() -> TestServer {
pub async fn server_with_pool(pool: &SqlitePool) -> TestServer { pub async fn server_with_pool(pool: &SqlitePool) -> TestServer {
let secret = [0u8; 64]; let secret = [0u8; 64];
let r = sqlx::query("select count(*) from witches") let r = sqlx::query("select count(*) from users")
.fetch_one(pool) .fetch_one(pool)
.await; .await;
assert!(r.is_ok()); assert!(r.is_ok());

View file

@ -10,8 +10,8 @@ use sqlx::SqlitePool;
use crate::{AuthContext, DbId}; use crate::{AuthContext, DbId};
const USERNAME_QUERY: &str = "select * from witches where username = $1"; const USERNAME_QUERY: &str = "select * from users where username = $1";
const LAST_SEEN_QUERY: &str = "update witches set last_seen = (select unixepoch()) where id = $1"; const LAST_SEEN_QUERY: &str = "update users set last_seen = (select unixepoch()) where id = $1";
#[derive(Default, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)] #[derive(Default, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
pub struct User { pub struct User {

View file

@ -18,13 +18,13 @@ use crate::{
//-************************************************************************ //-************************************************************************
const GET_SAVED_WATCHES_QUERY: &str = const GET_SAVED_WATCHES_QUERY: &str =
"select * from watches left join witch_watch on $1 = witch_watch.witch and watches.id = witch_watch.watch"; "select * from watches left join watch_quest on $1 = watch_quest.user and watches.id = watch_quest.watch";
const GET_WATCH_QUERY: &str = "select * from watches where id = $1"; const GET_WATCH_QUERY: &str = "select * from watches where id = $1";
const ADD_WATCH_QUERY: &str = "insert into watches (id, title, kind, release_date, metadata_url, added_by) values ($1, $2, $3, $4, $5, $6)"; const ADD_WATCH_QUERY: &str = "insert into watches (id, title, kind, release_date, metadata_url, added_by) values ($1, $2, $3, $4, $5, $6)";
const ADD_WITCH_WATCH_QUERY: &str = const ADD_WATCH_QUEST_QUERY: &str =
"insert into witch_watch (witch, watch, public, watched) values ($1, $2, $3, $4)"; "insert into watch_quest (user, watch, public, watched) values ($1, $2, $3, $4)";
const EMPTY_SEARCH_QUERY_STRUCT: SearchQuery = SearchQuery { const EMPTY_SEARCH_QUERY_STRUCT: SearchQuery = SearchQuery {
title: None, title: None,
@ -170,7 +170,7 @@ pub(crate) async fn add_new_watch_impl(
})?; })?;
if let Some(quest) = quest { if let Some(quest) = quest {
query(ADD_WITCH_WATCH_QUERY) query(ADD_WATCH_QUEST_QUERY)
.bind(quest.user) .bind(quest.user)
.bind(quest.watch) .bind(quest.watch)
.bind(quest.is_public) .bind(quest.is_public)
@ -200,7 +200,7 @@ pub async fn post_add_watch_quest(
} }
pub async fn _add_watch_quest_impl(pool: &SqlitePool, quest: &WatchQuest) -> Result<(), ()> { pub async fn _add_watch_quest_impl(pool: &SqlitePool, quest: &WatchQuest) -> Result<(), ()> {
query(ADD_WITCH_WATCH_QUERY) query(ADD_WATCH_QUEST_QUERY)
.bind(quest.user) .bind(quest.user)
.bind(quest.watch) .bind(quest.watch)
.bind(quest.is_public) .bind(quest.is_public)

View file

@ -1,7 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% import "macros.html" as m %} {% import "macros.html" as m %}
{% block title %}Welcome to Witch Watch, Bish{% endblock %} {% block title %}Welcome to What 2 Watch, Bish{% endblock %}
{% block content %} {% block content %}

View file

@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}{{ title }} - Witch Watch{% endblock %}</title> <title>{% block title %}{{ title }} - What 2 Watch{% endblock %}</title>
{% block head %}{% endblock %} {% block head %}{% endblock %}
</head> </head>
<body> <body>

View file

@ -1,7 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% import "macros.html" as m %} {% import "macros.html" as m %}
{% block title %}Welcome to Witch Watch, Bish{% endblock %} {% block title %}Welcome to What 2 Watch, Bish{% endblock %}
{% block content %} {% block content %}

View file

@ -1,10 +1,10 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}Welcome to Witch Watch, Bish{% endblock %} {% block title %}Welcome to What 2 Watch, Bish{% endblock %}
{% block content %} {% block content %}
<h1>Welcome to Witch Watch</h1> <h1>Welcome to What 2 Watch</h1>
{% match user %} {% match user %}
{% when Some with (usr) %} {% when Some with (usr) %}

View file

@ -1,6 +1,6 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}Login to Witch Watch, Bish{% endblock %} {% block title %}Login to What 2 Watch, Bish{% endblock %}
{% block header %}{% endblock %} {% block header %}{% endblock %}

View file

@ -1,6 +1,6 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}Logout of Witch Watch, Bish{% endblock %} {% block title %}Logout of What 2 Watch, Bish{% endblock %}
{% block header %}{% endblock %} {% block header %}{% endblock %}

View file

@ -1,6 +1,6 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}Thanks for Signing Up for Witch Watch, Bish{% endblock %} {% block title %}Thanks for Signing Up for What 2 Watch, Bish{% endblock %}
{% block header %}{% endblock %} {% block header %}{% endblock %}

View file

@ -1,7 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% import "macros.html" as m %} {% import "macros.html" as m %}
{% block title %}Welcome to Witch Watch, Bish{% endblock %} {% block title %}Welcome to What 2 Watch, Bish{% endblock %}
{% block content %} {% block content %}

View file

@ -1,7 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% import "macros.html" as m %} {% import "macros.html" as m %}
{% block title %}Welcome to Witch Watch, Bish{% endblock %} {% block title %}Welcome to What 2 Watch, Bish{% endblock %}
{% block content %} {% block content %}

View file

@ -1,6 +1,6 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}Sign Up for Witch Watch, Bish{% endblock %} {% block title %}Sign Up for What 2 Watch, Bish{% endblock %}
{% block header %} {% endblock %} {% block header %} {% endblock %}

View file

@ -1,6 +1,6 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}Thanks for Signing Up for Witch Watch, Bish{% endblock %} {% block title %}Thanks for Signing Up for What 2 Watch, Bish{% endblock %}
{% block content %} {% block content %}
{% block header %}{% endblock %} {% block header %}{% endblock %}