Search by tags.
Return tags used by posts.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
pull/1/head
Armored Dragon 2024-04-23 08:13:48 -05:00
parent 2997e4315f
commit e0b530c606
Signed by: ArmoredDragon
GPG Key ID: C7207ACC3382AD8B
6 changed files with 84 additions and 10 deletions

View File

@ -131,9 +131,19 @@ async function getPost({ requester_id, post_id, visibility = "PUBLISHED" } = {},
// Get a single post // Get a single post
if (post_id) { if (post_id) {
let post; let post;
post = await prisma.post.findUnique({ where: { id: post_id }, include: { owner: true } }); post = await prisma.post.findUnique({ where: { id: post_id }, include: { owner: true, tags: true } });
if (!post) return _r(false, "Post does not exist"); if (!post) return _r(false, "Post does not exist");
post = _stripPrivatePost(post); post = _stripPrivatePost(post);
// Tags
let post_tags = [];
post.raw_tags = [];
post.tags.forEach((tag) => {
post_tags.push(tag.name);
post.raw_tags.push();
});
post.tags = post_tags;
// Render post // Render post
return { success: true, data: await _renderPost(post) }; return { success: true, data: await _renderPost(post) };
} }
@ -170,7 +180,7 @@ async function getPost({ requester_id, post_id, visibility = "PUBLISHED" } = {},
}; };
// Build the "where_object" object // Build the "where_object" object
if (search) { if (search) {
if (search_tags) where_object["AND"][0]["OR"].push({ tags: { hasSome: [search?.toLowerCase()] } }); if (search_tags) where_object["AND"][0]["OR"].push({ tags: { some: { name: search?.toLowerCase() } } });
if (search_title) where_object["AND"][0]["OR"].push({ title: { contains: search, mode: "insensitive" } }); if (search_title) where_object["AND"][0]["OR"].push({ title: { contains: search, mode: "insensitive" } });
if (search_content) where_object["AND"][0]["OR"].push({ content: { contains: search, mode: "insensitive" } }); if (search_content) where_object["AND"][0]["OR"].push({ content: { contains: search, mode: "insensitive" } });
} }
@ -179,7 +189,7 @@ async function getPost({ requester_id, post_id, visibility = "PUBLISHED" } = {},
where: where_object, where: where_object,
take: limit, take: limit,
skip: Math.max(page, 0) * limit, skip: Math.max(page, 0) * limit,
include: { owner: true }, include: { owner: true, tags: true },
orderBy: [{ publish_date: "desc" }, { created_date: "desc" }], orderBy: [{ publish_date: "desc" }, { created_date: "desc" }],
}); });
@ -187,6 +197,10 @@ async function getPost({ requester_id, post_id, visibility = "PUBLISHED" } = {},
post = _stripPrivatePost(post); post = _stripPrivatePost(post);
post = await _renderPost(post); post = await _renderPost(post);
post_list.push(post); post_list.push(post);
let post_tags = [];
post.tags.forEach((tag) => post_tags.push(tag.name));
post.tags = post_tags;
} }
// Calculate pagination // Calculate pagination
@ -226,6 +240,20 @@ async function editPost({ requester_id, post_id, post_content }) {
publish_date = new Date(year, month - 1, day, hour, minute); publish_date = new Date(year, month - 1, day, hour, minute);
} }
// Handle tags ----
let database_tag_list = [];
const existing_tags = post.tags?.map((tag) => ({ name: tag })) || [];
// Add new tags
for (let tag_index = 0; post_content.tags.length > tag_index; tag_index++) {
let tag = post_content.tags[tag_index];
// Check to see if tag exists, create if necessary,
let database_tag = await prisma.tag.upsert({ where: { name: tag }, update: {}, create: { name: tag } });
database_tag_list.push(database_tag);
}
// Rebuild the post to save // Rebuild the post to save
let post_formatted = { let post_formatted = {
title: post_content.title, title: post_content.title,
@ -233,7 +261,7 @@ async function editPost({ requester_id, post_id, post_content }) {
content: post_content.content, content: post_content.content,
visibility: post_content.visibility || "PRIVATE", visibility: post_content.visibility || "PRIVATE",
publish_date: publish_date || post_content.publish_date, publish_date: publish_date || post_content.publish_date,
tags: post_content.tags, tags: { disconnect: [...existing_tags], connect: [...database_tag_list] },
media: [...post.raw_media, ...post_content.media], media: [...post.raw_media, ...post_content.media],
}; };
@ -345,6 +373,29 @@ async function getMedia({ parent_id, file_name }) {
// Unreferenced images and media will be deleted // Unreferenced images and media will be deleted
async function deleteMedia({ parent_id, file_name }) {} async function deleteMedia({ parent_id, file_name }) {}
async function getTags({ order = "count" } = {}) {
if (order == "count") {
return await prisma.tag.findMany({
include: { _count: { select: { posts: true } } },
where: {
posts: {
some: {},
},
},
take: 15,
orderBy: {
posts: {
_count: "desc",
},
},
});
}
}
// TODO:
// Will be done automatically in the background
async function deleteTag({ tag_id }) {}
// async function deleteImage(image, requester_id) { // async function deleteImage(image, requester_id) {
// const user = await getUser({ id: requester_id }); // const user = await getUser({ id: requester_id });
// const post = await getBlog({ id: image.parent, raw: true }); // const post = await getBlog({ id: image.parent, raw: true });
@ -540,4 +591,4 @@ const _r = (s, m) => {
return { success: s, message: m }; return { success: s, message: m };
}; };
module.exports = { settings, newUser, getUser, editUser, getPost, newPost, editPost, getBiography, updateBiography, uploadMedia, deleteBlog, postSetting, getSetting }; module.exports = { settings, newUser, getUser, editUser, getPost, newPost, editPost, getBiography, updateBiography, uploadMedia, deleteBlog, getTags, postSetting, getSetting };

View File

@ -14,6 +14,7 @@ async function index(request, response) {
// if (!is_setup_complete) return response.redirect("/register"); // if (!is_setup_complete) return response.redirect("/register");
const blog_list = await core.getPost({ requester_id: request.session.user?.id, page: request.query.page || 0 }); const blog_list = await core.getPost({ requester_id: request.session.user?.id, page: request.query.page || 0 });
const tags = await core.getTags();
blog_list.data.forEach((post) => { blog_list.data.forEach((post) => {
let published_date_parts = new Date(post.publish_date).toLocaleDateString().split("/"); let published_date_parts = new Date(post.publish_date).toLocaleDateString().split("/");
@ -27,6 +28,7 @@ async function index(request, response) {
pagination: blog_list.pagination, pagination: blog_list.pagination,
current_page: request.query.page || 0, current_page: request.query.page || 0,
loaded_page: request.path, loaded_page: request.path,
tags: tags,
}); });
} }
function register(request, response) { function register(request, response) {
@ -52,7 +54,7 @@ async function authorEdit(request, response) {
response.render(getThemePage("authorEdit"), { ...getDefaults(request), profile: author.data }); response.render(getThemePage("authorEdit"), { ...getDefaults(request), profile: author.data });
} }
async function blogList(req, res) { async function blogList(req, res) {
const blog_list = await core.getPost({ requester_id: req.session.user?.id }, { search: req.query.search, search_title: true }); const blog_list = await core.getPost({ requester_id: req.session.user?.id }, { search: req.query.search, search_title: true, search_tags: true, search_content: true });
blog_list.data.forEach((post) => { blog_list.data.forEach((post) => {
let published_date_parts = new Date(post.publish_date).toLocaleDateString().split("/"); let published_date_parts = new Date(post.publish_date).toLocaleDateString().split("/");

View File

@ -85,7 +85,8 @@
height: -moz-fit-content; height: -moz-fit-content;
height: fit-content; height: fit-content;
background-color: lightgray; background-color: lightgray;
margin-bottom: 0.1rem; margin-bottom: 0.5rem;
margin-right: 0.5rem;
} }
.post-list-container .tag-list .list .tag::before { .post-list-container .tag-list .list .tag::before {
display: none; display: none;
@ -98,6 +99,8 @@
padding: 0.2rem 0.3rem; padding: 0.2rem 0.3rem;
box-sizing: border-box; box-sizing: border-box;
border-radius: 4px; border-radius: 4px;
color: black;
text-decoration: none;
} }
.tag::before { .tag::before {

View File

@ -95,7 +95,8 @@
.tag { .tag {
height: fit-content; height: fit-content;
background-color: lightgray; background-color: lightgray;
margin-bottom: 0.1rem; margin-bottom: 0.5rem;
margin-right: 0.5rem;
} }
.tag::before { .tag::before {
@ -111,6 +112,9 @@
padding: 0.2rem 0.3rem; padding: 0.2rem 0.3rem;
box-sizing: border-box; box-sizing: border-box;
border-radius: 4px; border-radius: 4px;
color: black;
text-decoration: none;
} }
.tag::before { .tag::before {

View File

@ -22,7 +22,9 @@
<div class="tag-list"> <div class="tag-list">
<div class="tag-header">TAGS</div> <div class="tag-header">TAGS</div>
<div class="list"> <div class="list">
<div class="tag icon">Tag 1</div> <% for(tag of tags) { %>
<a class="tag icon" href="/posts/?search=<%= tag.name %> "><%= tag.name %></a>
<% } %>
</div> </div>
</div> </div>
</div> </div>

View File

@ -35,7 +35,7 @@ model Post {
ownerid String? ownerid String?
// Tags // Tags
tags String[] tags Tag[]
// Dates // Dates
publish_date DateTime? publish_date DateTime?
@ -62,6 +62,18 @@ model Group {
permissions String[] permissions String[]
} }
model Tag {
id String @id @unique @default(uuid())
name String @unique
type TagMode @default(NORMAL)
posts Post[]
}
enum TagMode {
NORMAL
ALIAS
}
enum Role { enum Role {
LOCKED LOCKED
USER USER