Merge branch 'dev'

This commit is contained in:
midou committed 2025-07-17 13:54:09 +01:00
commit 4d3494996a
13 files changed
+796 -876

No files matched your search

+4 -6
View File
@@ -4,7 +4,7 @@ Live at [projectsegfau.lt](https://projectsegfau.lt).
## Developing ## Developing
> You need a lot of infrastructure to run a complete version of the website including: Ghost CMS deployment and Authentik authentication. > You need a lot of infrastructure to run a complete version of the website including a Ghost CMS deployment.
### Prerequisites ### Prerequisites
@@ -39,13 +39,11 @@ The website has the following **mandatory** environment variables
| Name | Description | | Name | Description |
| :----------------- | :--------------------------------------------- | | :----------------- | :--------------------------------------------- |
| AUTH_CLIENT_ID | Authentik client ID |
| AUTH_CLIENT_SECRET | Authentik client secret |
| AUTH_ISSUER | Authentication issuer URL |
| AUTH_TRUST_HOST | Your domain |
| AUTH_SECRET | Random 32 char secret |
| GHOST_URL | Your Ghost CMS URL | | GHOST_URL | Your Ghost CMS URL |
| GHOST_API_KEY | Your Ghost CMS API key | | GHOST_API_KEY | Your Ghost CMS API key |
| GHOST_ALLPOSTS_URL | Your URL for all ghost posts, see below |
| KUMA_URL | Your Uptime Kuma announcements URL | | KUMA_URL | Your Uptime Kuma announcements URL |
| ORIGIN | Your domain | | ORIGIN | Your domain |
| ADDRESS_HEADER | Header used to retrieve client IP (Caddy only) | | ADDRESS_HEADER | Header used to retrieve client IP (Caddy only) |
> GHOST_ALLPOSTS_URL should be of the form `https://GHOST_URL/ghost/api/content/posts/?key=GHOST_API_KEY&include=authors,tags&limit=all&formats=html,plaintext`
+11 -13
View File
@@ -12,29 +12,27 @@
"format": "prettier --plugin-search-dir . --write ." "format": "prettier --plugin-search-dir . --write ."
}, },
"devDependencies": { "devDependencies": {
"@iconify-json/ic": "^1.2.1", "@iconify-json/ic": "^1.2.2",
"@iconify-json/simple-icons": "^1.2.11", "@iconify-json/simple-icons": "^1.2.42",
"@sveltejs/adapter-node": "^4.0.1", "@sveltejs/adapter-node": "^4.0.1",
"@sveltejs/kit": "^2.7.7", "@sveltejs/kit": "^2.22.5",
"@types/sanitize-html": "^2.13.0", "@types/sanitize-html": "^2.16.0",
"@unocss/reset": "^0.58.9", "@unocss/reset": "^0.58.9",
"axios": "^1.7.7", "axios": "^1.10.0",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"prettier": "^3.3.3", "prettier": "^3.6.2",
"prettier-plugin-svelte": "^3.2.7", "prettier-plugin-svelte": "^3.4.0",
"sanitize-html": "^2.13.1", "sanitize-html": "^2.17.0",
"svelte": "^4.2.19", "svelte": "^4.2.20",
"svelte-check": "^3.8.6", "svelte-check": "^3.8.6",
"svelte-dark-mode": "^2.1.0", "svelte-dark-mode": "^2.1.0",
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.6.3", "typescript": "^5.8.3",
"unocss": "^0.58.9", "unocss": "^0.58.9",
"vite": "^5.4.10" "vite": "^5.4.19"
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@auth/core": "^0.22.0",
"@auth/sveltekit": "^0.8.0",
"@sveltejs/vite-plugin-svelte": "^3.1.2", "@sveltejs/vite-plugin-svelte": "^3.1.2",
"@unocss/extractor-svelte": "^0.58.9", "@unocss/extractor-svelte": "^0.58.9",
"joi": "^17.13.3", "joi": "^17.13.3",
+755 -698
View File
File diff suppressed because it is too large. Load diff
+4 -53
View File
@@ -1,10 +1,4 @@
import { SvelteKitAuth } from "@auth/sveltekit";
import Authentik from "@auth/core/providers/authentik";
import { env } from "$env/dynamic/private"; import { env } from "$env/dynamic/private";
import type { Provider } from "@auth/core/providers";
import type { Profile } from "@auth/core/types";
import { redirect, type Handle } from "@sveltejs/kit";
import { sequence } from "@sveltejs/kit/hooks";
import { import {
announcements, announcements,
pubnixUsers, pubnixUsers,
@@ -19,52 +13,6 @@ const agent = new Agent({
family: 4 family: 4
}); });
const hasAuth =
!env.AUTH_CLIENT_ID ||
!env.AUTH_CLIENT_SECRET ||
!env.AUTH_ISSUER ||
!env.AUTH_TRUST_HOST ||
!env.AUTH_SECRET
? false
: true;
export const handle: Handle = sequence(
//@ts-ignore
SvelteKitAuth({
providers: [
Authentik({
clientId: env.AUTH_CLIENT_ID,
clientSecret: env.AUTH_CLIENT_SECRET,
issuer: env.AUTH_ISSUER
}) as Provider<Profile>
]
}),
hasAuth
? async ({ event, resolve }) => {
if (event.url.pathname.startsWith("/admin")) {
const session = await event.locals.getSession();
if (!session) {
throw redirect(303, "/login");
}
}
const result = await resolve(event, {
transformPageChunk: ({ html }) => html
});
return result;
}
: async ({ event, resolve }) => {
if (event.url.pathname.startsWith("/admin")) {
throw redirect(303, "/login");
}
const result = await resolve(event, {
transformPageChunk: ({ html }) => html
});
return result;
}
);
export const fetchGhost = async (action: string, additional?: string) => { export const fetchGhost = async (action: string, additional?: string) => {
return await axios( return await axios(
env.GHOST_URL + env.GHOST_URL +
@@ -110,7 +58,10 @@ const updateMap = async () => {
} }
try { try {
const res = await fetchGhost("posts"); const res = await axios(env.GHOST_ALLPOSTS_URL, {
httpsAgent: agent,
timeout: 10000
});
if (res.status === 200) { if (res.status === 200) {
blogPosts.set(res.data); blogPosts.set(res.data);
+1 -3
View File
@@ -13,9 +13,7 @@
text: "Wiki", text: "Wiki",
external: true external: true
}, },
// Temporary workaround. { href: "/blog", text: "Blog" },
//{ href: "/blog", text: "Blog" },
{ href: "https://blog.projectsegfau.lt", text: "Blog" },
{ href: "/donate", text: "Donate" }, { href: "/donate", text: "Donate" },
{ href: "/contact", text: "Contact" }, { href: "/contact", text: "Contact" },
+1 -1
View File
@@ -17,7 +17,7 @@
<button <button
on:click={toggle} on:click={toggle}
class="theme-toggle button text-text flex items-center text-sm" class="theme-toggle button text-text flex items-center text-sm navPlus1:!p-x-[.25rem]"
aria-label="Toggle theme" aria-label="Toggle theme"
> >
<div <div
-8
View File
@@ -1,8 +0,0 @@
<script lang="ts">
import type { PageData } from "./$types";
export let data: PageData;
</script>
<h1>{data.title}</h1>
<p>Nothing here yet.</p>
-7
View File
@@ -1,7 +0,0 @@
import type { PageLoad } from "./$types";
export const load = (() => {
return {
title: "Admin dashboard"
};
}) satisfies PageLoad;
+17 -14
View File
@@ -1,19 +1,22 @@
import type { PageServerLoad } from "./$types"; import type { PageServerLoad, PageServerLoadEvent } from "./$types";
import fetchGhost from "../fetchGhost";
import { blogPosts } from "../../../stores"; import { blogPosts } from "../../../stores";
import { get } from "svelte/store"; import { get } from "svelte/store";
export const load = (async ({ params, fetch }) => { // yes this was made by gitbub copilot
const data = await fetchGhost("posts/slug/" + params.title);
const allPosts = get(blogPosts); const load: PageServerLoad = async ({ params }: PageServerLoadEvent) => {
const meta = { const allPosts = get(blogPosts) as { error?: boolean; posts?: any[] };
title: !allPosts.error ? data.posts[0].title : "" let post: any = {};
}; let title = "";
if (allPosts && !allPosts.error && Array.isArray(allPosts.posts)) {
post = allPosts.posts.find((p: any) => p.slug === params.title) || {};
title = post.title || "";
}
return {
post,
allPosts,
title
};
};
return { export { load };
post: !allPosts.error ? data.posts[0] : {},
allPosts: allPosts,
...meta
};
}) satisfies PageServerLoad;
+2 -2
View File
@@ -63,7 +63,7 @@
{#each category.data as instance} {#each category.data as instance}
<a <a
href={instance.geo || instance.eu || instance.in } href={instance.geo || instance.eu || instance.in }
class="flex flex-row items-center gap-4 rounded bg-secondary p-4 w-110 no-underline text-text" class="flex flex-row items-center gap-4 rounded bg-secondary p-4 w-110 no-underline text-text break-all"
> >
{#if instance.icon} {#if instance.icon}
<img <img
@@ -94,7 +94,7 @@
instance.geo || instance.geo ||
instance.eu || instance.eu ||
instance.in} instance.in}
class="flex flex-row items-center gap-4 rounded bg-secondary p-4 w-110 no-underline text-text" class="flex flex-row items-center gap-4 rounded bg-secondary p-4 w-110 no-underline text-text break-all"
> >
{#if instance.icon} {#if instance.icon}
<img <img
+1 -1
View File
@@ -9,7 +9,7 @@
<h2>We don't collect more information than we need to.</h2> <h2>We don't collect more information than we need to.</h2>
<PMargin <PMargin class="break-all"
>We have disabled request logging. This is because it is extremely >We have disabled request logging. This is because it is extremely
identifiable. This means that, for example, what website you visited and identifiable. This means that, for example, what website you visited and
what path you visited (like https://libreddit.projectsegfau.lt/r/cats), your what path you visited (like https://libreddit.projectsegfau.lt/r/cats), your
-23
View File
@@ -1,23 +0,0 @@
import { env } from "$env/dynamic/private";
import type { PageServerLoad } from "./$types";
export const load = (async ({ locals }) => {
const meta = {
title: "Login"
};
const hasAuth =
!env.AUTH_CLIENT_ID ||
!env.AUTH_CLIENT_SECRET ||
!env.AUTH_ISSUER ||
!env.AUTH_TRUST_HOST ||
!env.AUTH_SECRET
? false
: true;
return {
session: hasAuth ? await locals.getSession() : undefined,
hasAuth,
...meta
};
}) satisfies PageServerLoad;
-47
View File
@@ -1,47 +0,0 @@
<script lang="ts">
import { signIn, signOut } from "@auth/sveltekit/client";
import { page } from "$app/stores";
import type { PageData } from "./$types";
const buttonStyles = "button w-fit";
export let data: PageData;
</script>
<h1>{data.title}</h1>
{#if data.hasAuth}
{#if Object.keys($page.data.session || {}).length}
<div class="flex flex-col gap-4">
<div class="flex flex-row items-center gap-1">
<span>Signed in as</span><br />
<span class="font-extrabold"
>{$page?.data?.session?.user?.email}</span
>
</div>
<a href="/admin">Go to admin dashboard</a>
<button
on:click={() => signOut()}
class={buttonStyles}
><div class="i-ic:outline-logout" />
Sign out</button
>
</div>
{:else}
<div class="flex flex-col gap-4">
<span>You are not signed in</span>
<button
on:click={() => signIn("authentik")}
class={buttonStyles}
><div class="i-ic:outline-login" />
Sign in using Authentik</button
>
</div>
{/if}
{:else}
<div class="flex flex-col gap-4">
<span>Authentik is not configured</span>
<a href="https://goauthentik.io/docs/installation"
>Configure Authentik</a
>
</div>
{/if}