summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--migrations/20231003193749_pinussy.sql6
-rw-r--r--src/main.rs83
-rw-r--r--templates/base.rs.html10
-rw-r--r--templates/login.rs.html7
-rw-r--r--templates/signup.rs.html7
-rw-r--r--templates/users.rs.html2
6 files changed, 94 insertions, 21 deletions
diff --git a/migrations/20231003193749_pinussy.sql b/migrations/20231003193749_pinussy.sql
index 24f3aa1..cba545e 100644
--- a/migrations/20231003193749_pinussy.sql
+++ b/migrations/20231003193749_pinussy.sql
@@ -4,7 +4,7 @@ create type file_type as enum ('image', 'video', 'audio', 'text', 'document', 's
create table users (
id integer primary key generated always as identity,
username varchar(32) not null,
- unique(id, username),
+ unique(username),
password varchar(128) not null,
email varchar(128),
bio text,
@@ -23,7 +23,6 @@ create table sessions (
create table boards (
id integer primary key generated always as identity,
idname varchar(256),
- unique(id, idname),
name varchar(256),
description text,
privacy privacy
@@ -40,7 +39,6 @@ create table board_ownership (
create table pins (
id integer primary key generated always as identity,
idname varchar(256),
- unique(id, idname),
subject varchar(256),
notes text,
privacy privacy
@@ -65,7 +63,6 @@ create table pins_boards (
create table sources (
id integer primary key generated always as identity,
idname varchar(256),
- unique(id, idname),
title varchar(256),
author varchar(256),
url varchar(256),
@@ -85,7 +82,6 @@ create table pin_sources (
create table files (
id integer primary key generated always as identity,
idname varchar(256),
- unique(id, idname),
thumbnail varchar(256),
path varchar(256) not null,
title varchar(256),
diff --git a/src/main.rs b/src/main.rs
index eb7f0e8..c1520ed 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -10,6 +10,7 @@ use actix_web::middleware::{ErrorHandlerResponse, ErrorHandlers};
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder, ResponseError};
use bcrypt::{hash, verify, DEFAULT_COST};
use serde::Deserialize;
+use sqlx::postgres::PgDatabaseError;
use sqlx::{postgres::PgPoolOptions, Pool, Postgres};
use templates::statics::StaticFile;
@@ -67,7 +68,7 @@ async fn home() -> HttpResponse {
#[get("/signup")]
async fn get_signup() -> HttpResponse {
- HttpResponse::Ok().body(render!(templates::signup_html).unwrap())
+ HttpResponse::Ok().body(render!(templates::signup_html, None).unwrap())
}
#[derive(Deserialize)]
@@ -82,31 +83,78 @@ async fn post_signup(
form: web::Form<SignupForm>,
) -> Result<HttpResponse> {
let password_hash = hash(&form.password, DEFAULT_COST)?;
- sqlx::query!(
+ match sqlx::query!(
"insert into users(username, password) values ($1, $2)",
&form.username,
password_hash
)
.execute(&state.db)
- .await?;
- Ok(HttpResponse::Ok().body(render!(templates::signup_html).unwrap()))
+ .await
+ {
+ Ok(_) => {
+ return Ok(HttpResponse::Ok().body(
+ render!(
+ templates::signup_html,
+ Some(Notification {
+ kind: NotificationKind::Info,
+ message: format!("you have successfully registered as {}", &form.username)
+ })
+ )
+ .unwrap(),
+ ))
+ }
+ Err(e) => {
+ match e {
+ sqlx::Error::Database(e) => {
+ if e.is_unique_violation() {
+ return Ok(HttpResponse::Conflict().body(
+ render!(
+ templates::signup_html,
+ Some(Notification {
+ kind: NotificationKind::Error,
+ message: format!(
+ "error: the username \"{}\" already exists",
+ &form.username
+ )
+ })
+ )
+ .unwrap(),
+ ));
+ }
+ }
+ // TODO: log error
+ _ => {}
+ }
+ return Ok(HttpResponse::InternalServerError().body(
+ render!(
+ templates::signup_html,
+ Some(Notification {
+ kind: NotificationKind::Error,
+ message: "there was an internal server error. please try again later."
+ .to_owned()
+ })
+ )
+ .unwrap(),
+ ));
+ }
+ };
}
#[get("/login")]
async fn get_login() -> HttpResponse {
- HttpResponse::Ok().body(render!(templates::login_html).unwrap())
+ HttpResponse::Ok().body(render!(templates::login_html, None).unwrap())
}
#[derive(Deserialize)]
struct LoginForm {
username: String,
password: String,
- rememberme: bool,
+ rememberme: Option<String>,
}
#[post("/login")]
async fn post_login(form: web::Form<LoginForm>) -> Result<HttpResponse> {
- Ok(HttpResponse::Ok().body(render!(templates::login_html).unwrap()))
+ Ok(HttpResponse::Ok().body(render!(templates::login_html, None).unwrap()))
}
#[derive(sqlx::Type)]
@@ -129,6 +177,27 @@ pub struct User {
admin: bool,
}
+pub enum NotificationKind {
+ Info,
+ Warning,
+ Error,
+}
+
+impl std::fmt::Display for NotificationKind {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ NotificationKind::Info => f.write_str("info"),
+ NotificationKind::Warning => f.write_str("warning"),
+ NotificationKind::Error => f.write_str("error"),
+ }
+ }
+}
+
+pub struct Notification {
+ kind: NotificationKind,
+ message: String,
+}
+
#[get("/users")]
async fn get_users(state: web::Data<Pinussy>) -> Result<HttpResponse> {
let users: Vec<User> = sqlx::query_as("select * from users")
diff --git a/templates/base.rs.html b/templates/base.rs.html
index 5b1a324..64a8d4e 100644
--- a/templates/base.rs.html
+++ b/templates/base.rs.html
@@ -1,6 +1,7 @@
@use super::statics::*;
+@use crate::Notification;
-@(authenticated: bool, body: Content)
+@(authenticated: bool, notification: Option<Notification>, body: Content)
<!DOCTYPE html>
<html>
@@ -15,11 +16,16 @@
<body>
<nav>
@if authenticated {
- logout
+ <form action="/logout" method="post">
+ <button type="submit">logout</button>
+ </form>
} else {
<a href="/login">log in</a>
}
</nav>
+ @if let Some(notification) = notification {
+ <div class="notification @notification.kind">@notification.message</div>
+ }
@:body()
</body>
diff --git a/templates/login.rs.html b/templates/login.rs.html
index 349263c..c87866c 100644
--- a/templates/login.rs.html
+++ b/templates/login.rs.html
@@ -1,8 +1,9 @@
@use super::base_html;
+@use crate::Notification;
-@()
+@(notification: Option<Notification>)
-@:base_html(false, {
+@:base_html(false, None, {
<form action="/login" method="post">
<label for="username">username:</label>
<input type="text" id="username" name="username" required="true" />
@@ -12,4 +13,4 @@
<input type="checkbox" id="rememberme" name="rememberme" />
<button type="submit">log in</button>
</form>
-}) \ No newline at end of file
+})
diff --git a/templates/signup.rs.html b/templates/signup.rs.html
index 354dae6..a482fc2 100644
--- a/templates/signup.rs.html
+++ b/templates/signup.rs.html
@@ -1,8 +1,9 @@
@use super::base_html;
+@use crate::Notification;
-@()
+@(notification: Option<Notification>)
-@:base_html(false, {
+@:base_html(false, notification, {
<form action="/signup" method="post">
<label for="username">username:</label>
<input type="text" id="username" name="username" required="true" />
@@ -10,4 +11,4 @@
<input type="text" id="password" name="password" required="true" />
<button type="submit">sign up</button>
</form>
-}) \ No newline at end of file
+})
diff --git a/templates/users.rs.html b/templates/users.rs.html
index 9b9be30..ab1ab0f 100644
--- a/templates/users.rs.html
+++ b/templates/users.rs.html
@@ -3,7 +3,7 @@
@(users: Vec<User>)
-@:base_html(false, {
+@:base_html(false, None, {
<ul>
@for user in users {
<li>@user.username</li>