Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
DATABASE_URL="postgres://root:mysecretpassword@localhost:5432/local"
TEST_USER="username"
TEST_USER_PASS="password"
TEST_USER_PASS="password"
DISABLE_COMMENTS="false"
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
test:
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.56.1-noble
image: mcr.microsoft.com/playwright:v1.57.0-noble
services:
db:
image: postgres:15
Expand Down
1,457 changes: 865 additions & 592 deletions package-lock.json

Large diffs are not rendered by default.

44 changes: 22 additions & 22 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,43 +18,43 @@
"test": "npm run test:e2e"
},
"devDependencies": {
"@eslint/compat": "^1.4.1",
"@eslint/js": "^9.39.1",
"@eslint/compat": "^2.0.0",
"@eslint/js": "^9.39.2",
"@fontsource/fira-mono": "^5.2.7",
"@neoconfetti/svelte": "^2.2.2",
"@playwright/test": "^1.56.1",
"@playwright/test": "^1.57.0",
"@sveltejs/adapter-auto": "^7.0.0",
"@sveltejs/adapter-node": "^5.4.0",
"@sveltejs/enhanced-img": "^0.8.4",
"@sveltejs/kit": "^2.48.4",
"@sveltejs/enhanced-img": "^0.9.2",
"@sveltejs/kit": "^2.49.2",
"@sveltejs/vite-plugin-svelte": "^6.2.1",
"@types/node": "^24.10.0",
"@types/node": "^25.0.3",
"@types/rss": "^0.0.32",
"@types/validator": "^13.15.4",
"date-picker-svelte": "^2.16.0",
"drizzle-kit": "^0.31.6",
"eslint": "^9.39.1",
"@types/validator": "^13.15.10",
"date-picker-svelte": "^2.17.0",
"drizzle-kit": "^0.31.8",
"eslint": "^9.39.2",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-svelte": "^3.13.0",
"globals": "^16.5.0",
"prettier": "^3.6.2",
"prettier-plugin-svelte": "^3.4.0",
"svelte": "^5.43.4",
"svelte-check": "^4.3.3",
"eslint-plugin-svelte": "^3.13.1",
"globals": "^17.0.0",
"prettier": "^3.7.4",
"prettier-plugin-svelte": "^3.4.1",
"svelte": "^5.46.1",
"svelte-check": "^4.3.5",
"typescript": "^5.9.3",
"typescript-eslint": "^8.46.3",
"vite": "^7.2.2"
"typescript-eslint": "^8.51.0",
"vite": "^7.3.0"
},
"dependencies": {
"@fontsource/courier-prime": "^5.2.8",
"@fortawesome/free-solid-svg-icons": "^7.1.0",
"@friendofsvelte/toggle": "^0.0.4-svelte.5",
"@inlang/paraglide-js": "^2.4.0",
"@inlang/paraglide-js": "^2.7.1",
"@node-rs/argon2": "^2.0.2",
"@oslojs/crypto": "^1.0.1",
"@oslojs/encoding": "^1.1.0",
"dotenv": "^17.2.3",
"drizzle-orm": "^0.44.7",
"drizzle-orm": "^0.45.1",
"drizzle-zod": "^0.8.3",
"ical-generator": "^10.0.0",
"postgres": "^3.4.7",
Expand All @@ -65,7 +65,7 @@
"svelte-exmarkdown": "^5.0.2",
"svelte-fa": "^4.0.4",
"svelte-relative-time": "^0.0.6",
"validator": "^13.15.20",
"zod": "^4.1.12"
"validator": "^13.15.26",
"zod": "^4.3.4"
}
}
4 changes: 3 additions & 1 deletion src/lib/components/blog/Post.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@
"lt-LT",
)}
</span>
<CommentCount taxonomy="blog" {slug} commentCount={post.comments} />
{#if page.data.globalCommentsEnabled}
<CommentCount taxonomy="blog" {slug} commentCount={post.comments} />
{/if}
</div>
<div class={preview ? "content preview" : "content"}>
<Markdown md={body} {plugins} />
Expand Down
21 changes: 12 additions & 9 deletions src/lib/components/blog/PostEntryForm.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import Fa from "svelte-fa";
import { slide } from "svelte/transition";
import { m } from "$lib/paraglide/messages.js";
import { page } from "$app/state";

let {
form,
Expand Down Expand Up @@ -52,15 +53,17 @@
></textarea>
{/if}
<FieldError errors={form?.errors?.body} />
<label for="disable_comments"
><input
type="checkbox"
id="disable_comments"
name="disable_comments"
checked={post.disable_comments}
/>
{m["form.disable_comments"]()}</label
>
{#if page.data.globalCommentsEnabled}
<label for="disable_comments"
><input
type="checkbox"
id="disable_comments"
name="disable_comments"
checked={post.disable_comments}
/>
{m["form.disable_comments"]()}</label
>
{/if}
<br />
<button type="submit" class="post action"
><Fa icon={faSave} /> {m.save()}</button
Expand Down
6 changes: 5 additions & 1 deletion src/lib/components/common/Header.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@
<div class="site-description">
<span>{description}</span><span class="cursor">&nbsp;</span>
</div>
<enhanced:img src={Logo} alt={description} /></a
<enhanced:img
src={Logo}
alt={description}
sizes="(min-width:500px) 500px"
/></a
>
</header>

Expand Down
14 changes: 8 additions & 6 deletions src/lib/components/events/Event.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,14 @@
minute: "2-digit",
})}
</span>
{#if !detailed}
<CommentCount
taxonomy="events"
{slug}
commentCount={event.comments}
/>
{#if page.data.globalCommentsEnabled}
{#if !detailed}
<CommentCount
taxonomy="events"
{slug}
commentCount={event.comments}
/>
{/if}
{/if}
</div>
<hr class="dim" />
Expand Down
21 changes: 12 additions & 9 deletions src/lib/components/events/EventEntryForm.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import { m } from "$lib/paraglide/messages.js";
import { DateInput } from "date-picker-svelte";
import { SvelteDate } from "svelte/reactivity";
import { page } from "$app/state";

let {
form,
Expand Down Expand Up @@ -109,15 +110,17 @@
/>
{m["form.publish_event"]()}</label
>
<label for="disable_comments"
><input
type="checkbox"
id="disable_comments"
name="disable_comments"
checked={event.disable_comments}
/>
{m["form.disable_comments"]()}</label
>
{#if page.data.globalCommentsEnabled}
<label for="disable_comments"
><input
type="checkbox"
id="disable_comments"
name="disable_comments"
checked={event.disable_comments}
/>
{m["form.disable_comments"]()}</label
>
{/if}
<br />
<button type="submit" class="post action"
><Fa icon={faSave} /> {m.save()}</button
Expand Down
7 changes: 5 additions & 2 deletions src/lib/formActions/commentActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import {
commentInsertSchema,
} from "$lib/server/db/validations";
import { z } from "zod";
import { env } from "$env/dynamic/private";

const commentsEnabled = env.DISABLE_COMMENTS === "true" ? false : true;

const queryPostId = async (slug: string): Promise<number | undefined> => {
const queryResult: table.Post | undefined = await db.query.post.findFirst({
Expand Down Expand Up @@ -43,7 +46,7 @@ export const commentActions = {
switch (parentRoute) {
case "blog": {
postId = await queryPostId(params.slug);
if (postId === -403) {
if (postId === -403 || !commentsEnabled) {
return fail(403, { errors: { submit: ["Comments are disabled"] } });
} else if (postId === -1) {
return fail(404, {
Expand All @@ -55,7 +58,7 @@ export const commentActions = {
}
case "events": {
eventId = await queryEventId(params.slug);
if (eventId === -403) {
if (eventId === -403 || !commentsEnabled) {
return fail(403, { errors: { submit: ["Comments are disabled"] } });
} else if (eventId === -1) {
return fail(404, {
Expand Down
10 changes: 4 additions & 6 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ export type CommentsArray = Array<Comment>;

export type BannedIpsArray = Array<BannedIp>;

export type Post = Omit<PostObject, "author">;
export type Post = Omit<PostObject, "disable_comments" | "author"> &
// Make "author" and "disable_comments" optional to handle some cases
// where "author" is missing/anonymous or "disable_comments" is not required in query
Partial<Pick<PostObject, "disable_comments" | "author">>;

export type PostsArray = Array<Post>;

Expand All @@ -36,11 +39,6 @@ export type PostComponent = Post & {
preview?: boolean;
} & { form?: BlogActionData };

export type LayoutData = {
user: UserInfoData;
recentComments: RecentCommentsData;
};

export type UserInfoData = {
id: string;
username: string;
Expand Down
29 changes: 20 additions & 9 deletions src/routes/+layout.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@ import { db } from "$lib/server/db";
import { sql } from "drizzle-orm";
import type { LayoutServerLoad } from "./$types";
import type { RecentComment, RecentCommentsData } from "$lib/types";
import { env } from "$env/dynamic/private";

const commentsEnabled = env.DISABLE_COMMENTS === "true" ? false : true;

const queryRecentComments = async ({ locals }) => {
const visibilityClause = locals.user
? sql``
: sql`AND (c.event_id IS NULL OR e.is_visible = TRUE)`;
if (commentsEnabled) {
const visibilityClause = locals.user
? sql``
: sql`AND (c.event_id IS NULL OR e.is_visible = TRUE)`;

try {
const result = await db.execute(sql`
try {
const result = await db.execute(sql`
SELECT
c.id AS id,
c.author AS author,
Expand All @@ -26,9 +30,12 @@ const queryRecentComments = async ({ locals }) => {
ORDER BY c.date DESC
LIMIT 5;`);

return result.map((r) => r as RecentComment);
} catch (e) {
console.error(e);
return result.map((r) => r as RecentComment);
} catch (e) {
console.error(e);
return [];
}
} else {
return [];
}
};
Expand All @@ -43,5 +50,9 @@ export const load: LayoutServerLoad = async ({ locals }) => {
recentComments[i].date = new Date(recentComments[i].date).toISOString();
}

return { user: locals.user, recentComments };
return {
user: locals.user,
recentComments,
globalCommentsEnabled: env.DISABLE_COMMENTS === "true" ? false : true,
};
};
5 changes: 4 additions & 1 deletion src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
let { data, children }: LayoutProps = $props();
let user: UserInfoData = $derived(data.user);
let recentComments: RecentCommentsData = $derived(data.recentComments);
let commentsEnabled: Boolean = $derived(data.globalCommentsEnabled);
let backgroundImage = $derived(page.data.event?.[0]?.image ?? null);
</script>

Expand Down Expand Up @@ -53,7 +54,9 @@
{/if}
<Menu />
</div>
<RecentComments {recentComments} />
{#if commentsEnabled}
<RecentComments {recentComments} />
{/if}
<ThemeToggle />
</sidebar>
</row>
Expand Down
37 changes: 32 additions & 5 deletions src/routes/blog/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ import * as table from "$lib/server/db/schema";
import { eq, desc } from "drizzle-orm";
import { postActions } from "$lib/formActions/postActions";
import type { PostsArray } from "$lib/types";
import { env } from "$env/dynamic/private";

const commentsEnabled = env.DISABLE_COMMENTS === "true" ? false : true;

export const load = (async ({
url,
}): Promise<{ posts: PostsArray; meta: { totalPosts: number }[] }> => {
const limit = Number(url.searchParams.get("limit")) || 5;
const posts = await db
.select({

const columns = {
commentsEnabled: {
id: table.post.id,
title: table.post.title,
date: table.post.date,
Expand All @@ -20,12 +24,34 @@ export const load = (async ({
image: table.post.image,
authorName: table.post.authorName,
authorUsername: table.user.username,
comments: sql<number>`COUNT(comment.id)`.as("comments"),
disable_comments: table.post.disable_comments,
})
comments: sql<number>`COUNT(${table.comment.id})`.as("comments"),
},
commentsDisabled: {
id: table.post.id,
title: table.post.title,
date: table.post.date,
body: table.post.body,
slug: table.post.slug,
image: table.post.image,
authorName: table.post.authorName,
authorUsername: table.user.username,
},
} as const;

const query = db
.select(
commentsEnabled ? columns.commentsEnabled : columns.commentsDisabled,
)
.from(table.post)
.leftJoin(table.user, eq(table.user.id, table.post.author))
.leftJoin(table.comment, eq(table.comment.postId, table.post.id))
.$dynamic();

if (commentsEnabled) {
query.leftJoin(table.comment, eq(table.comment.postId, table.post.id));
}

const posts = await query
.groupBy(
table.post.id,
table.post.title,
Expand All @@ -38,6 +64,7 @@ export const load = (async ({
)
.orderBy(desc(table.post.date))
.limit(limit);

const meta = await db.select({ totalPosts: count() }).from(table.post);

return { posts, meta };
Expand Down
Loading
Loading