Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/workflows/mirror.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: mirror

on: [push]

jobs:
mirror:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: yesolutions/mirror-action@master
with:
REMOTE: "https://codeberg.org/FyraStack/dashboard.git"
GIT_USERNAME: ${{ secrets.GIT_USERNAME }}
GIT_PASSWORD: ${{ secrets.GIT_PASSWORD }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@ vite.config.ts.timestamp-*

# traces for local dev
Trace-*.json

bun.lock
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@

1. `pnpm install`
2. Start a Postgres server: `podman run -p 5432:5432 -e POSTGRES_PASSWORD=mysecretpassword -d postgres`
3. Add secrets to `.env`
4. Set up the database: `pnpm --filter stack-dashboard db:push`
5. `pnpm --filter stack-dashboard dev`
3. Copy `apps/dashboard/.env.example` to `apps/dashboard/.env`
4. Add secrets to `apps/dashboard/.env`
- Update `CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_HYPERDRIVE` and set all `VPC` stuff to false
- set `BETTER_AUTH_SECRET`, `AUTUMN_SECRET` to anything
5. Set up the database: `pnpm --filter stack-dashboard db:migrate`
6. `pnpm --filter stack-dashboard dev`

### Postgres Setup

Expand All @@ -21,6 +24,7 @@
- Generate Better Auth
- Autumn
- Postgres connection URL
- if you use the podman command(s) listed above, the connection string is `postgres://postgres:mysecretpassword@127.0.0.1:5432/postgres`
- Highly recommended:
- Proxmox
- Optional:
Expand Down
19 changes: 11 additions & 8 deletions apps/dashboard/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ BETTER_AUTH_SECRET=""
# VyOS
# In production, router updates are routed through the VYOS_VPC_01 / VYOS_VPC_02
# Workers VPC bindings (router 1 first, falling back to router 2 if it is offline).
# Set VYOS_USE_VPC="false" to bypass VPC and talk directly to VYOS_API_URL instead.
# VPC bindings only exist in the deployed Workers — keep VYOS_USE_VPC="false" in
# local dev so requests go directly to VYOS_API_URL instead.
VYOS_API_URL=""
VYOS_API_KEY=""
VYOS_USE_VPC="true"
VYOS_USE_VPC="false"
# Only used for the direct (non-VPC) fallback; the VPC path terminates TLS itself.
VYOS_VERIFY_SSL="false"

Expand All @@ -41,18 +42,20 @@ GITHUB_CLIENT_SECRET=""

# Proxmox VE backend
# In production, requests are routed through the PROXMOX_VPC Workers VPC binding,
# which terminates TLS. Set PROXMOX_USE_VPC="false" to bypass VPC and talk directly
# to PROXMOX_API_URL (self-signed certs are accepted automatically outside production).
# which terminates TLS. VPC bindings only exist in the deployed Workers — keep
# PROXMOX_USE_VPC="false" in local dev so requests go directly to PROXMOX_API_URL
# (self-signed certs are accepted automatically outside production).
PROXMOX_API_URL="https://pve.example.com:8006"
PROXMOX_TOKEN_ID="root@pam!stack"
PROXMOX_TOKEN_SECRET=""
PROXMOX_USE_VPC="true"
PROXMOX_USE_VPC="false"
PROXMOX_VM_FIREWALL_SECURITY_GROUP=""

# In production, snippet uploads are routed through the SNIPPETS Workers VPC binding.
# Set PROXMOX_SNIPPETS_USE_VPC="false" to bypass VPC and talk directly to the endpoint
# below (PROXMOX_SNIPPETS_ENDPOINT_VERIFY_SSL then governs TLS for that direct path).
PROXMOX_SNIPPETS_USE_VPC="true"
# VPC bindings only exist in the deployed Workers — keep this "false" in local dev to
# talk directly to the endpoint below (PROXMOX_SNIPPETS_ENDPOINT_VERIFY_SSL then
# governs TLS for that direct path).
PROXMOX_SNIPPETS_USE_VPC="false"
PROXMOX_SNIPPETS_ENDPOINT_URL="https://pxmx-01.example:8005"
PROXMOX_SNIPPETS_ENDPOINT_USERNAME="pvecic"
PROXMOX_SNIPPETS_ENDPOINT_PASSWORD="change-me"
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"dev": "NODE_TLS_REJECT_UNAUTHORIZED=0 vite dev",
"build": "vite build",
"preview": "vite preview",
"cf:dev": "pnpm run build && wrangler dev --local-protocol http",
"cf:dev": "pnpm run build && wrangler dev -c wrangler.local.jsonc --local-protocol http",
"cf:dev:remote": "pnpm run build && wrangler dev --remote",
"cf:deploy": "wrangler deploy --env=\"\"",
"cf:deploy:preview": "pnpm run build && wrangler deploy --env preview",
Expand Down
8 changes: 8 additions & 0 deletions apps/dashboard/src/lib/name-generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const choices = [
["kitten", "cat", "neko", "puppy", "dog", "doggy"],
["box", "stack", "rack", "server", "node"],
];

const choose = (i: int) => choices[i][Math.floor(Math.random() * choices[i].length)];

export const generateServerName = () => choose(0) + choose(1);
8 changes: 8 additions & 0 deletions apps/dashboard/src/lib/server/name-generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const choices = [
["kitten", "cat", "neko", "puppy", "dog", "doggy"],
["box", "stack", "rack", "server", "node"],
];

const choose = (i: int) => choices[i][Math.floor(Math.random() * choices[i].length)];

export const generateServerName = () => choose(0) + choose(1);
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@
EyeOff,
HardDriveUpload,
Plus,
X
X,
Dices
} from '@lucide/svelte';
import { generateServerName } from '$lib/name-generator';

type PageData = {
currentProject?: { id: string } | null;
Expand Down Expand Up @@ -123,7 +125,10 @@
const ipv6Available = $derived(Boolean(data.ipamAvailability?.ipv6.available));
const bothNetworksAvailable = $derived(ipv4Available && ipv6Available);

let serverName = $state('');
let serverName = $state(generateServerName());
function regenerateServerName() {
serverName = generateServerName();
}
let selectedImageId = $state<string | null>(null);
let selectedImageVersion = $state<string | null>(null);
let selectedPlanId = $state<string | null>(null);
Expand Down Expand Up @@ -417,12 +422,15 @@
>Name</span
>
</div>
<div class="mt-3">
<div class="mt-3 flex">
<Input
bind:value={serverName}
placeholder="my-server"
class="h-11 text-base sm:h-9 sm:text-sm"
/>
<Button variant="outline" class="h-11 sm:h-9" onclick={regenerateServerName}
><Dices /></Button
>
</div>
</div>

Expand Down
6 changes: 5 additions & 1 deletion apps/dashboard/svelte.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ const config = {
}
},
kit: {
adapter: adapter(),
adapter: adapter({
platformProxy: {
configPath: 'wrangler.local.jsonc'
}
}),
experimental: {
remoteFunctions: true
}
Expand Down
50 changes: 50 additions & 0 deletions apps/dashboard/wrangler.local.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"$schema": "../../node_modules/wrangler/config-schema.json",
"name": "stack-dashboard",
"main": ".svelte-kit/cloudflare/_worker.js",
"compatibility_date": "2026-04-05",
"compatibility_flags": ["nodejs_compat"],
"assets": {
"binding": "ASSETS",
"directory": ".svelte-kit/cloudflare",
"run_worker_first": [
"/",
"/_app/remote/*",
"/api/*",
"/admin/*",
"/projects/*",
"/login*",
"/register*",
"/signup*",
"/accept-invitation/*"
]
},
"vars": {
"ORIGIN": "http://localhost:5173",
"EMAIL_FROM_ADDRESS": "no-reply@example.com",
"EMAIL_FROM_NAME": "Fyra Stack",
"EMAIL_REPLY_TO": "support@example.com",
"AUTUMN_DEFAULT_PLAN_ID": "stack",
"AUTUMN_SERVER_ENTITY_FEATURE_ID": "active_servers",
"VYOS_USE_VPC": "false",
"PROXMOX_USE_VPC": "false",
"PROXMOX_SNIPPETS_USE_VPC": "false"
},
"hyperdrive": [
{
"binding": "HYPERDRIVE",
"id": "25228ed6f77e49fb8041aed12879327d"
}
],
"kv_namespaces": [
{
"binding": "FEATURE_FLAGS",
"id": "0ad17de8747747bca1975d473e66e295"
}
],
"send_email": [
{
"name": "EMAIL"
}
]
}