about page
This commit is contained in:
@@ -12,6 +12,7 @@ mod m20260517_000007_audio_tags;
|
|||||||
mod m20260517_000008_audio_track_tags;
|
mod m20260517_000008_audio_track_tags;
|
||||||
mod m20260517_000009_simple_constraints;
|
mod m20260517_000009_simple_constraints;
|
||||||
mod m20260517_000010_drop_user_roles;
|
mod m20260517_000010_drop_user_roles;
|
||||||
|
mod m20260517_000011_site_pages;
|
||||||
|
|
||||||
pub struct Migrator;
|
pub struct Migrator;
|
||||||
|
|
||||||
@@ -30,6 +31,7 @@ impl MigratorTrait for Migrator {
|
|||||||
Box::new(m20260517_000008_audio_track_tags::Migration),
|
Box::new(m20260517_000008_audio_track_tags::Migration),
|
||||||
Box::new(m20260517_000009_simple_constraints::Migration),
|
Box::new(m20260517_000009_simple_constraints::Migration),
|
||||||
Box::new(m20260517_000010_drop_user_roles::Migration),
|
Box::new(m20260517_000010_drop_user_roles::Migration),
|
||||||
|
Box::new(m20260517_000011_site_pages::Migration),
|
||||||
// inject-above (do not remove this comment)
|
// inject-above (do not remove this comment)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
65
migration/src/m20260517_000011_site_pages.rs
Normal file
65
migration/src/m20260517_000011_site_pages.rs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
use sea_orm_migration::{prelude::*, sea_orm::ConnectionTrait, sea_query::Expr};
|
||||||
|
|
||||||
|
#[derive(DeriveMigrationName)]
|
||||||
|
pub struct Migration;
|
||||||
|
|
||||||
|
#[derive(DeriveIden)]
|
||||||
|
enum SitePages {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
Slug,
|
||||||
|
Title,
|
||||||
|
Content,
|
||||||
|
CreatedAt,
|
||||||
|
UpdatedAt,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl MigrationTrait for Migration {
|
||||||
|
async fn up(&self, m: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
m.create_table(
|
||||||
|
Table::create()
|
||||||
|
.table(SitePages::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(ColumnDef::new(SitePages::Id).uuid().not_null().primary_key())
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(SitePages::Slug)
|
||||||
|
.string_len(100)
|
||||||
|
.not_null()
|
||||||
|
.unique_key(),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(SitePages::Title).string_len(500).not_null())
|
||||||
|
.col(ColumnDef::new(SitePages::Content).text().not_null())
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(SitePages::CreatedAt)
|
||||||
|
.timestamp_with_time_zone()
|
||||||
|
.not_null()
|
||||||
|
.default(Expr::current_timestamp()),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(SitePages::UpdatedAt)
|
||||||
|
.timestamp_with_time_zone()
|
||||||
|
.not_null()
|
||||||
|
.default(Expr::current_timestamp()),
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
m.get_connection()
|
||||||
|
.execute_unprepared(
|
||||||
|
"INSERT INTO site_pages (id, slug, title, content, created_at, updated_at) \
|
||||||
|
VALUES (gen_random_uuid(), 'about', 'About', '', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) \
|
||||||
|
ON CONFLICT (slug) DO NOTHING",
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn down(&self, m: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
m.drop_table(Table::drop().table(SitePages::Table).to_owned())
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -54,6 +54,7 @@ impl Hooks for App {
|
|||||||
.add_route(controllers::auth::routes())
|
.add_route(controllers::auth::routes())
|
||||||
.add_route(controllers::admin::routes())
|
.add_route(controllers::admin::routes())
|
||||||
.add_route(controllers::blog::routes())
|
.add_route(controllers::blog::routes())
|
||||||
|
.add_route(controllers::pages::routes())
|
||||||
}
|
}
|
||||||
async fn connect_workers(ctx: &AppContext, queue: &Queue) -> Result<()> {
|
async fn connect_workers(ctx: &AppContext, queue: &Queue) -> Result<()> {
|
||||||
queue.register(DownloadWorker::build(ctx)).await?;
|
queue.register(DownloadWorker::build(ctx)).await?;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
pub mod admin;
|
pub mod admin;
|
||||||
pub mod auth;
|
pub mod auth;
|
||||||
pub mod blog;
|
pub mod blog;
|
||||||
|
pub mod pages;
|
||||||
|
|||||||
89
src/controllers/pages.rs
Normal file
89
src/controllers/pages.rs
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
use crate::{
|
||||||
|
controllers::admin,
|
||||||
|
models::_entities::site_pages,
|
||||||
|
};
|
||||||
|
use loco_rs::prelude::*;
|
||||||
|
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
const ABOUT_SLUG: &str = "about";
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct AboutParams {
|
||||||
|
title: String,
|
||||||
|
content: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
struct PageResponse {
|
||||||
|
id: Uuid,
|
||||||
|
slug: String,
|
||||||
|
title: String,
|
||||||
|
content: String,
|
||||||
|
updated_at: chrono::DateTime<chrono::FixedOffset>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<site_pages::Model> for PageResponse {
|
||||||
|
fn from(page: site_pages::Model) -> Self {
|
||||||
|
Self {
|
||||||
|
id: page.id,
|
||||||
|
slug: page.slug,
|
||||||
|
title: page.title,
|
||||||
|
content: page.content,
|
||||||
|
updated_at: page.updated_at,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn find_about(ctx: &AppContext) -> Result<site_pages::Model> {
|
||||||
|
site_pages::Entity::find()
|
||||||
|
.filter(site_pages::Column::Slug.eq(ABOUT_SLUG))
|
||||||
|
.one(&ctx.db)
|
||||||
|
.await?
|
||||||
|
.ok_or_else(|| Error::NotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
async fn about(State(ctx): State<AppContext>) -> Result<Response> {
|
||||||
|
format::json(PageResponse::from(find_about(&ctx).await?))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
async fn update_about(
|
||||||
|
auth: auth::JWT,
|
||||||
|
State(ctx): State<AppContext>,
|
||||||
|
Json(params): Json<AboutParams>,
|
||||||
|
) -> Result<Response> {
|
||||||
|
admin::current_admin(auth, &ctx).await?;
|
||||||
|
|
||||||
|
let page = match find_about(&ctx).await {
|
||||||
|
Ok(page) => {
|
||||||
|
let mut page = page.into_active_model();
|
||||||
|
page.title = Set(params.title);
|
||||||
|
page.content = Set(params.content);
|
||||||
|
page.update(&ctx.db).await?
|
||||||
|
}
|
||||||
|
Err(Error::NotFound) => {
|
||||||
|
site_pages::ActiveModel {
|
||||||
|
id: Set(Uuid::new_v4()),
|
||||||
|
slug: Set(ABOUT_SLUG.to_string()),
|
||||||
|
title: Set(params.title),
|
||||||
|
content: Set(params.content),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
.insert(&ctx.db)
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
Err(err) => return Err(err),
|
||||||
|
};
|
||||||
|
|
||||||
|
format::json(PageResponse::from(page))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn routes() -> Routes {
|
||||||
|
Routes::new()
|
||||||
|
.prefix("/api")
|
||||||
|
.add("/about", get(about))
|
||||||
|
.add("/admin/about", put(update_about))
|
||||||
|
}
|
||||||
@@ -8,4 +8,5 @@ pub mod audio_track_tags;
|
|||||||
pub mod audio_tracks;
|
pub mod audio_tracks;
|
||||||
pub mod audit_logs;
|
pub mod audit_logs;
|
||||||
pub mod blog_articles;
|
pub mod blog_articles;
|
||||||
|
pub mod site_pages;
|
||||||
pub mod users;
|
pub mod users;
|
||||||
|
|||||||
@@ -6,4 +6,5 @@ pub use super::audio_track_tags::Entity as AudioTrackTags;
|
|||||||
pub use super::audio_tracks::Entity as AudioTracks;
|
pub use super::audio_tracks::Entity as AudioTracks;
|
||||||
pub use super::audit_logs::Entity as AuditLogs;
|
pub use super::audit_logs::Entity as AuditLogs;
|
||||||
pub use super::blog_articles::Entity as BlogArticles;
|
pub use super::blog_articles::Entity as BlogArticles;
|
||||||
|
pub use super::site_pages::Entity as SitePages;
|
||||||
pub use super::users::Entity as Users;
|
pub use super::users::Entity as Users;
|
||||||
|
|||||||
21
src/models/_entities/site_pages.rs
Normal file
21
src/models/_entities/site_pages.rs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||||
|
#[sea_orm(table_name = "site_pages")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub id: Uuid,
|
||||||
|
#[sea_orm(unique)]
|
||||||
|
pub slug: String,
|
||||||
|
pub title: String,
|
||||||
|
#[sea_orm(column_type = "Text")]
|
||||||
|
pub content: String,
|
||||||
|
pub created_at: DateTimeWithTimeZone,
|
||||||
|
pub updated_at: DateTimeWithTimeZone,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {}
|
||||||
@@ -6,3 +6,4 @@ pub mod audio_track_tags;
|
|||||||
pub mod audit_logs;
|
pub mod audit_logs;
|
||||||
pub mod blog_articles;
|
pub mod blog_articles;
|
||||||
pub mod audio_albums;
|
pub mod audio_albums;
|
||||||
|
pub mod site_pages;
|
||||||
|
|||||||
23
src/models/site_pages.rs
Normal file
23
src/models/site_pages.rs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
pub use super::_entities::site_pages::{ActiveModel, Entity, Model};
|
||||||
|
pub type SitePages = Entity;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl ActiveModelBehavior for ActiveModel {
|
||||||
|
async fn before_save<C>(self, _db: &C, insert: bool) -> std::result::Result<Self, DbErr>
|
||||||
|
where
|
||||||
|
C: ConnectionTrait,
|
||||||
|
{
|
||||||
|
if !insert && self.updated_at.is_unchanged() {
|
||||||
|
let mut this = self;
|
||||||
|
this.updated_at = sea_orm::ActiveValue::Set(chrono::Utc::now().into());
|
||||||
|
Ok(this)
|
||||||
|
} else {
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Model {}
|
||||||
|
impl ActiveModel {}
|
||||||
|
impl Entity {}
|
||||||
Reference in New Issue
Block a user