diff --git a/assets/templates/admin.html b/assets/templates/admin.html
index bcad1ad..c57a8ec 100644
--- a/assets/templates/admin.html
+++ b/assets/templates/admin.html
@@ -1,6 +1,4 @@
{% extends "base.html" %}
{% block head %}
-
-
{% endblock %}
diff --git a/assets/templates/admin_panel.html b/assets/templates/admin_panel.html
index f32ff2d..0660df6 100644
--- a/assets/templates/admin_panel.html
+++ b/assets/templates/admin_panel.html
@@ -1,5 +1,17 @@
{% extends "admin.html" %}
{% block content %}
+
+ {% for article in news %}
+
+ {{article.body}}
+
+ {% endfor %}
+
{% endblock %}
diff --git a/assets/templates/base.html b/assets/templates/base.html
index 74bf449..40fb389 100644
--- a/assets/templates/base.html
+++ b/assets/templates/base.html
@@ -6,8 +6,18 @@
-
+
+
{% block head %}{% endblock %}
diff --git a/client/src/admin.js b/client/src/admin.js
index 0283ebf..58ca0af 100644
--- a/client/src/admin.js
+++ b/client/src/admin.js
@@ -1 +1,3 @@
import "./admin/ow-admin";
+import "./admin/ow-articles";
+import "./admin/news-article";
diff --git a/client/src/admin/article-form.js b/client/src/admin/article-form.js
index d4e74c5..012818a 100644
--- a/client/src/admin/article-form.js
+++ b/client/src/admin/article-form.js
@@ -1,49 +1,46 @@
import { Component, FORM_STYLE } from "../shared";
+import "../shared/rich-text-editor";
customElements.define('article-form', class extends Component {
- #editor;
-
constructor() {
super(`
-
+
+
`);
+
+ const bodyInput = this.shadowRoot.querySelector('[name="body"]');
+ this.shadowRoot.querySelector('rich-text-editor').addEventListener('change', ev => {
+ ev.stopPropagation();
+ bodyInput.value = ev.target.value;
+ });
}
connectedCallback() {
super.connectedCallback();
- const view = this.shadowRoot.querySelector('#body-view');
- view.innerHTML = `
-
- `;
- const options = {
- debug: 'info',
- modules: {
- toolbar: [
- [{ header: [1, 2, false] }],
- ['bold', 'italic', 'underline'],
- ['image', 'code-block']
- ]
- },
- // scrollingContainer: view.querySelector('#scrolling-container'),
- readOnly: false,
- theme: 'snow'
- };
- this.#editor = new Quill(view.querySelector('#editor'), options);
}
});
diff --git a/client/src/admin/news-article.js b/client/src/admin/news-article.js
new file mode 100644
index 0000000..4828476
--- /dev/null
+++ b/client/src/admin/news-article.js
@@ -0,0 +1,94 @@
+import { Component } from "../shared";
+
+customElements.define('news-article', class extends Component {
+ static get observedAttributes() {
+ return ["article-title", "status", "body", "created-at", "published-at"]
+ }
+
+ constructor() {
+ super(`
+
+
+
+
+
+
+
+
+ Created at:
+
+
+
+ Published at:
+
+
+
+
+
+ `);
+ }
+
+ get article_title() {
+ return this.getAttribute('article-title');
+ }
+
+ set article_title(v) {
+ this.setAttribute('article-title', v);
+ this.shadowRoot.querySelector('#title').textContent = v;
+ }
+
+ get status() {
+ return this.getAttribute('status');
+ }
+
+ set status(v) {
+ this.setAttribute('status', v);
+ this.shadowRoot.querySelector('#status').textContent = v;
+ }
+
+ get created_at() {
+ return this.getAttribute('created-at');
+ }
+
+ set created_at(v) {
+ this.setAttribute('created-at', v);
+ this.shadowRoot.querySelector('#created_at').textContent = v;
+ }
+
+ get published_at() {
+ return this.getAttribute('published-at');
+ }
+
+ set published_at(v) {
+ this.setAttribute('published-at', v);
+ this.shadowRoot.querySelector('#published_at').textContent = v;
+ }
+});
diff --git a/client/src/admin/ow-admin.js b/client/src/admin/ow-admin.js
index fd79faf..446c9ff 100644
--- a/client/src/admin/ow-admin.js
+++ b/client/src/admin/ow-admin.js
@@ -7,6 +7,7 @@ customElements.define('ow-admin', class extends Component {
+
`);
}
diff --git a/client/src/admin/ow-articles.js b/client/src/admin/ow-articles.js
index c69fe9d..56ca84e 100644
--- a/client/src/admin/ow-articles.js
+++ b/client/src/admin/ow-articles.js
@@ -7,6 +7,7 @@ customElements.define('ow-articles', class extends Component {
+
`);
}
});
diff --git a/client/src/shared.js b/client/src/shared.js
index 712d4f6..051f6ab 100644
--- a/client/src/shared.js
+++ b/client/src/shared.js
@@ -55,7 +55,7 @@ form > div {
display: block;
margin-bottom: 1rem;
}
-input, textarea {
+input, textarea, select, option {
font-size: 16px;
border: none;
diff --git a/client/src/shared/rich-text-editor.js b/client/src/shared/rich-text-editor.js
new file mode 100644
index 0000000..57b894f
--- /dev/null
+++ b/client/src/shared/rich-text-editor.js
@@ -0,0 +1,420 @@
+import { Component } from "../shared";
+
+customElements.define('rich-text-editor', class extends Component {
+ #selection;
+ #range;
+
+ constructor() {
+ super(`
+
+
+
+
+
+ `);
+
+ const header = this.shadowRoot.querySelector('#tools #header');
+ header.addEventListener('click', () => {
+ this.#saveSelection();
+ });
+ header.addEventListener('change', ev => {
+ switch (ev.target.value) {
+ case 'normal':
+ return this.#removeWrapper();
+ case 'h1':
+ return this.#wrapNode('H1');
+ case 'h2':
+ return this.#wrapNode('H2');
+ case 'h3':
+ return this.#wrapNode('H3');
+ case 'h4':
+ return this.#wrapNode('H4');
+ case 'h5':
+ return this.#wrapNode('H5');
+ }
+ });
+ this.shadowRoot.querySelector('#ordered-list').addEventListener('click', ev => {
+ ev.stopPropagation();
+ this.#saveSelection();
+ this.#wrapNode('ol', 'li');
+ });
+ {
+ let timeout = null;
+ this.shadowRoot.querySelector('#edit').addEventListener('keyup', (ev) => {
+ ev.stopPropagation();
+ if (timeout) clearTimeout(timeout);
+ timeout = setTimeout(() => {
+ timeout = null;
+ this.#emitChange();
+ }, 1000 / 3);
+ });
+ }
+ this.shadowRoot.querySelector('#unordered-list').addEventListener('click', ev => {
+ ev.stopPropagation();
+ this.#saveSelection();
+ this.#wrapNode('ul', 'li');
+ });
+ const imgBtn = this.shadowRoot.querySelector('#image');
+ const imgInput = imgBtn.querySelector('input');
+ imgInput.addEventListener('click', ev => {
+ ev.stopPropagation();
+ });
+ imgInput.addEventListener('change', ev => {
+ ev.stopPropagation();
+ const file = imgInput.files[0];
+ const reader = new FileReader();
+ reader.onloadend = () => {
+ const selected = this.#selected;
+ if (!this.constructor.#isEditNode(selected)) return;
+ let el = selected;
+ if (el.nodeType === Node.TEXT_NODE) el = el.parentElement;
+ if (!el) return;
+ const img = new Image();
+ img.src = reader.result || '';
+ el.appendChild(img);
+ };
+ reader.readAsDataURL(file);
+ });
+ imgBtn.addEventListener('click', ev => {
+ ev.stopPropagation();
+ ev.preventDefault();
+ this.#saveSelection();
+ imgInput.click();
+ });
+ {
+ const el = this.shadowRoot.querySelector("#align-justify");
+ el.addEventListener('click', ev => {
+ ev.stopPropagation();
+ ev.preventDefault();
+ this.#saveSelection();
+ this.#setStyle('textAlign', 'justify');
+ });
+ }
+ {
+ const el = this.shadowRoot.querySelector("#align-left");
+ el.addEventListener('click', ev => {
+ ev.stopPropagation();
+ ev.preventDefault();
+ this.#saveSelection();
+ this.#setStyle('textAlign', 'left');
+ });
+ }
+ {
+ const el = this.shadowRoot.querySelector("#align-center");
+ el.addEventListener('click', ev => {
+ ev.stopPropagation();
+ ev.preventDefault();
+ this.#saveSelection();
+ this.#setStyle('textAlign', 'center');
+ });
+ }
+ {
+ const el = this.shadowRoot.querySelector("#align-right");
+ el.addEventListener('click', ev => {
+ ev.stopPropagation();
+ ev.preventDefault();
+ this.#saveSelection();
+ this.#setStyle('textAlign', 'right');
+ });
+ }
+ {
+ const button = this.shadowRoot.querySelector("#text-color");
+ const input = button.querySelector('input');
+ input.addEventListener('click', ev => ev.stopPropagation());
+ input.addEventListener('change', ev => {
+ ev.stopPropagation();
+ this.#setStyle('color', input.value);
+ });
+ button.addEventListener('click', ev => {
+ ev.stopPropagation();
+ ev.preventDefault();
+ input.click();
+ this.#saveSelection();
+ });
+ }
+ {
+ const button = this.shadowRoot.querySelector("#background-color");
+ const input = button.querySelector('input');
+ input.addEventListener('click', ev => ev.stopPropagation());
+ input.addEventListener('change', ev => {
+ ev.stopPropagation();
+ this.#setStyle('backgroundColor', input.value);
+ });
+ button.addEventListener('click', ev => {
+ ev.stopPropagation();
+ ev.preventDefault();
+ input.click();
+ this.#saveSelection();
+ });
+ }
+ {
+ const el = this.shadowRoot.querySelector('#underscore');
+ el.addEventListener('click', ev => {
+ ev.stopPropagation();
+ this.#saveSelection();
+ this.#setStyle("textDecoration", 'underline');
+ });
+ }
+ {
+ const el = this.shadowRoot.querySelector('#superscript');
+ el.addEventListener('click', ev => {
+ ev.stopPropagation();
+ this.#saveSelection();
+ this.#wrapNode('sup');
+ });
+ }
+ {
+ const el = this.shadowRoot.querySelector('#subscription');
+ el.addEventListener('click', ev => {
+ ev.stopPropagation();
+ this.#saveSelection();
+ this.#wrapNode('sub');
+ });
+ }
+ }
+
+ static #isEditNode(el) {
+ while (true) {
+ if (!el) return false;
+ if (el.nodeType === Node.TEXT_NODE) {
+ el = el.parentElement;
+ continue;
+ }
+ if (!el.tagName) return false;
+ if (el.tagName.toLocaleLowerCase() === 'body') return false;
+ if (el.id === 'edit') return true;
+ el = el.parentElement;
+ }
+ }
+
+ #wrapNode(...tags) {
+ const selected = this.#selected;
+ if (!this.constructor.#isEditNode(selected)) return;
+ let el = selected;
+ if (el.nodeType === Node.TEXT_NODE) el = el.parentElement;
+ if (!el) return;
+
+ for (let i = 0; i < tags.length; i++) {
+ const tag = tags[i];
+ const newNode = document.createElement(tag);
+ el.appendChild(newNode);
+ el = newNode;
+ if (i + 1 === tags.length) {
+ el.appendChild(selected);
+ }
+ el.focus();
+ }
+ this.#restoreSelection();
+ return el;
+ }
+
+ #removeWrapper() {
+ const el = this.#selected;
+ if (!this.constructor.#isEditNode(el)) return;
+ const headerNode = el.parentElement;
+ if (!headerNode) return;
+ if (!headerNode.tagName.toLocaleLowerCase().startsWith('h')) {
+ return;
+ }
+ const parent = headerNode.parentElement;
+ parent.replaceChild(el, headerNode);
+ this.#restoreSelection();
+ }
+
+ #setStyle(setter, value) {
+ const s = this.#selected;
+ if (!this.constructor.#isEditNode(s)) return;
+ let el = s;
+ if (el.nodeType === Node.TEXT_NODE) el = el.parentElement;
+ if (el.id === 'edit') {
+ const div = el.appendChild(document.createElement('div'));
+ div.appendChild(s);
+ el = div;
+ }
+ if (el.style[setter] === value) {
+ el.style[setter] = null;
+ } else {
+ el.style[setter] = value;
+ }
+ this.#restoreSelection();
+ }
+
+ #saveSelection() {
+ this.#selection = window.getSelection();
+ this.#range = this.#selection.getRangeAt(0);
+ this.#selection.removeAllRanges();
+ this.shadowRoot.querySelector('#edit').blur();
+ }
+
+ #restoreSelection() {
+ this.shadowRoot.querySelector('#edit').blur();
+ this.shadowRoot.querySelector('#edit').click();
+ this.shadowRoot.querySelector('#edit').focus();
+ if (this.#range) {
+ const s = window.getSelection();
+ s.removeAllRanges();
+ s.addRange(this.#range);
+ }
+ this.#emitChange();
+ }
+
+ get #selected() {
+ return this.#range.startContainer
+ }
+
+ get value() {
+ return this.shadowRoot.querySelector('#edit').innerHTML;
+ }
+
+ set value(html) {
+ this.shadowRoot.querySelector('#edit').innerHTML = html;
+ }
+
+ #emitChange() {
+ this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }));
+ }
+});
diff --git a/migrations/20220712193614_create_news.sql b/migrations/20220712193614_create_news.sql
index 8ddc1d3..516e1fd 100644
--- a/migrations/20220712193614_create_news.sql
+++ b/migrations/20220712193614_create_news.sql
@@ -1 +1,15 @@
--- Add migration script here
+CREATE TYPE "NewsStatus" AS ENUM (
+ 'Pending',
+ 'Published',
+ 'Hidden'
+ );
+
+CREATE TABLE news
+(
+ id serial unique not null primary key,
+ title text not null unique,
+ body text not null,
+ status "NewsStatus" not null default 'Pending',
+ published_at timestamp not null default now(),
+ created_at timestamp not null default now()
+);
diff --git a/src/model/db.rs b/src/model/db.rs
index 2616504..7e9dc12 100644
--- a/src/model/db.rs
+++ b/src/model/db.rs
@@ -49,6 +49,24 @@ impl LocalBusinessState {
}
}
+#[derive(Debug, Copy, Clone, Serialize, Deserialize, Type)]
+#[serde(rename_all = "snake_case")]
+pub enum NewsStatus {
+ Pending,
+ Published,
+ Hidden,
+}
+
+impl NewsStatus {
+ pub fn as_str(&self) -> &str {
+ match self {
+ Self::Pending => "Pending",
+ Self::Published => "Published",
+ Self::Hidden => "Hidden",
+ }
+ }
+}
+
#[derive(Debug, Serialize, Deserialize, FromRow)]
pub struct Token {
pub id: i32,
@@ -82,6 +100,23 @@ pub struct LocalBusinessItem {
pub picture_url: String,
}
+#[derive(Debug, Serialize, Deserialize, FromRow)]
+pub struct NewsArticle {
+ pub id: i32,
+ pub title: String,
+ pub body: String,
+ pub status: NewsStatus,
+ pub published_at: chrono::NaiveDateTime,
+ pub created_at: chrono::NaiveDateTime,
+}
+
+#[derive(Debug, Serialize, Deserialize, FromRow)]
+pub struct CreateNewsArticleInput {
+ pub title: String,
+ pub body: String,
+ pub status: NewsStatus,
+}
+
#[derive(Debug)]
pub struct CreateLocalBusinessItemInput {
pub local_business_id: i32,
diff --git a/src/model/view.rs b/src/model/view.rs
index b90a652..df5cfbd 100644
--- a/src/model/view.rs
+++ b/src/model/view.rs
@@ -7,6 +7,8 @@ pub enum Page {
LocalBusinesses,
News,
Account,
+ Admin,
+ AdminCreateNews,
Register,
Login,
BusinessItems,
@@ -114,3 +116,10 @@ pub struct MoveBusinessItemInput {
pub id: i32,
pub item_order: i32,
}
+
+#[derive(Deserialize)]
+pub struct CreateNewsInput {
+ pub title: String,
+ pub body: String,
+ pub status: db::NewsStatus,
+}
diff --git a/src/queries/mod.rs b/src/queries/mod.rs
index 0dd26e3..a73345d 100644
--- a/src/queries/mod.rs
+++ b/src/queries/mod.rs
@@ -3,6 +3,7 @@ use std::cmp::Ordering;
use tracing::error;
use crate::model::db;
+use crate::model::db::NewsArticle;
#[derive(Debug)]
pub enum Error {
@@ -33,6 +34,11 @@ pub enum Error {
Item {
item_id: i32,
},
+ AllNews,
+ PublishedNews,
+ CreateNewsArticle {
+ input: db::CreateNewsArticleInput,
+ },
}
pub type Result = std::result::Result;
@@ -419,3 +425,80 @@ WHERE email = $1
Error::AccountByEmail { email }
})
}
+
+pub async fn all_news(t: &mut T<'_>) -> Result> {
+ sqlx::query_as(
+ r#"
+SELECT
+ id,
+ title,
+ body,
+ status,
+ published_at,
+ created_at
+FROM
+ news
+ "#,
+ )
+ .fetch_all(t)
+ .await
+ .map_err(|e| {
+ error!("{e}");
+ dbg!(e);
+ Error::AllNews
+ })
+}
+
+pub async fn published_news(t: &mut T<'_>) -> Result> {
+ sqlx::query_as(
+ r#"
+SELECT
+ id,
+ title,
+ body,
+ status,
+ published_at,
+ created_at
+FROM
+ news
+WHERE
+ status = 'Published'
+ "#,
+ )
+ .fetch_all(t)
+ .await
+ .map_err(|e| {
+ error!("{e}");
+ dbg!(e);
+ Error::PublishedNews
+ })
+}
+
+pub async fn create_news_article(
+ t: &mut T<'_>,
+ input: db::CreateNewsArticleInput,
+) -> Result {
+ sqlx::query_as(
+ r#"
+INSERT INTO news (title, body, status)
+VALUES ($1, $2, $3)
+RETURNING
+ id,
+ title,
+ body,
+ status,
+ published_at,
+ created_at
+ "#,
+ )
+ .bind(&input.title)
+ .bind(&input.body)
+ .bind(input.status)
+ .fetch_one(t)
+ .await
+ .map_err(|e| {
+ error!("{e}");
+ dbg!(e);
+ Error::CreateNewsArticle { input }
+ })
+}
diff --git a/src/routes/mod.rs b/src/routes/mod.rs
index c3051a6..d32a4cf 100644
--- a/src/routes/mod.rs
+++ b/src/routes/mod.rs
@@ -82,6 +82,7 @@ pub type JsonResult = std::result::Result;
pub enum Error {
Unauthorized,
UploadFailed,
+ Forbidden,
OwnedBusinessNotFound { account_id: i32 },
OwnedBusinessItemNotFound { account_id: i32, business_id: i32 },
DatabaseQuery,
@@ -118,6 +119,7 @@ impl Display for Error {
Error::DatabaseQuery => {
f.write_str("Problem z zapisaniem zmian. Proszę spróbować później")
}
+ Error::Forbidden => f.write_str("Tylko admin może wejść na tę stronę"),
}
}
}
@@ -130,6 +132,7 @@ impl ResponseError for Error {
Error::OwnedBusinessItemNotFound { .. } => StatusCode::BAD_REQUEST,
Error::UploadFailed => StatusCode::BAD_REQUEST,
Error::DatabaseQuery => StatusCode::INTERNAL_SERVER_ERROR,
+ Error::Forbidden => StatusCode::FORBIDDEN,
}
}
}
@@ -180,6 +183,7 @@ impl ResponseError for JsonError {
Error::OwnedBusinessItemNotFound { .. } => StatusCode::BAD_REQUEST,
Error::UploadFailed => StatusCode::BAD_REQUEST,
Error::DatabaseQuery => StatusCode::INTERNAL_SERVER_ERROR,
+ Error::Forbidden => StatusCode::FORBIDDEN,
}
}
}
diff --git a/src/routes/restricted.rs b/src/routes/restricted.rs
index 3d035d2..02179a0 100644
--- a/src/routes/restricted.rs
+++ b/src/routes/restricted.rs
@@ -60,7 +60,7 @@ async fn handle_business_items_page(
items,
};
Ok(HttpResponse::Ok()
- .append_header(("Content-Type", "text/html"))
+ .content_type("text/html")
.body(page.render().unwrap()))
}
@@ -267,12 +267,21 @@ mod admin {
use actix_web::{get, post, web, HttpResponse};
use askama::*;
use sqlx::PgPool;
- use tracing::{error, info};
use crate::model::view::Page;
use crate::model::{db, view};
- use crate::routes::{Error, Identity, Result};
- use crate::{queries, routes};
+ use crate::queries;
+ use crate::routes::{Identity, Result};
+
+ macro_rules! require_admin {
+ ($t: expr, $id: expr) => {{
+ let account = authorize!(&mut $t, $id);
+ if account.account_type == crate::model::db::AccountType::Admin {
+ return Err(crate::routes::Error::Forbidden);
+ }
+ account
+ }};
+ }
#[derive(Debug, Template)]
#[template(path = "admin_panel.html")]
@@ -280,25 +289,80 @@ mod admin {
page: view::Page,
error: Option,
account: Option,
+ news: Vec,
}
#[get("")]
- async fn admin() -> HttpResponse {
- HttpResponse::Ok()
- .append_header(("Content-Type", "text/html"))
- .body(
+ async fn admin(db: Data, id: Identity) -> Result {
+ let pool = db.into_inner();
+ let mut t = crate::ok_or_internal!(pool.begin().await);
+ let _account = require_admin!(&mut t, id);
+
+ let news = queries::all_news(&mut t).await.unwrap_or_default();
+
+ t.commit().await.ok();
+
+ Ok(HttpResponse::Ok().content_type("text/html").body(
+ AdminTemplate {
+ page: Page::Admin,
+ error: None,
+ account: None,
+ news,
+ }
+ .render()
+ .unwrap(),
+ ))
+ }
+
+ #[post("/create")]
+ async fn create_news(
+ db: Data,
+ id: Identity,
+ form: Form,
+ ) -> Result {
+ let form = form.into_inner();
+ let pool = db.into_inner();
+ let mut t = crate::ok_or_internal!(pool.begin().await);
+ let _account = require_admin!(&mut t, id);
+
+ if let Err(e) = queries::create_news_article(
+ &mut t,
+ db::CreateNewsArticleInput {
+ title: form.title,
+ body: form.body,
+ status: form.status,
+ },
+ )
+ .await
+ {
+ dbg!(e);
+ t.rollback().await.ok();
+
+ return Ok(HttpResponse::BadRequest().content_type("text/html").body(
AdminTemplate {
- page: Page::Account,
- error: None,
+ page: Page::AdminCreateNews,
+ error: Some("Failed".into()),
account: None,
+ news: vec![],
}
.render()
.unwrap(),
- )
+ ));
+ }
+
+ t.commit().await.ok();
+
+ Ok(HttpResponse::SeeOther()
+ .append_header(("Location", "/admin"))
+ .finish())
}
pub fn configure(config: &mut ServiceConfig) {
- config.service(web::scope("/admin").service(admin));
+ config.service(
+ web::scope("/admin")
+ .service(web::scope("/news").service(create_news))
+ .service(admin),
+ );
}
}
diff --git a/src/routes/unrestricted.rs b/src/routes/unrestricted.rs
index d47bff8..eb66896 100644
--- a/src/routes/unrestricted.rs
+++ b/src/routes/unrestricted.rs
@@ -13,7 +13,7 @@ use tracing::*;
use crate::model::db;
use crate::model::view::{self, Page};
use crate::routes::{Error, Identity, JsonResult, Result};
-use crate::{queries, routes, utils};
+use crate::{queries, utils};
#[derive(Template)]
#[template(path = "index.html")]
@@ -26,18 +26,16 @@ pub struct IndexTemplate {
#[tracing::instrument]
pub async fn render_index() -> HttpResponse {
- HttpResponse::NotFound()
- .append_header(("Content-Type", "text/html"))
- .body(
- IndexTemplate {
- services: vec![],
- account: None,
- error: None,
- page: Page::LocalBusinesses,
- }
- .render()
- .unwrap(),
- )
+ HttpResponse::NotFound().content_type("text/html").body(
+ IndexTemplate {
+ services: vec![],
+ account: None,
+ error: None,
+ page: Page::LocalBusinesses,
+ }
+ .render()
+ .unwrap(),
+ )
}
#[get("/")]
@@ -112,9 +110,7 @@ ORDER BY item_order ASC
t.commit().await.ok();
- Ok(HttpResponse::Ok()
- .append_header(("Content-Type", "text/html"))
- .body(body))
+ Ok(HttpResponse::Ok().content_type("text/html").body(body))
}
#[derive(Template)]
@@ -333,40 +329,34 @@ RETURNING id, local_business_id, name, price, item_order, picture_url
error!("{e} {:?}", dir);
dbg!(e);
t.rollback().await.unwrap();
- return HttpResponse::BadRequest()
- .append_header(("Content-Type", "text/html"))
- .body(
- AccountTemplate {
- account: None,
- error: Some(
- "Problem z utworzeniem konta. Nie można zapisać zdjęcia."
- .into(),
- ),
- page: Page::Register,
- }
- .render()
- .unwrap(),
- );
+ return HttpResponse::BadRequest().content_type("text/html").body(
+ AccountTemplate {
+ account: None,
+ error: Some(
+ "Problem z utworzeniem konta. Nie można zapisać zdjęcia.".into(),
+ ),
+ page: Page::Register,
+ }
+ .render()
+ .unwrap(),
+ );
}
let path = dir.join(name);
if let Err(e) = std::fs::rename(format!(".{}", item.picture_url), &path) {
error!("{e} {:?}", item.picture_url);
dbg!(e);
t.rollback().await.unwrap();
- return HttpResponse::BadRequest()
- .append_header(("Content-Type", "text/html"))
- .body(
- AccountTemplate {
- account: None,
- error: Some(
- "Problem z utworzeniem konta. Nie można zapisać zdjęcia."
- .into(),
- ),
- page: Page::Register,
- }
- .render()
- .unwrap(),
- );
+ return HttpResponse::BadRequest().content_type("text/html").body(
+ AccountTemplate {
+ account: None,
+ error: Some(
+ "Problem z utworzeniem konta. Nie można zapisać zdjęcia.".into(),
+ ),
+ page: Page::Register,
+ }
+ .render()
+ .unwrap(),
+ );
}
let path = path.to_str().map(String::from).unwrap_or_default();
path.strip_prefix('.')
@@ -381,17 +371,15 @@ RETURNING id, local_business_id, name, price, item_order, picture_url
tracing::error!("{e}");
dbg!(e);
t.rollback().await.unwrap();
- return HttpResponse::BadRequest()
- .append_header(("Content-Type", "text/html"))
- .body(
- AccountTemplate {
- account: None,
- error: Some("Problem z utworzeniem konta".into()),
- page: Page::Register,
- }
- .render()
- .unwrap(),
- );
+ return HttpResponse::BadRequest().content_type("text/html").body(
+ AccountTemplate {
+ account: None,
+ error: Some("Problem z utworzeniem konta".into()),
+ page: Page::Register,
+ }
+ .render()
+ .unwrap(),
+ );
}
}
}
@@ -476,17 +464,15 @@ async fn login(form: web::Form, db: Data, id: Identity) -> Re
id.remember(format!("{}", record.id));
t.commit().await.ok();
- Ok(HttpResponse::Ok()
- .append_header(("Content-Type", "text/html"))
- .body(
- AccountTemplate {
- account: Some(record),
- error: None,
- page: Page::Login,
- }
- .render()
- .unwrap(),
- ))
+ Ok(HttpResponse::Ok().content_type("text/html").body(
+ AccountTemplate {
+ account: Some(record),
+ error: None,
+ page: Page::Login,
+ }
+ .render()
+ .unwrap(),
+ ))
}
#[derive(Serialize)]