add cursor and move to uuidv7
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
Hamcha 2023-07-13 17:43:05 +02:00
parent 203df76d6c
commit 81134dc0dd
Signed by: hamcha
GPG key ID: 1669C533B8CF6D89
16 changed files with 435 additions and 145 deletions

6
.cargo/config.toml Normal file
View file

@ -0,0 +1,6 @@
[build]
rustflags = ["--cfg", "uuid_unstable"]
[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-Clink-arg=-fuse-ld=mold", "--cfg", "uuid_unstable"]

6
Cargo.lock generated
View file

@ -1830,12 +1830,12 @@ dependencies = [
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.3.4" version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa2982af2eec27de306107c027578ff7f423d65f7250e40ce0fea8f45248b81" checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be"
dependencies = [ dependencies = [
"atomic",
"getrandom", "getrandom",
"rand",
"serde", "serde",
] ]

View file

@ -12,7 +12,7 @@ tracing = "0.1"
tracing-subscriber = "0.3" tracing-subscriber = "0.3"
tokio = { version = "1.28", features = ["full"] } tokio = { version = "1.28", features = ["full"] }
sqlx = { version = "0.6", features = [ "runtime-tokio-rustls", "postgres", "uuid", "chrono", "macros", "migrate", "json", "offline"] } sqlx = { version = "0.6", features = [ "runtime-tokio-rustls", "postgres", "uuid", "chrono", "macros", "migrate", "json", "offline"] }
uuid = { version = "1.3", features = ["v4", "fast-rng", "serde"] } uuid = { version = "1.4", features = ["v7", "serde", "std"] }
serde = { version = "1" } serde = { version = "1" }
serde_json = { version = "1", features = ["raw_value"] } serde_json = { version = "1", features = ["raw_value"] }
figment = { version = "0.10", features = ["toml", "env", "test"] } figment = { version = "0.10", features = ["toml", "env", "test"] }

View file

@ -1,7 +1,7 @@
CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- noqa: RF05 CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- noqa: RF05
CREATE TABLE users ( CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), id UUID PRIMARY KEY,
name VARCHAR UNIQUE NOT NULL, name VARCHAR UNIQUE NOT NULL,
email VARCHAR, email VARCHAR,
password VARCHAR, password VARCHAR,
@ -14,7 +14,7 @@ CREATE TABLE users (
); );
CREATE TABLE sites ( CREATE TABLE sites (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), id UUID PRIMARY KEY,
owner UUID NOT NULL REFERENCES users (id) ON DELETE CASCADE, owner UUID NOT NULL REFERENCES users (id) ON DELETE CASCADE,
name VARCHAR UNIQUE NOT NULL, name VARCHAR UNIQUE NOT NULL,
title VARCHAR NOT NULL, title VARCHAR NOT NULL,
@ -25,7 +25,7 @@ CREATE TABLE sites (
); );
CREATE TABLE pages ( CREATE TABLE pages (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), id UUID PRIMARY KEY,
site UUID NOT NULL REFERENCES sites (id) ON DELETE CASCADE, site UUID NOT NULL REFERENCES sites (id) ON DELETE CASCADE,
author UUID REFERENCES users (id) ON DELETE SET NULL, author UUID REFERENCES users (id) ON DELETE SET NULL,
slug VARCHAR NOT NULL, slug VARCHAR NOT NULL,

View file

@ -1,7 +1,7 @@
CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- noqa: RF05 CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- noqa: RF05
CREATE TABLE sessions ( CREATE TABLE sessions (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), id UUID PRIMARY KEY,
actor UUID NOT NULL REFERENCES users (id) ON DELETE CASCADE, actor UUID NOT NULL REFERENCES users (id) ON DELETE CASCADE,
secret VARCHAR NOT NULL, secret VARCHAR NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT now(), created_at TIMESTAMP NOT NULL DEFAULT now(),

View file

@ -1,7 +1,7 @@
CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- noqa: RF05 CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- noqa: RF05
CREATE TABLE collections ( CREATE TABLE collections (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), id UUID PRIMARY KEY,
site UUID NOT NULL REFERENCES sites (id) ON DELETE CASCADE, site UUID NOT NULL REFERENCES sites (id) ON DELETE CASCADE,
slug VARCHAR NOT NULL, slug VARCHAR NOT NULL,
name VARCHAR NOT NULL, name VARCHAR NOT NULL,

View file

@ -1,4 +1,4 @@
-- Remove constraint and index (collections_site_slug_unique, collections_site_slug_idx) -- Remove constraint and index
ALTER TABLE collections DROP CONSTRAINT collections_site_slug_unique; ALTER TABLE collections DROP CONSTRAINT collections_site_slug_unique;
DROP INDEX IF EXISTS collections_site_slug_unique; DROP INDEX IF EXISTS collections_site_slug_unique;

View file

@ -1,28 +1,5 @@
{ {
"db": "PostgreSQL", "db": "PostgreSQL",
"0e89d76bdba2178afc2fb2c2a5eba0404e68e7a1d26d9fd5f1161f055dad92df": {
"describe": {
"columns": [
{
"name": "id",
"ordinal": 0,
"type_info": "Uuid"
}
],
"nullable": [
false
],
"parameters": {
"Left": [
"Uuid",
"Varchar",
"Timestamp",
"Timestamp"
]
}
},
"query": "INSERT INTO sessions (actor, secret, created_at, expires_at) VALUES ($1, $2, $3, $4) RETURNING id"
},
"11e96cfd8c2736f13ce55975ea910dd68640f6f14e38a4b3342d514804e3de27": { "11e96cfd8c2736f13ce55975ea910dd68640f6f14e38a4b3342d514804e3de27": {
"describe": { "describe": {
"columns": [], "columns": [],
@ -35,20 +12,228 @@
}, },
"query": "DELETE FROM sessions WHERE id = $1" "query": "DELETE FROM sessions WHERE id = $1"
}, },
"244e021e36237f5e9ce0f17b6eb3ba58d42fe292e7517a33938c5191bc8787e1": { "23f1a1f2aa96a5a23880f330d04f5dc695a148f4aa50b8c76378b68944569e44": {
"describe": {
"columns": [
{
"name": "author_display_name",
"ordinal": 0,
"type_info": "Varchar"
},
{
"name": "author_username",
"ordinal": 1,
"type_info": "Varchar"
},
{
"name": "slug",
"ordinal": 2,
"type_info": "Varchar"
},
{
"name": "title",
"ordinal": 3,
"type_info": "Varchar"
},
{
"name": "description",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "tags",
"ordinal": 5,
"type_info": "VarcharArray"
},
{
"name": "blocks!: Json<Vec<PostBlock>>",
"ordinal": 6,
"type_info": "Jsonb"
},
{
"name": "created_at",
"ordinal": 7,
"type_info": "Timestamp"
},
{
"name": "modified_at",
"ordinal": 8,
"type_info": "Timestamp"
},
{
"name": "published",
"ordinal": 9,
"type_info": "Bool"
}
],
"nullable": [
null,
false,
false,
false,
true,
false,
false,
false,
true,
false
],
"parameters": {
"Left": [
"Text",
"Text"
]
}
},
"query": "SELECT\n COALESCE(users.display_name,users.name) as author_display_name,\n users.name as author_username,\n pages.slug,\n pages.title,\n pages.description,\n pages.tags,\n pages.blocks as \"blocks!: Json<Vec<PostBlock>>\",\n pages.created_at,\n pages.modified_at,\n pages.published\n FROM pages \n JOIN\n sites ON site = sites.id\n JOIN\n users ON author = users.id\n WHERE\n slug = $1\n AND sites.name = $2 \n AND pages.deleted_at IS NULL\n AND sites.deleted_at IS NULL"
},
"28ec2833283df836e457161f44f0fbc75ed37dcc0ba63eb09bf285aae2f7a380": {
"describe": { "describe": {
"columns": [], "columns": [],
"nullable": [], "nullable": [],
"parameters": { "parameters": {
"Left": [ "Left": [
"Varchar", "Uuid",
"Uuid", "Uuid",
"Varchar", "Varchar",
"Varchar"
]
}
},
"query": "INSERT INTO collections (id, site, slug, name)\n VALUES ($1, $2, $3, $4)"
},
"29e0259def5c6fdc34c6a18345150d2714736b91c1f3040fcb07d77cbe2552e1": {
"describe": {
"columns": [
{
"name": "name",
"ordinal": 0,
"type_info": "Varchar"
},
{
"name": "owner",
"ordinal": 1,
"type_info": "Varchar"
},
{
"name": "owner_display_name",
"ordinal": 2,
"type_info": "Varchar"
},
{
"name": "title",
"ordinal": 3,
"type_info": "Varchar"
},
{
"name": "description",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "created_at",
"ordinal": 5,
"type_info": "Timestamp"
},
{
"name": "collections!: Vec<CollectionNameAndSlug>",
"ordinal": 6,
"type_info": "RecordArray"
}
],
"nullable": [
false,
false,
null,
false,
true,
false,
null
],
"parameters": {
"Left": [
"Text" "Text"
] ]
} }
}, },
"query": "INSERT INTO sites (name, owner, title, description) VALUES ($1, $2, $3, $4)" "query": "SELECT\n sites.name,\n users.name as owner,\n COALESCE(users.display_name,users.name) as owner_display_name,\n title,\n description,\n sites.created_at,\n array_agg(row(collections.slug, collections.name)) as \"collections!: Vec<CollectionNameAndSlug>\"\n FROM sites\n JOIN\n users ON sites.owner = users.id\n JOIN\n collections ON collections.site = sites.id\n WHERE\n sites.name = $1\n AND sites.deleted_at IS NULL\n GROUP BY\n sites.name, users.name, users.display_name, title, description, sites.created_at"
},
"320625aa3fac73cb43d6a68592767174558ab1a6923d3574ad629d1a1bd0d4a6": {
"describe": {
"columns": [
{
"name": "author_display_name",
"ordinal": 0,
"type_info": "Varchar"
},
{
"name": "author_username",
"ordinal": 1,
"type_info": "Varchar"
},
{
"name": "slug",
"ordinal": 2,
"type_info": "Varchar"
},
{
"name": "title",
"ordinal": 3,
"type_info": "Varchar"
},
{
"name": "description",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "tags",
"ordinal": 5,
"type_info": "VarcharArray"
},
{
"name": "blocks!: Json<Vec<PostBlock>>",
"ordinal": 6,
"type_info": "Jsonb"
},
{
"name": "created_at",
"ordinal": 7,
"type_info": "Timestamp"
},
{
"name": "modified_at",
"ordinal": 8,
"type_info": "Timestamp"
},
{
"name": "published",
"ordinal": 9,
"type_info": "Bool"
}
],
"nullable": [
null,
false,
false,
false,
true,
false,
false,
false,
true,
false
],
"parameters": {
"Left": [
"Uuid",
"Uuid",
"Timestamp",
"Int8"
]
}
},
"query": "SELECT\n COALESCE(users.display_name,users.name) as author_display_name,\n users.name as author_username,\n pages.slug,\n pages.title,\n pages.description,\n pages.tags,\n pages.blocks as \"blocks!: Json<Vec<PostBlock>>\",\n pages.created_at,\n pages.modified_at,\n pages.published\n FROM pages\n JOIN\n users ON author = users.id\n WHERE\n site = $1\n AND $2 = ANY(collections)\n AND pages.deleted_at IS NULL\n AND published = true\n AND pages.created_at <= $3\n ORDER BY created_at DESC\n LIMIT $4\n "
}, },
"35deaad55b84124dcba02c555718fe815003e3f50b1c8ee2fc7e540009fa5aef": { "35deaad55b84124dcba02c555718fe815003e3f50b1c8ee2fc7e540009fa5aef": {
"describe": { "describe": {
@ -68,23 +253,50 @@
}, },
"query": "SELECT CASE WHEN EXISTS(SELECT 1 FROM users) THEN false ELSE true END AS empty;" "query": "SELECT CASE WHEN EXISTS(SELECT 1 FROM users) THEN false ELSE true END AS empty;"
}, },
"51786c014e6f0863b0462646121bed527cc5c0bac343974f2d3b049e46c14e72": { "39237d1e40ce33ce386320212a53986cb21949dd07014772a150242caf4fa610": {
"describe": { "describe": {
"columns": [], "columns": [],
"nullable": [], "nullable": [],
"parameters": { "parameters": {
"Left": [ "Left": [
"Uuid",
"Uuid",
"Uuid",
"Varchar",
"Varchar", "Varchar",
"Text", "Text",
"VarcharArray", "VarcharArray",
"Jsonb", "Jsonb",
"Uuid", "UuidArray",
"Uuid", "Bool"
"Text"
] ]
} }
}, },
"query": "UPDATE pages SET title = COALESCE($1, title), description = COALESCE($2, description), tags = COALESCE($3, tags), blocks = COALESCE($4, blocks) WHERE author = $5 AND site = $6 AND slug = $7 AND deleted_at IS NULL" "query": "INSERT INTO pages (id, author, site, slug, title, description, tags, blocks, collections, published)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)"
},
"562254387ad52ad5ee3cf806085873485d6b8a0b372ac59a0796b9f0c8910cf2": {
"describe": {
"columns": [
{
"name": "id",
"ordinal": 0,
"type_info": "Uuid"
}
],
"nullable": [
false
],
"parameters": {
"Left": [
"Uuid",
"Uuid",
"Varchar",
"Timestamp",
"Timestamp"
]
}
},
"query": "INSERT INTO sessions (id, actor, secret, created_at, expires_at) VALUES ($1, $2, $3, $4, $5) RETURNING id"
}, },
"61fcf083e6abcfed9480164f3ed4ff2f396c8041a30df002f6e4af920b41f4e4": { "61fcf083e6abcfed9480164f3ed4ff2f396c8041a30df002f6e4af920b41f4e4": {
"describe": { "describe": {
@ -111,24 +323,6 @@
}, },
"query": "DELETE FROM sessions WHERE expires_at < $1" "query": "DELETE FROM sessions WHERE expires_at < $1"
}, },
"7b294c850a2feba882b52cc479ae943fdd4a3d26278274ea19eb6a3a0393b4ad": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Left": [
"Uuid",
"Uuid",
"Varchar",
"Varchar",
"Text",
"VarcharArray",
"Jsonb"
]
}
},
"query": "INSERT INTO pages (author, site, slug, title, description, tags, blocks) VALUES ($1, $2, $3, $4, $5, $6, $7)"
},
"7bdb55b060b5e81b50c1bf86cb117215cba7010c91f8384a397efac06a88507d": { "7bdb55b060b5e81b50c1bf86cb117215cba7010c91f8384a397efac06a88507d": {
"describe": { "describe": {
"columns": [], "columns": [],
@ -143,6 +337,35 @@
}, },
"query": "DELETE FROM pages WHERE author = $1 AND site = $2 AND slug = $3" "query": "DELETE FROM pages WHERE author = $1 AND site = $2 AND slug = $3"
}, },
"82450bdd6a4a7ac9ee66fb98fd87993b70fa58577876b0761ceac9e31af27b8a": {
"describe": {
"columns": [
{
"name": "id",
"ordinal": 0,
"type_info": "Uuid"
},
{
"name": "created_at",
"ordinal": 1,
"type_info": "Timestamp"
}
],
"nullable": [
false,
false
],
"parameters": {
"Left": [
"Uuid",
"Varchar",
"Varchar",
"UuidArray"
]
}
},
"query": "INSERT INTO users ( id, name, password, roles )\n VALUES ( $1,$2,$3,$4 ) RETURNING id, created_at"
},
"9f37c6f3929ca1bf2a6ec55291d652b216419fd13e27b3d61d23c9cf900c501e": { "9f37c6f3929ca1bf2a6ec55291d652b216419fd13e27b3d61d23c9cf900c501e": {
"describe": { "describe": {
"columns": [ "columns": [
@ -164,66 +387,6 @@
}, },
"query": "UPDATE sessions SET expires_at = $1 WHERE id = $2 RETURNING id" "query": "UPDATE sessions SET expires_at = $1 WHERE id = $2 RETURNING id"
}, },
"c04874a0f4ab7409f53ee50ec34679f6b274b350aff39a8710575c54fcaef1e4": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Text",
"Text",
"Uuid"
]
}
},
"query": "UPDATE sites SET name = COALESCE($1, name), title = COALESCE($2, title), description = COALESCE($3, description), modified_at = NOW() WHERE name = $4 AND owner = $5 AND deleted_at IS NULL"
},
"c1121a7da8dc099e6eb3ed8d1008a247524f7b953f2d0db674371714f09c6530": {
"describe": {
"columns": [
{
"name": "name",
"ordinal": 0,
"type_info": "Varchar"
},
{
"name": "owner",
"ordinal": 1,
"type_info": "Varchar"
},
{
"name": "title",
"ordinal": 2,
"type_info": "Varchar"
},
{
"name": "description",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "created_at",
"ordinal": 4,
"type_info": "Timestamp"
}
],
"nullable": [
false,
false,
false,
true,
false
],
"parameters": {
"Left": [
"Text"
]
}
},
"query": "SELECT sites.name, users.name as owner, title, description, sites.created_at FROM sites JOIN users ON sites.owner = users.id WHERE sites.name = $1 AND sites.deleted_at IS NULL"
},
"c567d96c92638902bc90f5fc0aa5bbbe0b6d26607767720a87fdbe67335a0180": { "c567d96c92638902bc90f5fc0aa5bbbe0b6d26607767720a87fdbe67335a0180": {
"describe": { "describe": {
"columns": [ "columns": [
@ -335,6 +498,22 @@
}, },
"query": "DELETE FROM sites WHERE name = $1 AND owner = $2" "query": "DELETE FROM sites WHERE name = $1 AND owner = $2"
}, },
"d0005084c0ae3939460c3c727b29899c65a05e53fbc477cdda9665e7f243d5e9": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Text",
"Text",
"Uuid"
]
}
},
"query": "UPDATE sites SET\n name = COALESCE($1, name),\n title = COALESCE($2, title),\n description = COALESCE($3, description),\n modified_at = NOW()\n WHERE\n name = $4\n AND owner = $5\n AND deleted_at IS NULL"
},
"d9649c375db5825adda3b91c52468235fd9c361b827df64350c7e65bca0f00b4": { "d9649c375db5825adda3b91c52468235fd9c361b827df64350c7e65bca0f00b4": {
"describe": { "describe": {
"columns": [], "columns": [],
@ -349,33 +528,49 @@
}, },
"query": "UPDATE pages SET deleted_at = NOW() WHERE author = $1 AND site = $2 AND slug = $3 AND deleted_at IS NULL" "query": "UPDATE pages SET deleted_at = NOW() WHERE author = $1 AND site = $2 AND slug = $3 AND deleted_at IS NULL"
}, },
"f054746ab3092a8671604c9388e02220d62d337e9d199fa9102c0363a72f6940": { "e9f521d288a92f1e70b4de35b0d75792007d910ee65e2782eee4a3cdd30560a0": {
"describe": { "describe": {
"columns": [ "columns": [
{ {
"name": "id", "name": "id",
"ordinal": 0, "ordinal": 0,
"type_info": "Uuid" "type_info": "Uuid"
},
{
"name": "created_at",
"ordinal": 1,
"type_info": "Timestamp"
} }
], ],
"nullable": [ "nullable": [
false,
false false
], ],
"parameters": { "parameters": {
"Left": [ "Left": [
"Uuid",
"Varchar", "Varchar",
"Uuid",
"Varchar", "Varchar",
"UuidArray" "Text"
] ]
} }
}, },
"query": "INSERT INTO users ( name, password, roles ) VALUES ( $1,$2,$3 ) RETURNING id, created_at" "query": "INSERT INTO sites (id, name, owner, title, description)\n VALUES ($1, $2, $3, $4, $5) RETURNING id"
},
"ecbbf6f400e058cd6e8695354d65a1883f974a39e11ac5ba0f7fba408fcf6b2a": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Left": [
"Varchar",
"Text",
"VarcharArray",
"Jsonb",
"Uuid",
"Uuid",
"Text",
"UuidArray",
"Bool"
]
}
},
"query": "UPDATE pages SET title = COALESCE($1, title), description = COALESCE($2, description), tags = COALESCE($3, tags), blocks = COALESCE($4, blocks), collections = COALESCE($8, collections), published = COALESCE($9, published) WHERE author = $5 AND site = $6 AND slug = $7 AND deleted_at IS NULL"
}, },
"f3d0f52ab3c9d7ed46086d21cd88207a75d9f2e2990f3fb721f0e14e1ec52e10": { "f3d0f52ab3c9d7ed46086d21cd88207a75d9f2e2990f3fb721f0e14e1ec52e10": {
"describe": { "describe": {

View file

@ -38,7 +38,8 @@ impl Session {
let expires = now + duration; let expires = now + duration;
let secret = random(); let secret = random();
let result = sqlx::query!( let result = sqlx::query!(
"INSERT INTO sessions (actor, secret, created_at, expires_at) VALUES ($1, $2, $3, $4) RETURNING id", "INSERT INTO sessions (id, actor, secret, created_at, expires_at) VALUES ($1, $2, $3, $4, $5) RETURNING id",
Uuid::now_v7(),
user_id, user_id,
secret, secret,
now, now,

View file

@ -60,13 +60,15 @@ impl User {
roles: &Vec<Uuid>, roles: &Vec<Uuid>,
) -> Result<Self> { ) -> Result<Self> {
let result = sqlx::query!( let result = sqlx::query!(
r#"INSERT INTO users ( name, password, roles ) VALUES ( $1,$2,$3 ) RETURNING id, created_at"#, r#"INSERT INTO users ( id, name, password, roles )
username, VALUES ( $1,$2,$3,$4 ) RETURNING id, created_at"#,
hash(&password)?, Uuid::now_v7(),
roles, username,
) hash(&password)?,
.fetch_one(pool) roles,
.await?; )
.fetch_one(pool)
.await?;
Ok(Self { Ok(Self {
id: result.id, id: result.id,
name: username.to_owned(), name: username.to_owned(),

View file

@ -4,6 +4,8 @@ use serde::{Deserialize, Serialize};
use sqlx::{types::Json, FromRow}; use sqlx::{types::Json, FromRow};
use uuid::Uuid; use uuid::Uuid;
use crate::cursor::{AsCursor, CursorList};
use super::Error; use super::Error;
#[derive(Deserialize, Serialize, FromRow)] #[derive(Deserialize, Serialize, FromRow)]
@ -115,15 +117,29 @@ pub struct PostWithAuthor {
pub blocks: Json<Vec<PostBlock>>, pub blocks: Json<Vec<PostBlock>>,
pub created_at: NaiveDateTime, pub created_at: NaiveDateTime,
pub modified_at: Option<NaiveDateTime>, pub modified_at: Option<NaiveDateTime>,
pub deleted_at: Option<NaiveDateTime>,
pub published: bool, pub published: bool,
} }
impl AsCursor<NaiveDateTime> for PostWithAuthor {
fn cursor(&self) -> NaiveDateTime {
self.created_at
}
}
#[async_trait] #[async_trait]
pub trait PostRepository { pub trait PostRepository {
/// Get a post from its slug /// Get a post from its slug
async fn get_post_from_url(&self, site: &str, slug: &str) -> Result<PostWithAuthor, Error>; async fn get_post_from_url(&self, site: &str, slug: &str) -> Result<PostWithAuthor, Error>;
/// List posts in a collection
async fn list_posts_in_collection(
&self,
site: &Uuid,
collection: &Uuid,
cursor: Option<NaiveDateTime>,
limit: i64,
) -> Result<CursorList<PostWithAuthor, NaiveDateTime>, Error>;
/// Create a new post /// Create a new post
async fn create_post( async fn create_post(
&self, &self,

17
src/cursor.rs Normal file
View file

@ -0,0 +1,17 @@
pub struct CursorList<T, C> {
pub items: Vec<T>,
pub next_cursor: Option<C>,
}
pub trait AsCursor<C> {
fn cursor(&self) -> C;
}
impl<T: AsCursor<C>, C> CursorList<T, C> {
pub fn new(items: Vec<T>, limit: usize) -> Self {
CursorList {
next_cursor: items.get(limit - 1).map(|x| x.cursor()),
items,
}
}
}

View file

@ -15,7 +15,9 @@ impl CollectionRepository for Database {
for (slug, name) in DEFAULT_COLLECTIONS { for (slug, name) in DEFAULT_COLLECTIONS {
sqlx::query!( sqlx::query!(
r#"INSERT INTO collections (site, slug, name) VALUES ($1, $2, $3)"#, r#"INSERT INTO collections (id, site, slug, name)
VALUES ($1, $2, $3, $4)"#,
Uuid::now_v7(),
site, site,
slug, slug,
name name

View file

@ -1,19 +1,66 @@
use async_trait::async_trait; use async_trait::async_trait;
use chrono::NaiveDateTime;
use content::post::PostBlock; use content::post::PostBlock;
use serde_json::json; use serde_json::json;
use sqlx::types::Json; use sqlx::types::Json;
use uuid::Uuid; use uuid::Uuid;
use crate::content::{ use crate::{
self, content::{
post::{CreatePostData, PostRepository, PostWithAuthor, UpdatePostData}, self,
Error, post::{CreatePostData, PostRepository, PostWithAuthor, UpdatePostData},
Error,
},
cursor::CursorList,
}; };
use super::Database; use super::Database;
#[async_trait] #[async_trait]
impl PostRepository for Database { impl PostRepository for Database {
async fn list_posts_in_collection(
&self,
site: &Uuid,
collection: &Uuid,
from: Option<NaiveDateTime>,
limit: i64,
) -> Result<CursorList<PostWithAuthor, NaiveDateTime>, Error> {
let posts = sqlx::query_as!(
PostWithAuthor,
r#"SELECT
COALESCE(users.display_name,users.name) as author_display_name,
users.name as author_username,
pages.slug,
pages.title,
pages.description,
pages.tags,
pages.blocks as "blocks!: Json<Vec<PostBlock>>",
pages.created_at,
pages.modified_at,
pages.published
FROM pages
JOIN
users ON author = users.id
WHERE
site = $1
AND $2 = ANY(collections)
AND pages.deleted_at IS NULL
AND published = true
AND pages.created_at <= $3
ORDER BY created_at DESC
LIMIT $4
"#,
site,
collection,
from.unwrap_or_else(|| NaiveDateTime::MAX),
limit
)
.fetch_all(&self.pool)
.await?;
Ok(CursorList::new(posts, limit as usize))
}
async fn get_post_from_url( async fn get_post_from_url(
&self, &self,
site: &str, site: &str,
@ -31,7 +78,6 @@ impl PostRepository for Database {
pages.blocks as "blocks!: Json<Vec<PostBlock>>", pages.blocks as "blocks!: Json<Vec<PostBlock>>",
pages.created_at, pages.created_at,
pages.modified_at, pages.modified_at,
pages.deleted_at,
pages.published pages.published
FROM pages FROM pages
JOIN JOIN
@ -62,7 +108,9 @@ impl PostRepository for Database {
data: CreatePostData, data: CreatePostData,
) -> Result<(), Error> { ) -> Result<(), Error> {
sqlx::query!( sqlx::query!(
"INSERT INTO pages (author, site, slug, title, description, tags, blocks, collections, published) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", "INSERT INTO pages (id, author, site, slug, title, description, tags, blocks, collections, published)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
Uuid::now_v7(),
owner, owner,
site, site,
data.slug, data.slug,

View file

@ -69,7 +69,9 @@ impl SiteRepository for Database {
async fn create_site(&self, owner: &Uuid, options: CreateSiteData) -> Result<Uuid, Error> { async fn create_site(&self, owner: &Uuid, options: CreateSiteData) -> Result<Uuid, Error> {
let result = sqlx::query!( let result = sqlx::query!(
"INSERT INTO sites (name, owner, title, description) VALUES ($1, $2, $3, $4) RETURNING id", "INSERT INTO sites (id, name, owner, title, description)
VALUES ($1, $2, $3, $4, $5) RETURNING id",
Uuid::now_v7(),
options.name, options.name,
owner, owner,
options.title, options.title,

View file

@ -1,6 +1,7 @@
mod auth; mod auth;
mod builtins; mod builtins;
mod content; mod content;
mod cursor;
mod database; mod database;
mod http; mod http;
mod routes; mod routes;