···11+# Logs
22+logs
33+*.log
44+npm-debug.log*
55+yarn-debug.log*
66+yarn-error.log*
77+pnpm-debug.log*
88+lerna-debug.log*
99+1010+node_modules
1111+dist
1212+dist-ssr
1313+*.local
1414+1515+# Editor directories and files
1616+.vscode/*
1717+!.vscode/extensions.json
1818+.idea
1919+.DS_Store
2020+*.suo
2121+*.ntvs*
2222+*.njsproj
2323+*.sln
2424+*.sw?
2525+2626+# Diagnostic reports (https://nodejs.org/api/report.html)
2727+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
2828+2929+# Runtime data
3030+pids
3131+*.pid
3232+*.seed
3333+*.pid.lock
3434+3535+# Directory for instrumented libs generated by jscoverage/JSCover
3636+lib-cov
3737+3838+# Coverage directory used by tools like istanbul
3939+coverage
4040+*.lcov
4141+4242+# nyc test coverage
4343+.nyc_output
4444+4545+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
4646+.grunt
4747+4848+# Bower dependency directory (https://bower.io/)
4949+bower_components
5050+5151+# node-waf configuration
5252+.lock-wscript
5353+5454+# Compiled binary addons (https://nodejs.org/api/addons.html)
5555+build/Release
5656+5757+# Dependency directories
5858+node_modules/
5959+jspm_packages/
6060+6161+# Snowpack dependency directory (https://snowpack.dev/)
6262+web_modules/
6363+6464+# TypeScript cache
6565+*.tsbuildinfo
6666+6767+# Optional npm cache directory
6868+.npm
6969+7070+# Optional eslint cache
7171+.eslintcache
7272+7373+# Optional stylelint cache
7474+.stylelintcache
7575+7676+# Microbundle cache
7777+.rpt2_cache/
7878+.rts2_cache_cjs/
7979+.rts2_cache_es/
8080+.rts2_cache_umd/
8181+8282+# Optional REPL history
8383+.node_repl_history
8484+8585+# Output of 'npm pack'
8686+*.tgz
8787+8888+# Yarn Integrity file
8989+.yarn-integrity
9090+9191+# dotenv environment variable files
9292+.env
9393+.env.development.local
9494+.env.test.local
9595+.env.production.local
9696+.env.local
9797+9898+# parcel-bundler cache (https://parceljs.org/)
9999+.cache
100100+.parcel-cache
101101+102102+# Next.js build output
103103+.next
104104+out
105105+106106+# Nuxt.js build / generate output
107107+.nuxt
108108+dist
109109+110110+# Gatsby files
111111+.cache/
112112+# Comment in the public line in if your project uses Gatsby and not Next.js
113113+# https://nextjs.org/blog/next-9-1#public-directory-support
114114+# public
115115+116116+# vuepress build output
117117+.vuepress/dist
118118+119119+# vuepress v2.x temp and cache directory
120120+.temp
121121+.cache
122122+123123+# vitepress build output
124124+**/.vitepress/dist
125125+126126+# vitepress cache directory
127127+**/.vitepress/cache
128128+129129+# Docusaurus cache and generated files
130130+.docusaurus
131131+132132+# Serverless directories
133133+.serverless/
134134+135135+# FuseBox cache
136136+.fusebox/
137137+138138+# DynamoDB Local files
139139+.dynamodb/
140140+141141+# TernJS port file
142142+.tern-port
143143+144144+# Stores VSCode versions used for testing VSCode extensions
145145+.vscode-test
146146+147147+# yarn v2
148148+.yarn/cache
149149+.yarn/unplugged
150150+.yarn/build-state.yml
151151+.yarn/install-state.gz
152152+.pnp.*
153153+154154+# Config files
155155+config.ts
+21
LICENSE
···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.
+59
README.md
···11+# pds-dash
22+33+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
···11+import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
22+33+export default {
44+ // Consult https://svelte.dev/docs#compile-time-svelte-preprocess
55+ // for more information about preprocessors
66+ preprocess: vitePreprocess(),
77+};
···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+};
+20
tsconfig.app.json
···11+{
22+ "extends": "@tsconfig/svelte/tsconfig.json",
33+ "compilerOptions": {
44+ "target": "ESNext",
55+ "useDefineForClassFields": true,
66+ "module": "ESNext",
77+ "resolveJsonModule": true,
88+ /**
99+ * Typecheck JS in `.svelte` and `.js` files by default.
1010+ * Disable checkJs if you'd like to use dynamic types in JS.
1111+ * Note that setting allowJs false does not prevent the use
1212+ * of JS in `.svelte` files.
1313+ */
1414+ "allowJs": true,
1515+ "checkJs": true,
1616+ "isolatedModules": true,
1717+ "moduleDetection": "force"
1818+ },
1919+ "include": ["src/**/*.ts", "src/**/*.js", "src/**/*.svelte"]
2020+}