doesn't not work

This commit is contained in:
Joe Ardent 2024-02-25 16:34:32 -08:00
parent ff673840e3
commit a31b41cb9e
13 changed files with 1563 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

4
.rustfmt.toml Normal file
View File

@ -0,0 +1,4 @@
imports_granularity = "Crate"
group_imports = "StdExternalCrate"
wrap_comments = true
edition = "2021"

1223
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

15
Cargo.toml Normal file
View File

@ -0,0 +1,15 @@
[package]
name = "queenie"
version = "0.1.0"
edition = "2021"
[dependencies]
askama = { version = "0.12", default-features = false, features = ["with-axum", "serde"] }
askama_axum = { version = "0.4.0", default-features = false }
axum = { version = "0.7", default-features = false, features = ["tokio", "http1", "form"] }
justerror = { version = "1" }
serde = { version = "1", default-features = false, features = ["derive"] }
thiserror = { version = "1" }
tokio = { version = "1", default-features = false, features = ["rt-multi-thread"] }
tower-sessions = { version = "0.10", default-features = false, features = ["axum-core", "memory-store"] }
unicode-segmentation = { version = "1", default-features = false }

1
assets/htmx.min.js vendored Normal file

File diff suppressed because one or more lines are too long

40
assets/ww.css Normal file
View File

@ -0,0 +1,40 @@
body {
background-color: darkgrey
}
table {
border-collapse: collapse;
width: 100%;
}
th {
background-color: ghostwhite;
}
th, td {
text-align: left;
padding: 8px;
}
tr:nth-child(even) {background-color: ghostwhite;}
#header {
text-align: end;
display: flex;
flex-direction: row;
justify-content: flex-end;
}
.warning {
background-color: goldenrod;
}
.watchtitle {
font-style: italic;
}
.header_logged_in {
display: flex;
flex-direction: column;
}

156
src/main.rs Normal file
View File

@ -0,0 +1,156 @@
use std::{
fmt::{Debug, Display},
net::SocketAddr,
};
use askama::Template;
use axum::{
extract::{Form, Path},
http::StatusCode,
response::{IntoResponse, Response},
routing::get,
Router,
};
use serde::{Deserialize, Serialize};
use tower_sessions::{MemoryStore, Session, SessionManagerLayer};
#[macro_use]
extern crate justerror;
const SIGNUP_KEY: &str = "meow";
#[derive(Default, Deserialize, Serialize)]
struct Counter(usize);
/// Displays the signup form.
async fn get_signup() -> impl IntoResponse {
SignupPage::default()
}
/// Receives the form with the user signup fields filled out.
async fn post_signup(session: Session, Form(form): Form<SignupForm>) -> impl IntoResponse {
todo!()
}
/// Called from Stripe with the receipt of payment.
async fn signup_success(session: Session, receipt: Option<Path<String>>) -> impl IntoResponse {
todo!()
}
#[tokio::main]
async fn main() {
let session_store = MemoryStore::default();
let session_layer = SessionManagerLayer::new(session_store).with_secure(false);
let app = Router::new()
//.nest_service("/assets", assets_svc)
.route("/signup", get(get_signup).post(post_signup))
.route("/signup_success/:receipt", get(signup_success))
.layer(session_layer);
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
axum::serve(listener, app.into_make_service())
.await
.unwrap();
}
#[Error(desc = "Could not create user.")]
#[non_exhaustive]
pub struct CreateUserError(#[from] CreateUserErrorKind);
impl IntoResponse for CreateUserError {
fn into_response(self) -> Response {
(StatusCode::FORBIDDEN, format!("{:?}", self.0)).into_response()
}
}
#[Error]
#[non_exhaustive]
pub enum CreateUserErrorKind {
AlreadyExists,
#[error(desc = "Usernames must be between 1 and 20 characters long")]
BadUsername,
PasswordMismatch,
#[error(desc = "Password must have at least 4 and at most 50 characters")]
BadPassword,
#[error(desc = "Display name must be less than 100 characters long")]
BadDisplayname,
BadEmail,
BadPayment,
}
#[derive(Debug, Default, Deserialize, PartialEq, Eq)]
pub struct SignupForm {
pub username: String,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub displayname: Option<String>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub email: Option<String>,
pub password: String,
pub pw_verify: String,
pub invitation: String,
}
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct User {
pub username: String,
pub displayname: Option<String>,
pub email: Option<String>,
pub password: String,
pub pw_verify: String,
}
impl Debug for User {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let pw_check = if self.password == self.pw_verify {
"password matched"
} else {
"PASSWORD MISMATCH"
};
f.debug_struct("User")
.field("username", &self.username)
.field("displayname", &self.displayname)
.field("email", &self.email)
.field("pw-check", &pw_check)
.finish()
}
}
impl Display for User {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let uname = &self.username;
let dname = if let Some(ref n) = self.displayname {
n
} else {
""
};
let email = if let Some(ref e) = self.email { e } else { "" };
write!(f, "Username: {uname}\nDisplayname: {dname}\nEmail: {email}")
}
}
pub(crate) fn empty_string_as_none<'de, D, T>(de: D) -> Result<Option<T>, D::Error>
where
D: serde::Deserializer<'de>,
T: std::str::FromStr,
T::Err: std::fmt::Display,
{
let opt = <Option<String> as serde::Deserialize>::deserialize(de)?;
match opt.as_deref() {
None | Some("") => Ok(None),
Some(s) => std::str::FromStr::from_str(s)
.map_err(serde::de::Error::custom)
.map(Some),
}
}
#[derive(Debug, Default, Template, Deserialize, Serialize, PartialEq, Eq)]
#[template(path = "signup.html")]
pub struct SignupPage {
pub username: String,
pub displayname: Option<String>,
pub email: Option<String>,
pub password: String,
pub pw_verify: String,
}

29
templates/base.html Normal file
View File

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}{{ title }} - What 2 Watch{% endblock %}</title>
<link rel="stylesheet" href="/assets/ww.css">
{% block head %}{% endblock %}
</head>
<body>
<div id="header">
{% block header %}{% endblock %}
</div>
<div id="content">
<hr />
{% block content %}{% endblock %}
</div>
<div id="footer">
{% block footer %}{% endblock %}
</div>
<script src="/assets/htmx.min.js"></script>
</body>
</html>

26
templates/index.html Normal file
View File

@ -0,0 +1,26 @@
{% extends "base.html" %}
{% block title %}Welcome to What 2 Watch, Bish{% endblock %}
{% block content %}
<h1>Welcome to What 2 Watch</h1>
{% match user %}
{% when Some with (usr) %}
<p>
Hello, {{ usr.username }}! It's nice to see you. <a href="watches">Let's get watchin'!</a>
</p>
</br>
<p>
<form action="/logout" enctype="application/x-www-form-urlencoded" method="post">
<input type="submit" value="sign out?">
</form>
</p>
{% when None %}
<p>
Heya, why don't you <a href="/login">log in</a> or <a href="/signup">sign up</a>?
</p>
{% endmatch %}
{% endblock %}

10
templates/macros.html Normal file
View File

@ -0,0 +1,10 @@
{% macro get_or_default(val, def) %}
{% match val %}
{% when Some with (v) %}
{{v}}
{% else %}
{{def}}
{% endmatch %}
{% endmacro %}

25
templates/signup.html Normal file
View File

@ -0,0 +1,25 @@
{% extends "base.html" %}
{% block title %}Welcome, friend, to git.kittenclause.com{% endblock %}
{% block header %} {% endblock %}
{% block content %}
<p>
<form action="/signup" enctype="application/x-www-form-urlencoded" method="post">
<label for="username">Username</label>
<input type="text" name="username" id="username" minlength="1" maxlength="20" required></br>
<label for="displayname">Displayname (optional)</label>
<input type="text" name="displayname" id="displayname"></br>
<label for="email">Email (optional)</label>
<input type="text" name="email"></br>
<label for="password">Password</label>
<input type="password" name="password" id="password" required></br>
<label for="confirm_password">Confirm Password</label>
<input type="password" name="pw_verify" id="pw_verify" required></br>
<input type="submit" value="Signup">
</form>
</p>
{% endblock %}

View File

@ -0,0 +1,16 @@
{% extends "base.html" %}
{% block title %}Dang, Bish{% endblock %}
{% block content %}
{% block header %}{% endblock %}
<h1>Oh dang!</h1>
<div id="signup_success">
<p>
Sorry, something went wrong: {{self.0}}
</p>
</div>
{% endblock %}

View File

@ -0,0 +1,17 @@
{% extends "base.html" %}
{% block title %}Thanks for Signing Up for What 2 Watch, Bish{% endblock %}
{% block content %}
{% block header %}{% endblock %}
<h1>You did it!</h1>
<div id="signup_success"><p>
{{ self.0 }}
</p>
</div>
<p>Now, head on over to <a href="/login">the login page</a> and get watchin'!</p>
{% endblock %}