mirror of
https://github.com/ProjectSegfault/website
synced 2026-03-27 17:17:54 +05:30
Merge branch 'dev'
This commit is contained in:
10
README.md
10
README.md
@@ -4,7 +4,7 @@ Live at [projectsegfau.lt](https://projectsegfau.lt).
|
||||
|
||||
## 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
|
||||
|
||||
@@ -39,13 +39,11 @@ The website has the following **mandatory** environment variables
|
||||
|
||||
| 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_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 |
|
||||
| ORIGIN | Your domain |
|
||||
| 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`
|
||||
24
package.json
24
package.json
@@ -12,29 +12,27 @@
|
||||
"format": "prettier --plugin-search-dir . --write ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify-json/ic": "^1.2.1",
|
||||
"@iconify-json/simple-icons": "^1.2.11",
|
||||
"@iconify-json/ic": "^1.2.2",
|
||||
"@iconify-json/simple-icons": "^1.2.42",
|
||||
"@sveltejs/adapter-node": "^4.0.1",
|
||||
"@sveltejs/kit": "^2.7.7",
|
||||
"@types/sanitize-html": "^2.13.0",
|
||||
"@sveltejs/kit": "^2.22.5",
|
||||
"@types/sanitize-html": "^2.16.0",
|
||||
"@unocss/reset": "^0.58.9",
|
||||
"axios": "^1.7.7",
|
||||
"axios": "^1.10.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"prettier": "^3.3.3",
|
||||
"prettier-plugin-svelte": "^3.2.7",
|
||||
"sanitize-html": "^2.13.1",
|
||||
"svelte": "^4.2.19",
|
||||
"prettier": "^3.6.2",
|
||||
"prettier-plugin-svelte": "^3.4.0",
|
||||
"sanitize-html": "^2.17.0",
|
||||
"svelte": "^4.2.20",
|
||||
"svelte-check": "^3.8.6",
|
||||
"svelte-dark-mode": "^2.1.0",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.6.3",
|
||||
"typescript": "^5.8.3",
|
||||
"unocss": "^0.58.9",
|
||||
"vite": "^5.4.10"
|
||||
"vite": "^5.4.19"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@auth/core": "^0.22.0",
|
||||
"@auth/sveltekit": "^0.8.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.1.2",
|
||||
"@unocss/extractor-svelte": "^0.58.9",
|
||||
"joi": "^17.13.3",
|
||||
|
||||
1453
pnpm-lock.yaml
generated
1453
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,4 @@
|
||||
import { SvelteKitAuth } from "@auth/sveltekit";
|
||||
import Authentik from "@auth/core/providers/authentik";
|
||||
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 {
|
||||
announcements,
|
||||
pubnixUsers,
|
||||
@@ -19,52 +13,6 @@ const agent = new Agent({
|
||||
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) => {
|
||||
return await axios(
|
||||
env.GHOST_URL +
|
||||
@@ -110,7 +58,10 @@ const updateMap = async () => {
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetchGhost("posts");
|
||||
const res = await axios(env.GHOST_ALLPOSTS_URL, {
|
||||
httpsAgent: agent,
|
||||
timeout: 10000
|
||||
});
|
||||
|
||||
if (res.status === 200) {
|
||||
blogPosts.set(res.data);
|
||||
|
||||
@@ -13,9 +13,7 @@
|
||||
text: "Wiki",
|
||||
external: true
|
||||
},
|
||||
// Temporary workaround.
|
||||
//{ href: "/blog", text: "Blog" },
|
||||
{ href: "https://blog.projectsegfau.lt", text: "Blog" },
|
||||
{ href: "/blog", text: "Blog" },
|
||||
|
||||
{ href: "/donate", text: "Donate" },
|
||||
{ href: "/contact", text: "Contact" },
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
<button
|
||||
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"
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -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>
|
||||
@@ -1,7 +0,0 @@
|
||||
import type { PageLoad } from "./$types";
|
||||
|
||||
export const load = (() => {
|
||||
return {
|
||||
title: "Admin dashboard"
|
||||
};
|
||||
}) satisfies PageLoad;
|
||||
@@ -1,19 +1,22 @@
|
||||
import type { PageServerLoad } from "./$types";
|
||||
import fetchGhost from "../fetchGhost";
|
||||
import type { PageServerLoad, PageServerLoadEvent } from "./$types";
|
||||
import { blogPosts } from "../../../stores";
|
||||
import { get } from "svelte/store";
|
||||
|
||||
export const load = (async ({ params, fetch }) => {
|
||||
const data = await fetchGhost("posts/slug/" + params.title);
|
||||
// yes this was made by gitbub copilot
|
||||
|
||||
const allPosts = get(blogPosts);
|
||||
const meta = {
|
||||
title: !allPosts.error ? data.posts[0].title : ""
|
||||
};
|
||||
const load: PageServerLoad = async ({ params }: PageServerLoadEvent) => {
|
||||
const allPosts = get(blogPosts) as { error?: boolean; posts?: any[] };
|
||||
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 {
|
||||
post: !allPosts.error ? data.posts[0] : {},
|
||||
allPosts: allPosts,
|
||||
...meta
|
||||
};
|
||||
}) satisfies PageServerLoad;
|
||||
export { load };
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
{#each category.data as instance}
|
||||
<a
|
||||
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}
|
||||
<img
|
||||
@@ -94,7 +94,7 @@
|
||||
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}
|
||||
<img
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<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
|
||||
identifiable. This means that, for example, what website you visited and
|
||||
what path you visited (like https://libreddit.projectsegfau.lt/r/cats), your
|
||||
|
||||
@@ -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;
|
||||
@@ -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}
|
||||
Reference in New Issue
Block a user