···11+# MIT License
22+33+Copyright (c) 2025 Witchcraft Systems
44+55+Permission is hereby granted, free of charge, to any person obtaining a copy
66+of this software and associated documentation files (the "Software"), to deal
77+in the Software without restriction, including without limitation the rights
88+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
99+copies of the Software, and to permit persons to whom the Software is
1010+furnished to do so, subject to the following conditions:
1111+1212+The above copyright notice and this permission notice shall be included in all
1313+copies or substantial portions of the Software.
1414+1515+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1616+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1717+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1818+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1919+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2020+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121+SOFTWARE.
+57-1
README.md
···11# pds-dash
2233-Frontend with stats for your ATProto PDS33+a frontend dashboard with stats for your ATProto PDS.
44+55+## setup
66+77+### prerequisites
88+99+- [deno](https://deno.com/manual/getting_started/installation)
1010+1111+### installing
1212+1313+clone the repo, copy `config.ts.example` to `config.ts` and edit it to your liking.
1414+1515+then, install dependencies using deno:
1616+1717+```sh
1818+deno install
1919+```
2020+2121+### development server
2222+2323+local develompent server with hot reloading:
2424+2525+```sh
2626+deno task dev
2727+```
2828+2929+### building
3030+3131+to build the optimized bundle run:
3232+3333+```sh
3434+deno task build
3535+```
3636+3737+the output will be in the `dist/` directory.
3838+3939+## deploying
4040+4141+we use our own CI/CD workflow at [`.forgejo/workflows/deploy.yaml`](.forgejo/workflows/deploy.yaml), but it boils down to building the project bundle and deploying it to a web server. it'll probably make more sense to host it on the same domain as your PDS, but it doesn't affect anything if you host it somewhere else.
4242+4343+## configuring
4444+4545+[`config.ts`](config.ts) is the main configuration file, you can find more information in the file itself.
4646+4747+## theming
4848+4949+themes are located in the `themes/` directory, you can create your own theme by copying one of the existing themes and modifying it to your liking.
5050+5151+currently, the name of the theme is determined by the directory name, and the theme itself is defined in `theme.css` inside that directory.
5252+5353+you can switch themes by changing the `theme` property in `config.ts`.
5454+5555+the favicon is located at [`public/favicon.ico`](public/favicon.ico)
5656+5757+## license
5858+5959+MIT
-16
config.ts
···11-/**
22- * Configuration module for the PDS Dashboard
33- */
44-export class Config {
55- /**
66- * The base URL of the PDS (Personal Data Server)
77- * @default "https://pds.witchcraft.systems"
88- */
99- static readonly PDS_URL: string = "https://pds.witchcraft.systems";
1010-1111- /**
1212- * The base URL of the frontend service for linking to replies
1313- * @default "https://deer.social"
1414- */
1515- static readonly FRONTEND_URL: string = "https://deer.social";
1616-}
+44
config.ts.example
···11+/**
22+ * Configuration module for the PDS Dashboard
33+ */
44+export class Config {
55+ /**
66+ * The base URL of the PDS (Personal Data Server).
77+ * @default none
88+ */
99+ static readonly PDS_URL: string = "";
1010+1111+ /**
1212+ * Theme to be used
1313+ * @default "default"
1414+ */
1515+ static readonly THEME: string = "default";
1616+1717+ /**
1818+ * The base URL of the frontend service for linking to replies/quotes/accounts etc.
1919+ * @default "https://deer.social" // or https://bsky.app if you're boring
2020+ */
2121+ static readonly FRONTEND_URL: string = "https://deer.social";
2222+2323+ /**
2424+ * Maximum number of posts to fetch from the PDS per request
2525+ * Should be around 20 for about 10 users on the pds
2626+ * The more users you have, the lower the number should be
2727+ * since sorting is slow and is done on the frontend
2828+ * @default 20
2929+ */
3030+ static readonly MAX_POSTS: number = 20;
3131+3232+ /**
3333+ * Footer text for the dashboard, you probably want to change this. Supports HTML.
3434+ * @default "<a href='https://git.witchcraft.systems/scientific-witchery/pds-dash' target='_blank'>Source</a> (<a href='https://github.com/witchcraft-systems/pds-dash/' target='_blank'>github mirror</a>)"
3535+ */
3636+ static readonly FOOTER_TEXT: string =
3737+ "<a href='https://git.witchcraft.systems/scientific-witchery/pds-dash' target='_blank'>Source</a> (<a href='https://github.com/witchcraft-systems/pds-dash/' target='_blank'>github mirror</a>)";
3838+3939+ /**
4040+ * Whether to show the posts with timestamps that are in the future.
4141+ * @default false
4242+ */
4343+ static readonly SHOW_FUTURE_POSTS: boolean = false;
4444+}
···11+import { Plugin } from 'vite';
22+import { Config } from './config';
33+44+55+// Replaces app.css with the contents of the file specified in the
66+// config file.
77+export const themePlugin = (): Plugin => {
88+ const themeFolder = Config.THEME;
99+ console.log(`Using theme folder: ${themeFolder}`);
1010+ return {
1111+ name: 'theme-generator',
1212+ enforce: 'pre', // Ensure this plugin runs first
1313+ transform(code, id) {
1414+ if (id.endsWith('app.css')) {
1515+ // Read the theme file and replace the contents of app.css with it
1616+ // Needs full path to the file
1717+ const themeCode = Deno.readTextFileSync(Deno.cwd() + '/themes/' + themeFolder + '/theme.css');
1818+ // Replace the contents of app.css with the theme code
1919+2020+ // and add a comment at the top
2121+ const themeComment = `/* Generated from ${themeFolder} */\n`;
2222+ const themeCodeWithComment = themeComment + themeCode;
2323+ // Return the theme code as the new contents of app.css
2424+ return {
2525+ code: themeCodeWithComment,
2626+ map: null,
2727+ };
2828+ }
2929+ return null;
3030+ }
3131+ };
3232+};
+8-4
vite.config.ts
···11-import { defineConfig } from 'vite'
22-import { svelte } from '@sveltejs/vite-plugin-svelte'
11+import { defineConfig } from "vite";
22+import { svelte } from "@sveltejs/vite-plugin-svelte";
33+import { themePlugin } from "./theming";
3445// https://vite.dev/config/
56export default defineConfig({
66- plugins: [svelte()],
77-})
77+ plugins: [
88+ themePlugin(),
99+ svelte(),
1010+ ],
1111+});