├── .dockerignore ├── .github └── workflows │ ├── docker-dev.yml │ └── docker.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc ├── Dockerfile ├── LICENSE ├── README.md ├── compose.yml ├── flake.lock ├── flake.nix ├── package.json ├── pnpm-lock.yaml ├── src ├── app.css ├── app.d.ts ├── app.html ├── hooks.server.ts ├── lib │ ├── BlogCard │ │ ├── Meta.svelte │ │ ├── PostContent.svelte │ │ ├── PostOuter.svelte │ │ ├── PostsContainer.svelte │ │ ├── ReadMore.svelte │ │ ├── SingleWordLists.svelte │ │ ├── Title.svelte │ │ └── index.ts │ ├── Footer.svelte │ ├── Hero.svelte │ ├── Nav │ │ ├── Link.svelte │ │ ├── Logo.svelte │ │ ├── Nav.svelte │ │ └── ThemeToggle.svelte │ ├── PMargin.svelte │ └── PageTransition.svelte ├── routes │ ├── +error.svelte │ ├── +layout.svelte │ ├── +layout.ts │ ├── +page.server.ts │ ├── +page.svelte │ ├── admin │ │ ├── +page.svelte │ │ └── +page.ts │ ├── blog │ │ ├── +page.server.ts │ │ ├── +page.svelte │ │ ├── [title] │ │ │ ├── +page.server.ts │ │ │ └── +page.svelte │ │ ├── authors │ │ │ ├── +page.server.ts │ │ │ ├── +page.svelte │ │ │ └── [author] │ │ │ │ ├── +page.server.ts │ │ │ │ └── +page.svelte │ │ ├── fetchGhost.ts │ │ └── tags │ │ │ ├── +page.server.ts │ │ │ ├── +page.svelte │ │ │ └── [tag] │ │ │ ├── +page.server.ts │ │ │ └── +page.svelte │ ├── contact │ │ ├── +page.svelte │ │ └── +page.ts │ ├── donate │ │ ├── +page.svelte │ │ ├── +page.ts │ │ └── CryptoInfo.svelte │ ├── instances │ │ ├── +page.server.ts │ │ ├── +page.svelte │ │ ├── advanced │ │ │ ├── +page.server.ts │ │ │ └── +page.svelte │ │ └── instances.ts │ ├── legal │ │ ├── +page.svelte │ │ ├── +page.ts │ │ ├── privacy-policy │ │ │ ├── +page.svelte │ │ │ └── +page.ts │ │ └── tos │ │ │ ├── +page.svelte │ │ │ └── +page.ts │ ├── login │ │ ├── +page.server.ts │ │ └── +page.svelte │ ├── pubnix │ │ ├── +page.server.ts │ │ ├── +page.svelte │ │ ├── User.svelte │ │ ├── faq │ │ │ ├── +page.svelte │ │ │ └── +page.ts │ │ ├── register │ │ │ ├── +page.server.ts │ │ │ └── +page.svelte │ │ └── users │ │ │ ├── +page.server.ts │ │ │ └── +page.svelte │ └── team │ │ ├── +page.svelte │ │ ├── +page.ts │ │ └── Team.json └── stores.ts ├── static ├── JetBrainsMono.woff2 ├── favicon.png ├── icons │ ├── FreshRSS-logo.svg │ ├── akkoma.png │ ├── anonymousoverflow.png │ ├── authentik.svg │ ├── breezewiki.svg │ ├── cinny.svg │ ├── cockpit.svg │ ├── element.svg │ ├── ente.svg │ ├── gitea.svg │ ├── gothub.svg │ ├── healthchecks.svg │ ├── hedgedoc.svg │ ├── hydrogen.svg │ ├── hyperpipe.svg │ ├── invidious.svg │ ├── jitsi.svg │ ├── kbin.svg │ ├── librarian.png │ ├── libreddit.png │ ├── libretranslate.png │ ├── mailu.png │ ├── matrix.svg │ ├── mozhi.svg │ ├── nextcloud.svg │ ├── nitter.png │ ├── owncloud.svg │ ├── piped.svg │ ├── plausible.png │ ├── pubnix.png │ ├── rimgo.png │ ├── rssbridge-short.svg │ ├── safetwitch.png │ ├── searxng.svg │ ├── shoelace.svg │ ├── simplelogin.svg │ ├── teddit.png │ ├── timetagger.svg │ ├── vaultwarden.svg │ ├── vikunja.png │ ├── wikijs.svg │ └── xmpp.svg ├── logo.png ├── logo.svg └── qr │ ├── Bitcoin.png │ ├── Litecoin.png │ ├── Monero.png │ └── UPI.png ├── svelte.config.js ├── tsconfig.json ├── uno.config.ts └── vite.config.ts /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.github/workflows/docker-dev.yml: -------------------------------------------------------------------------------- 1 | name: Docker dev 2 | 3 | on: 4 | push: 5 | branches: 6 | - "dev" 7 | 8 | jobs: 9 | build: 10 | name: "Build" 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Set up QEMU 14 | uses: docker/setup-qemu-action@v2 15 | - name: Set up Docker Buildx 16 | uses: docker/setup-buildx-action@v2 17 | 18 | - name: "Build:checkout" 19 | uses: actions/checkout@v3 20 | 21 | - name: Log in to the Container registry 22 | uses: docker/login-action@v2 23 | with: 24 | registry: ghcr.io 25 | username: ProjectSegfault 26 | password: ${{ secrets.ACCESS_TOKEN }} 27 | 28 | - name: "Build:dockerimage" 29 | uses: docker/build-push-action@v3 30 | with: 31 | tags: ghcr.io/projectsegfault/website:dev 32 | context: "." 33 | push: true 34 | no-cache: true 35 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: Docker 2 | 3 | on: 4 | push: 5 | branches: 6 | - "master" 7 | 8 | jobs: 9 | build: 10 | name: "Build" 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Set up QEMU 14 | uses: docker/setup-qemu-action@v2 15 | - name: Set up Docker Buildx 16 | uses: docker/setup-buildx-action@v2 17 | 18 | - name: "Build:checkout" 19 | uses: actions/checkout@v3 20 | 21 | - name: Log in to the Container registry 22 | uses: docker/login-action@v2 23 | with: 24 | registry: ghcr.io 25 | username: ProjectSegfault 26 | password: ${{ secrets.ACCESS_TOKEN }} 27 | 28 | - name: "Build:dockerimage" 29 | uses: docker/build-push-action@v3 30 | with: 31 | tags: ghcr.io/projectsegfault/website:latest 32 | context: "." 33 | push: true 34 | no-cache: true 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | vite.config.js.timestamp-* 10 | vite.config.ts.timestamp-* 11 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | 15 | # Ignore NixOS flake lock 16 | flake.lock 17 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "useTabs": true, 4 | "bracketSpacing": true, 5 | "singleAttributePerLine": true, 6 | "trailingComma": "none", 7 | "plugins": ["prettier-plugin-svelte"], 8 | "pluginSearchDirs": ["."], 9 | "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] 10 | } 11 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-alpine 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY package.json ./ 6 | COPY pnpm-lock.yaml ./ 7 | 8 | RUN npm install -g pnpm 9 | 10 | RUN pnpm i 11 | 12 | COPY . . 13 | 14 | RUN pnpm build 15 | 16 | EXPOSE 3000 17 | 18 | CMD ["node", "build/index.js"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 - present, Project Segfault team 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Project Segfault website 2 | 3 | Live at [projectsegfau.lt](https://projectsegfau.lt). 4 | 5 | ## Developing 6 | 7 | > You need a lot of infrastructure to run a complete version of the website including: Ghost CMS deployment and Authentik authentication. 8 | 9 | ### Prerequisites 10 | 11 | - Install [node.js](https://nodejs.org). 12 | - Install [pnpm](https://pnpm.io/). 13 | - Learn [Svelte](https://svelte.dev). 14 | - Add the environment variables from the [environment variables section](#environment-variables). 15 | 16 | ### Running a dev server. 17 | 18 | 1. Clone the repository using `git clone https://github.com/ProjectSegfault/website`. 19 | 2. Change directory into the clone using `cd ./website`. 20 | 3. Install dependencies using `pnpm i`. 21 | 4. Run the dev server using `pnpm dev`. 22 | 5. Open a browser on `http://localhost:5173/` and you should see the website running locally! 23 | 24 | ## Running in production 25 | 26 | In production you can run the website through Docker Compose or locally. We strongly recommend using Docker since it makes everything 10 times easier. 27 | 28 | ### Docker 29 | 30 | First install Docker and Docker Compose on your system (use Linux if you are sane). After that add the environment variables from the [environment variables section](#environment-variables) and run `docker compose up -d` in the directory of the source code (or just the compose.yml file if you aren't building from source). If you are using Portainer (if you aren't, start using it) you should add a new stack in the Stacks section and select the compose file option, then copy the compose.yml file. 31 | 32 | ### Locally 33 | 34 | If you want to run the website locally in production follow the steps in [developing](#developing) but use `node build` instead of `pnpm dev` and expect the website to be in `http://localhost:3000`. 35 | 36 | ## Environment variables 37 | 38 | The website has the following **mandatory** environment variables 39 | 40 | | Name | Description | 41 | | :----------------- | :--------------------------------------------- | 42 | | AUTH_CLIENT_ID | Authentik client ID | 43 | | AUTH_CLIENT_SECRET | Authentik client secret | 44 | | AUTH_ISSUER | Authentication issuer URL | 45 | | AUTH_TRUST_HOST | Your domain | 46 | | AUTH_SECRET | Random 32 char secret | 47 | | GHOST_URL | Your Ghost CMS URL | 48 | | GHOST_API_KEY | Your Ghost CMS API key | 49 | | KUMA_URL | Your Uptime Kuma announcements URL | 50 | | ORIGIN | Your domain | 51 | | ADDRESS_HEADER | Header used to retrieve client IP (Caddy only) | 52 | -------------------------------------------------------------------------------- /compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | website: 3 | container_name: website 4 | image: ghcr.io/projectsegfault/website:latest 5 | restart: unless-stopped 6 | # uncomment these lines if you want to build from source 7 | #build: 8 | # context: . 9 | # dockerfile: Dockerfile 10 | ports: 11 | - "127.0.0.1:1339:3000" 12 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "inputs": { 5 | "systems": "systems" 6 | }, 7 | "locked": { 8 | "lastModified": 1689068808, 9 | "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", 10 | "owner": "numtide", 11 | "repo": "flake-utils", 12 | "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", 13 | "type": "github" 14 | }, 15 | "original": { 16 | "id": "flake-utils", 17 | "type": "indirect" 18 | } 19 | }, 20 | "nixpkgs": { 21 | "locked": { 22 | "lastModified": 1691209513, 23 | "narHash": "sha256-V8e6wywJ34DgwP4sDSWpHMPx3OC+k1l/xPSQTfdPj5U=", 24 | "owner": "NixOS", 25 | "repo": "nixpkgs", 26 | "rev": "cc0e47cd36a2d6815eba5abf3317e519abd35571", 27 | "type": "github" 28 | }, 29 | "original": { 30 | "owner": "NixOS", 31 | "ref": "master", 32 | "repo": "nixpkgs", 33 | "type": "github" 34 | } 35 | }, 36 | "root": { 37 | "inputs": { 38 | "flake-utils": "flake-utils", 39 | "nixpkgs": "nixpkgs", 40 | "systems": "systems_2" 41 | } 42 | }, 43 | "systems": { 44 | "locked": { 45 | "lastModified": 1681028828, 46 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 47 | "owner": "nix-systems", 48 | "repo": "default", 49 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 50 | "type": "github" 51 | }, 52 | "original": { 53 | "owner": "nix-systems", 54 | "repo": "default", 55 | "type": "github" 56 | } 57 | }, 58 | "systems_2": { 59 | "locked": { 60 | "lastModified": 1681028828, 61 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 62 | "owner": "nix-systems", 63 | "repo": "default", 64 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 65 | "type": "github" 66 | }, 67 | "original": { 68 | "owner": "nix-systems", 69 | "repo": "default", 70 | "type": "github" 71 | } 72 | } 73 | }, 74 | "root": "root", 75 | "version": 7 76 | } 77 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs.nixpkgs.url = "github:NixOS/nixpkgs/master"; 3 | inputs.systems.url = "github:nix-systems/default"; 4 | 5 | outputs = { 6 | self, 7 | nixpkgs, 8 | flake-utils, 9 | systems, 10 | }: 11 | flake-utils.lib.eachSystem (import systems) 12 | (system: let 13 | pkgs = import nixpkgs { 14 | inherit system; 15 | }; 16 | in { 17 | devShells.default = pkgs.mkShell { 18 | buildInputs = [ 19 | pkgs.nodejs 20 | 21 | # Package manager 22 | pkgs.nodePackages.pnpm 23 | ]; 24 | }; 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "project-segfault-website", 3 | "version": "3.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vite dev", 7 | "build": "vite build", 8 | "preview": "vite preview", 9 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 10 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", 11 | "lint": "prettier --plugin-search-dir . --check .", 12 | "format": "prettier --plugin-search-dir . --write ." 13 | }, 14 | "devDependencies": { 15 | "@iconify-json/ic": "^1.2.1", 16 | "@iconify-json/simple-icons": "^1.2.11", 17 | "@sveltejs/adapter-node": "^4.0.1", 18 | "@sveltejs/kit": "^2.7.7", 19 | "@types/sanitize-html": "^2.13.0", 20 | "@unocss/reset": "^0.58.9", 21 | "axios": "^1.7.7", 22 | "dayjs": "^1.11.13", 23 | "prettier": "^3.3.3", 24 | "prettier-plugin-svelte": "^3.2.7", 25 | "sanitize-html": "^2.13.1", 26 | "svelte": "^4.2.19", 27 | "svelte-check": "^3.8.6", 28 | "svelte-dark-mode": "^2.1.0", 29 | "tslib": "^2.8.1", 30 | "typescript": "^5.6.3", 31 | "unocss": "^0.58.9", 32 | "vite": "^5.4.10" 33 | }, 34 | "type": "module", 35 | "dependencies": { 36 | "@auth/core": "^0.22.0", 37 | "@auth/sveltekit": "^0.8.0", 38 | "@sveltejs/vite-plugin-svelte": "^3.1.2", 39 | "@unocss/extractor-svelte": "^0.58.9", 40 | "joi": "^17.13.3", 41 | "snarkdown": "^2.0.0", 42 | "svelte-hcaptcha": "^0.1.1" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/app.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "JetBrains Mono"; 3 | src: url("/JetBrainsMono.woff2"); 4 | font-display: swap; 5 | } 6 | 7 | html, 8 | html.light { 9 | --accent: #00a584; 10 | --accent-translucent: #00a58498; 11 | --font-primary: "JetBrains Mono", monospace; 12 | --primary: #ffffff; 13 | --secondary: #e9e9e9; 14 | --tertiary: #939393; 15 | --text: #444444; 16 | --grey: #cecece; 17 | --alt: #ddd; 18 | --alt-text: #333; 19 | --black: #151515; 20 | --body-text: #666; 21 | color-scheme: light; 22 | } 23 | 24 | html.dark { 25 | --primary: #151515; 26 | --secondary: #1d1d1d; 27 | --tertiary: #353535; 28 | --text: #ffffffde; 29 | --grey: #5454547a; 30 | --alt: #333; 31 | --alt-text: #ddd; 32 | --body-text: #bbb; 33 | color-scheme: dark; 34 | } 35 | 36 | @media (prefers-color-scheme: dark) { 37 | html { 38 | --primary: #151515; 39 | --secondary: #1d1d1d; 40 | --tertiary: #353535; 41 | --text: #ffffffde; 42 | --grey: #5454547a; 43 | --alt: #333; 44 | --alt-text: #ddd; 45 | --body-text: #bbb; 46 | color-scheme: dark; 47 | } 48 | } 49 | 50 | body { 51 | @apply bg-primary text-text font-primary m-0 leading-loose min-h-screen transition-colors duration-200; 52 | } 53 | 54 | ::selection { 55 | @apply bg-accentTranslucent; 56 | } 57 | 58 | a { 59 | @apply text-accent underline underline-offset-4 transition-filter duration-200; 60 | } 61 | 62 | a:hover { 63 | @apply brightness-70; 64 | } 65 | 66 | h1 { 67 | @apply text-4xl font-bold mt-8 mb-2 border-b-2 border-accent pb-2; 68 | } 69 | 70 | .h1-no-lg { 71 | @apply mt-8 mb-2 border-b-2 border-accent pb-2; 72 | } 73 | 74 | h2 { 75 | @apply text-3xl font-bold mt-8 mb-2; 76 | } 77 | 78 | h3 { 79 | @apply text-2xl font-bold mt-8 mb-2; 80 | } 81 | 82 | h4 { 83 | @apply text-xl font-bold mt-8 mb-2; 84 | } 85 | 86 | p { 87 | color: var(--body-text); 88 | } 89 | 90 | /* Between-paragraph spacing */ 91 | p + p { 92 | @apply mt-4; 93 | } 94 | 95 | summary { 96 | @apply cursor-pointer; 97 | } 98 | 99 | .button { 100 | @apply px-2 py-1 bg-accent text-primary rounded no-underline flex flex-row items-center gap-2; 101 | } 102 | -------------------------------------------------------------------------------- /src/app.d.ts: -------------------------------------------------------------------------------- 1 | // See https://kit.svelte.dev/docs/types#app 2 | // for information about these interfaces 3 | // and what to do when importing types 4 | declare global { 5 | namespace App { 6 | // interface Error {} 7 | // interface Locals {} 8 | // interface PageData {} 9 | // interface Platform {} 10 | } 11 | } 12 | 13 | export {}; 14 | -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 13 | 18 | %sveltekit.head% 19 | 20 | 21 |
%sveltekit.body%
22 | 23 | 24 | -------------------------------------------------------------------------------- /src/hooks.server.ts: -------------------------------------------------------------------------------- 1 | import { SvelteKitAuth } from "@auth/sveltekit"; 2 | import Authentik from "@auth/core/providers/authentik"; 3 | import { env } from "$env/dynamic/private"; 4 | import type { Provider } from "@auth/core/providers"; 5 | import type { Profile } from "@auth/core/types"; 6 | import { redirect, type Handle } from "@sveltejs/kit"; 7 | import { sequence } from "@sveltejs/kit/hooks"; 8 | import { 9 | announcements, 10 | pubnixUsers, 11 | blogPosts, 12 | blogTags, 13 | blogAuthors 14 | } from "./stores"; 15 | import axios from "axios"; 16 | import { Agent } from "https"; 17 | 18 | const agent = new Agent({ 19 | family: 4 20 | }); 21 | 22 | const hasAuth = 23 | !env.AUTH_CLIENT_ID || 24 | !env.AUTH_CLIENT_SECRET || 25 | !env.AUTH_ISSUER || 26 | !env.AUTH_TRUST_HOST || 27 | !env.AUTH_SECRET 28 | ? false 29 | : true; 30 | 31 | export const handle: Handle = sequence( 32 | //@ts-ignore 33 | SvelteKitAuth({ 34 | providers: [ 35 | Authentik({ 36 | clientId: env.AUTH_CLIENT_ID, 37 | clientSecret: env.AUTH_CLIENT_SECRET, 38 | issuer: env.AUTH_ISSUER 39 | }) as Provider 40 | ] 41 | }), 42 | hasAuth 43 | ? async ({ event, resolve }) => { 44 | if (event.url.pathname.startsWith("/admin")) { 45 | const session = await event.locals.getSession(); 46 | if (!session) { 47 | throw redirect(303, "/login"); 48 | } 49 | } 50 | 51 | const result = await resolve(event, { 52 | transformPageChunk: ({ html }) => html 53 | }); 54 | return result; 55 | } 56 | : async ({ event, resolve }) => { 57 | if (event.url.pathname.startsWith("/admin")) { 58 | throw redirect(303, "/login"); 59 | } 60 | 61 | const result = await resolve(event, { 62 | transformPageChunk: ({ html }) => html 63 | }); 64 | return result; 65 | } 66 | ); 67 | 68 | export const fetchGhost = async (action: string, additional?: string) => { 69 | return await axios( 70 | env.GHOST_URL + 71 | "/ghost/api/content/" + 72 | action + 73 | "/?key=" + 74 | env.GHOST_API_KEY + 75 | "&include=authors,tags&limit=all&formats=html,plaintext" + 76 | additional || "", 77 | { httpsAgent: agent, timeout: 10000 } 78 | ); 79 | }; 80 | 81 | const updateMap = async () => { 82 | try { 83 | const res = await axios(env.KUMA_URL, { 84 | httpsAgent: agent, 85 | timeout: 10000 86 | }); 87 | 88 | if (res.status === 200) { 89 | announcements.set(res.data); 90 | } else { 91 | announcements.set({ error: true, message: "Error: " + res.status }); 92 | } 93 | } catch (err) { 94 | announcements.set({ error: true, message: "Error: " + err }); 95 | } 96 | 97 | try { 98 | const res = await axios("https://publapi.p.projectsegfau.lt/users", { 99 | httpsAgent: agent, 100 | timeout: 10000 101 | }); 102 | 103 | if (res.status === 200) { 104 | pubnixUsers.set(res.data); 105 | } else { 106 | pubnixUsers.set({ error: true, message: "Error: " + res.status }); 107 | } 108 | } catch (err) { 109 | pubnixUsers.set({ error: true, message: "Error: " + err }); 110 | } 111 | 112 | try { 113 | const res = await fetchGhost("posts"); 114 | 115 | if (res.status === 200) { 116 | blogPosts.set(res.data); 117 | } else { 118 | blogPosts.set({ error: true, message: "Error: " + res.status }); 119 | } 120 | } catch (err) { 121 | blogPosts.set({ error: true, message: "Error: " + err }); 122 | } 123 | 124 | try { 125 | const res = await fetchGhost("tags"); 126 | 127 | if (res.status === 200) { 128 | blogTags.set(res.data); 129 | } else { 130 | blogTags.set({ error: true, message: "Error: " + res.status }); 131 | } 132 | } catch (err) { 133 | blogTags.set({ error: true, message: "Error: " + err }); 134 | } 135 | 136 | try { 137 | const res = await fetchGhost("authors"); 138 | 139 | if (res.status === 200) { 140 | blogAuthors.set(res.data); 141 | } else { 142 | blogAuthors.set({ error: true, message: "Error: " + res.status }); 143 | } 144 | } catch (err) { 145 | blogAuthors.set({ error: true, message: "Error: " + err }); 146 | } 147 | }; 148 | 149 | updateMap(); 150 | 151 | setInterval(updateMap, 30000); 152 | -------------------------------------------------------------------------------- /src/lib/BlogCard/Meta.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 |
9 | {#if post.tags.length > 0} 10 |
11 |
12 | {#each post.tags as tag} 13 | {tag.name} 19 | {/each} 20 |
21 | {/if} 22 | {#each post.authors as author} 23 |
27 | {author.name} 29 | {/each} 30 |
32 | {dayjs(post.published_at).format("ddd, DD MMM YYYY HH:mm")} 34 |
36 | {post.plaintext.trim().split(/\s+/).length} words 38 |
40 | {post.reading_time} minute read 42 |
43 | -------------------------------------------------------------------------------- /src/lib/BlogCard/PostContent.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 | {#if data.post.feature_image} 7 | {data.post.title} image 12 | {/if} 13 | {@html data.post.html} 14 |
15 | -------------------------------------------------------------------------------- /src/lib/BlogCard/PostOuter.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
9 | 10 | 11 | {#if url} 12 | View on Ghost 16 | {/if} 17 |
18 | -------------------------------------------------------------------------------- /src/lib/BlogCard/PostsContainer.svelte: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | -------------------------------------------------------------------------------- /src/lib/BlogCard/ReadMore.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | {post.plaintext.split(" ").slice(0, 20).join(" ") + "..."} 6 | Read more... 7 | -------------------------------------------------------------------------------- /src/lib/BlogCard/SingleWordLists.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 | {#each items as item} 8 | {item.name} 12 | {/each} 13 |
14 | -------------------------------------------------------------------------------- /src/lib/BlogCard/Title.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | {#if !isPost} 7 | {#if post.feature_image} 8 | {post.title} image 13 | {/if} 14 | {post.title} 19 | {:else} 20 | {post.title} 21 | {/if} 22 | -------------------------------------------------------------------------------- /src/lib/BlogCard/index.ts: -------------------------------------------------------------------------------- 1 | export { default as PostsContainer } from "./PostsContainer.svelte"; 2 | export { default as PostOuter } from "./PostOuter.svelte"; 3 | export { default as Title } from "./Title.svelte"; 4 | export { default as Meta } from "./Meta.svelte"; 5 | export { default as ReadMore } from "./ReadMore.svelte"; 6 | export { default as PostContent } from "./PostContent.svelte"; 7 | export { default as SingleWordLists } from "./SingleWordLists.svelte"; 8 | -------------------------------------------------------------------------------- /src/lib/Footer.svelte: -------------------------------------------------------------------------------- 1 |
2 |
5 |

6 | Made with SvelteKit 7 |

8 | 9 | Source code 10 |
11 |
12 | -------------------------------------------------------------------------------- /src/lib/Hero.svelte: -------------------------------------------------------------------------------- 1 |
2 |

3 | Project Segfault 4 |

5 |

6 | Open-source development and hosted services. 7 |

8 |
9 |
13 | Instances 15 |
19 | Donate 21 | 28 |
29 | -------------------------------------------------------------------------------- /src/lib/Nav/Link.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 20 | {#if link.icon} 21 |
22 | {/if} 23 | {link.text} 24 | 25 | -------------------------------------------------------------------------------- /src/lib/Nav/Logo.svelte: -------------------------------------------------------------------------------- 1 | 5 | Project Segfault logo 10 | Project Segfault 12 | -------------------------------------------------------------------------------- /src/lib/Nav/Nav.svelte: -------------------------------------------------------------------------------- 1 | 76 | 77 | 82 | 83 | 127 | 128 | 149 | -------------------------------------------------------------------------------- /src/lib/Nav/ThemeToggle.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 30 | 31 | 36 | -------------------------------------------------------------------------------- /src/lib/PMargin.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |

7 | 8 |

9 | -------------------------------------------------------------------------------- /src/lib/PageTransition.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | {#key pathname} 7 |
11 | 12 |
13 | {/key} 14 | -------------------------------------------------------------------------------- /src/routes/+error.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |

6 | {$page.status} 7 |

{$page.error?.message}

8 |

9 | -------------------------------------------------------------------------------- /src/routes/+layout.svelte: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | {title} 21 | {#if $page.data.description} 22 | 26 | {/if} 27 | 28 | 29 |