[fix] board about

[add] markup: images, links
[add] redirect to {{ bundlrs }} on homepage
[add] /api/auth/logout
[add] auth picker
This commit is contained in:
hkau 2024-03-30 12:10:57 -04:00
parent ada66bfc64
commit 32162e1635
10 changed files with 121 additions and 22 deletions

View File

@ -3,7 +3,7 @@ name = "puffer"
authors = ["hkau"]
license = "MIT"
version = "0.1.0"
version = "0.1.1"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,4 +1,6 @@
use actix_web::{get, web, HttpResponse, Responder};
use actix_web::{get, web, HttpRequest, HttpResponse, Responder};
use crate::db::pufferdb::AppData;
#[derive(Default, PartialEq, serde::Deserialize)]
pub struct CallbackQueryProps {
@ -27,3 +29,27 @@ pub async fn callback_request(info: web::Query<CallbackQueryProps>) -> impl Resp
</head>",
);
}
#[get("/api/auth/logout")]
pub async fn logout(req: HttpRequest, data: web::Data<AppData>) -> impl Responder {
let cookie = req.cookie("__Secure-Token");
if cookie.is_none() {
return HttpResponse::NotAcceptable().body("Missing token");
}
let res = data
.db
.get_user_by_unhashed(cookie.unwrap().value().to_string()) // if the user is returned, that means the ID is valid
.await;
if !res.success {
return HttpResponse::NotAcceptable().body("Invalid token");
}
// return
return HttpResponse::Ok()
.append_header(("Set-Cookie", "__Secure-Token=refresh; SameSite=Strict; Secure; Path=/; HostOnly=true; HttpOnly=true; Max-Age=0"))
.append_header(("Content-Type", "text/plain"))
.body("You have been signed out. You can now close this tab.");
}

View File

@ -116,6 +116,7 @@ async fn main() -> std::io::Result<()> {
.service(fs::Files::new("/api/docs", "./target/doc").show_files_listing())
// GET api
.service(crate::api::auth::callback_request)
.service(crate::api::auth::logout)
// GET boards api
.service(crate::api::boards::get_posts_request)
.service(crate::api::boards::get_post_request)

View File

@ -64,6 +64,16 @@ pub fn render(input: &str) -> String {
"<div class=\"mdnote note-$2\"><b class=\"mdnote-title\">$3</b></div>\n",
);
// markdown images (normal)
out = regex_replace(
&out,
r"!\[(.*?)\]\((.*?)\)",
"<img alt=\"$1\" title=\"$1\" src=\"$2\" />",
);
// markdown links
out = regex_replace(&out, r"\[(.*?)\]\((.*?)\)", "<a href=\"$2\">$1</a>");
// some bbcode stuff
out = regex_replace(&out, r"\[b\](.*?)\[/b\]", "<strong>$1</strong>"); // bold
out = regex_replace(&out, r"\[i\](.*?)\[/i\]", "<em>$1</em>"); // italic

View File

@ -29,6 +29,16 @@ struct PasswordPromptTemplate {
board_name: String,
}
#[derive(Template)]
#[template(path = "auth_options.html")]
struct AuthPromptTemplate {
// required fields (super::base)
info: String,
auth_state: bool,
bundlrs: String,
guppy: String,
}
#[derive(Template)]
#[template(path = "boards/view_board.html")]
struct ViewBoardTemplate {
@ -162,13 +172,18 @@ pub async fn new_request(req: HttpRequest, data: web::Data<pufferdb::AppData>) -
if token_user.is_none() {
// you must have an account to create boards
let base = base::get_base_values(token_user.is_some());
let props = AuthPromptTemplate {
// required fields
info: base.info,
auth_state: base.auth_state,
bundlrs: base.bundlrs,
guppy: base.guppy,
};
return HttpResponse::NotFound()
.append_header(("Content-Type", "text/plain"))
.body(
"You must have an account to create a board.
You can login at: /d/auth/login
You can create an account at: /d/auth/register",
);
.append_header(("Content-Type", "text/html"))
.body(props.render().unwrap());
}
// ...
@ -239,8 +254,19 @@ pub async fn view_board_request(
if metadata.is_private == "yes" {
// anonymous
if token_user.is_none() {
// show auth picker
let base = base::get_base_values(token_user.is_some());
let props = AuthPromptTemplate {
// required fields
info: base.info,
auth_state: base.auth_state,
bundlrs: base.bundlrs,
guppy: base.guppy,
};
return HttpResponse::NotFound()
.body("You do not have permission to view this board's contents.");
.append_header(("Content-Type", "text/html"))
.body(props.render().unwrap());
}
// not owner and not included in UserMailStreamIdentifier content (and not staff)
@ -396,8 +422,19 @@ pub async fn create_board_post_request(
if metadata.is_private == "yes" {
// anonymous
if token_user.is_none() {
// show auth picker
let base = base::get_base_values(token_user.is_some());
let props = AuthPromptTemplate {
// required fields
info: base.info,
auth_state: base.auth_state,
bundlrs: base.bundlrs,
guppy: base.guppy,
};
return HttpResponse::NotFound()
.body("You do not have permission to view this board's contents.");
.append_header(("Content-Type", "text/html"))
.body(props.render().unwrap());
}
// not owner and not included in UserMailStreamIdentifier content (and not staff)
@ -885,13 +922,18 @@ pub async fn dashboard_request(
if token_user.is_none() {
// you must have an account to use boards
let base = base::get_base_values(token_user.is_some());
let props = AuthPromptTemplate {
// required fields
info: base.info,
auth_state: base.auth_state,
bundlrs: base.bundlrs,
guppy: base.guppy,
};
return HttpResponse::NotFound()
.append_header(("Content-Type", "text/plain"))
.body(
"You must have an account to use puffer!
You can login at: /d/auth/login
You can create an account at: /d/auth/register",
);
.append_header(("Content-Type", "text/html"))
.body(props.render().unwrap());
}
// fetch boards

View File

@ -0,0 +1,17 @@
{% extends "base.html" %}
{% block title %}Auth Options{% endblock %}
{% block content %}
<div class="flex flex-column g-4">
<main class="small flex flex-column g-4 align-center">
<div class="card secondary round border flex flex-column g-4" style="width: 25rem; max-width: 100dvw;"
id="options">
<a href="{{ guppy }}/d/auth/login" class="button full round puffer-primary"
data-wants-redirect="true">Login</a>
<a href="{{ guppy }}/d/auth/register" class="button full round puffer-primary"
data-wants-redirect="true">Register</a>
</div>
</main>
</div>
{% call super() %}
{% endblock %}

View File

@ -59,7 +59,7 @@
<div class="card secondary full" id="about">
{% if board_m.about.is_some() %}
{% let content = crate::markup::render(board_m.about.as_ref().unwrap()) %}
{content}
{{ content|safe }}
{% endif %}
</div>
</details>

View File

@ -39,9 +39,9 @@
{% let mailstream = crate::db::pufferdb::deserialize_mailstream(board_m.about.as_ref().unwrap()) %}
<!-- show OTHER USER's username if we're in an inbox -->
{% if me == mailstream.user1 %}
mailstream.user2
{{ mailstream.user2 }}
{% else %}
mailstream.user1
{{ mailstream.user1 }}
{% endif %}
{% else %}
{{ board.name }}
@ -84,7 +84,7 @@
{% if board_m.about.is_some() %}
{% let content = crate::markup::render(board_m.about.as_ref().unwrap()) %}
<hr />
{content}
{{ content|safe }}
{% endif %}
<hr />

View File

@ -111,7 +111,7 @@
<div class="card secondary full" id="about">
{% if board_m.about.is_some() %}
{% let content = crate::markup::render(board_m.about.as_ref().unwrap()) %}
{content}
{{ content|safe }}
{% endif %}
</div>
</details>

View File

@ -7,7 +7,10 @@
{% endif %}
{% endblock %}
{% block head %}
<meta http-equiv="refresh" content="0; url={{ bundlrs }}" />
{% endblock %}
{% block content %}
<h1>Puffer!</h1>
{% call super() %}
{% endblock %}