Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
d4907df
wip
ddrayko Jul 23, 2026
907260b
Merge branch 'beta' of origin into beta
ddrayko Jul 23, 2026
2a1820f
wip
ddrayko Jul 23, 2026
4331ea6
wip
ddrayko Jul 23, 2026
791fde2
remove nodejs (import and all files)
ddrayko Jul 23, 2026
6ddde7a
add public to go compilation
ddrayko Jul 23, 2026
1b91388
add systemd and update CI
ddrayko Jul 23, 2026
ec6cdfd
fix: support Node.js argon2id hash format for login compatibility
ddrayko Jul 23, 2026
abc8b38
fix: use stable Go version in CI
ddrayko Jul 23, 2026
0b4e792
fix: stop service before replacing binary
ddrayko Jul 23, 2026
f5f5dbc
fix: stop service before SCP to avoid text file busy
ddrayko Jul 23, 2026
9dddaf6
fix: parse argon2id params correctly (split by $)
ddrayko Jul 23, 2026
8a5ffea
feat: add /api/config and /api/activity endpoints
ddrayko Jul 23, 2026
a1721a4
fix: route paths matching Node.js (passkeys under /api/auth, login no…
ddrayko Jul 23, 2026
3be806c
fix: passkey login handles unauthenticated users, challengeMap in mem…
ddrayko Jul 23, 2026
334630b
fix: add missing passkey columns (last_used_at, user_handle), add /un…
ddrayko Jul 23, 2026
3b51655
fix: snake_case ptero_nest_id -> camelCase pteroNestId in API respons…
ddrayko Jul 23, 2026
f1cc134
fix: fetch egg variables from Pterodactyl API with ?include=variables…
ddrayko Jul 23, 2026
2fdcd1d
fix: relationships is at top-level of API response, not inside attrib…
ddrayko Jul 23, 2026
1a3275e
fix: query egg_variables from panel DB (same as Node.js), remove ?inc…
ddrayko Jul 23, 2026
a4f0fbb
fix: use Pterodactyl API ?include=variables with top-level relationsh…
ddrayko Jul 23, 2026
78fdf21
fix: use /api/auth/login for admin login instead of non-existent /api…
ddrayko Jul 23, 2026
a161c21
fix: add POST /admin/login backend route and bump JS cache version v6…
ddrayko Jul 23, 2026
4ebbcee
fix: add /admin/check route, check isAdmin on login response, bump ca…
ddrayko Jul 23, 2026
e3916b2
fix: add enforced Content-Security-Policy header to override Cloudfla…
ddrayko Jul 23, 2026
d9d80c9
fix: add 'self' to script-src/style-src and cdn.jsdelivr.net to conne…
ddrayko Jul 23, 2026
325f1e4
fix: add missing AuthenticateToken middleware to all admin routes — R…
ddrayko Jul 23, 2026
e2061d7
fix: AdminStats response format — wrap in stats object with snake_cas…
ddrayko Jul 23, 2026
43a71f2
feat: complete rewrite of admin.go with all 35+ endpoints matching fr…
ddrayko Jul 23, 2026
e1ce8f6
fix: remove unused search variable in ListAdminServers
ddrayko Jul 23, 2026
b4a9a5f
fix: correct ptero_id -> ptero_user_id column in ListUsers; fix insta…
ddrayko Jul 23, 2026
7033770
Merge pull request #51 from ZeroHost-Code/main
zerohost-bot Jul 23, 2026
d5236e5
Merge pull request #52 from ZeroHost-Code/main
ddrayko Jul 24, 2026
fa1f7df
fix: pass sessionToken to passkey login complete endpoint
ddrayko Jul 24, 2026
6d5f950
fix: use SHA-1 for HIBP breach check (HIBP requires SHA-1)
ddrayko Jul 24, 2026
932c4dd
fix: rename systemd service from zerohost-dashboard to beta in deploy…
ddrayko Jul 24, 2026
614729d
fix: bump JS cache version v8->v9 for passkey sessionToken fix
ddrayko Jul 24, 2026
9c5ad93
debug: log sessionToken and challengeMap keys in CompleteLogin
ddrayko Jul 24, 2026
64a7928
fix: read passkey id/rawId from response wrapper in CompleteRegistration
ddrayko Jul 24, 2026
6f3de9f
fix: add gravatar.com to CSP img-src to allow avatar loading
ddrayko Jul 25, 2026
cdd346a
fix: wire ResendAPIKey, ResendFrom, BaseURL to services package
ddrayko Jul 25, 2026
01e4f59
feat: add logo for verification_resent activity in account logs
ddrayko Jul 25, 2026
d9664e9
fix: return empty array instead of null when user has no servers
ddrayko Jul 25, 2026
16af57e
fix: add search.drayko.xyz to CSP img-src for search image proxy
ddrayko Jul 25, 2026
888e165
fix: add missing TOTP routes (enable, recovery-codes/regenerate, logi…
ddrayko Jul 25, 2026
e76f28d
fix: TOTP status returns 'enabled' field matching frontend expectation
ddrayko Jul 25, 2026
3442289
fix: bump JS cache version v9->v10 for TOTP status fix
ddrayko Jul 25, 2026
6006030
fix: remove non-existent totp_verified column from SQL queries
ddrayko Jul 25, 2026
9dff069
fix: convert []byte to string in activity log to prevent base64 encod…
ddrayko Jul 25, 2026
df7f57c
chore: bump version to v2.0.0
ddrayko Jul 25, 2026
0f312d2
ci: align prod deploy workflow with beta (Go build + systemctl prod)
ddrayko Jul 25, 2026
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
88 changes: 56 additions & 32 deletions .github/workflows/deploy-beta.yml
Original file line number Diff line number Diff line change
@@ -1,32 +1,56 @@
name: Deploy Beta

on:
push:
branches: [beta]

permissions:
contents: read

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: SSH and deploy
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ secrets.SSH_PORT || 22 }}
host_key_checking: false
script: |
cd ${{ secrets.BETA_PATH }}
cp .env /tmp/beta.env 2>/dev/null; cp port.txt /tmp/beta.port.txt 2>/dev/null
git fetch origin --prune
git checkout beta
git reset --hard origin/beta
cp /tmp/beta.env .env 2>/dev/null; cp /tmp/beta.port.txt port.txt 2>/dev/null
grep 'discord.zero-host.org' public/js/app.js || echo "⚠ DISCORD LINK NOT FOUND IN FILE"
head -20 port.txt 2>/dev/null || echo "⚠ port.txt missing or empty"
npm install
pm2 restart beta
name: Deploy Beta

on:
push:
branches: [beta]

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: 'stable'

- name: Build
run: go build -o server ./cmd/server/

- name: Stop service
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ secrets.SSH_PORT || 22 }}
script: |
systemctl stop beta || true

- name: Upload binary
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ secrets.SSH_PORT || 22 }}
source: server
target: ${{ secrets.BETA_PATH }}
strip_components: 0
overwrite: true

- name: Start service
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ secrets.SSH_PORT || 22 }}
script: |
chmod +x ${{ secrets.BETA_PATH }}/server
systemctl start beta
86 changes: 56 additions & 30 deletions .github/workflows/deploy-main.yml
Original file line number Diff line number Diff line change
@@ -1,30 +1,56 @@
name: Deploy Main

on:
push:
branches: [main]

permissions:
contents: read

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: SSH and deploy
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ secrets.SSH_PORT || 22 }}
host_key_checking: false
script: |
cd ${{ secrets.MAIN_PATH }}
cp .env /tmp/main.env 2>/dev/null; cp port.txt /tmp/main.port.txt 2>/dev/null
git fetch origin --prune
git checkout main
git reset --hard origin/main
cp /tmp/main.env .env 2>/dev/null; cp /tmp/main.port.txt port.txt 2>/dev/null
npm install
pm2 restart zerohost-dashboard
name: Deploy Main

on:
push:
branches: [main]

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: 'stable'

- name: Build
run: go build -o server ./cmd/server/

- name: Stop service
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ secrets.SSH_PORT || 22 }}
script: |
systemctl stop prod || true

- name: Upload binary
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ secrets.SSH_PORT || 22 }}
source: server
target: ${{ secrets.MAIN_PATH }}
strip_components: 0
overwrite: true

- name: Start service
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ secrets.SSH_PORT || 22 }}
script: |
chmod +x ${{ secrets.MAIN_PATH }}/server
systemctl start prod
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,6 @@ uploads/
# Secrets (if any local dump)
*.pem
*.key

# Go binary
/server
105 changes: 105 additions & 0 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package main

import (
"log"
"net/http"
"os"
"os/signal"
"strings"
"syscall"

"github.com/go-chi/chi/v5"
chimw "github.com/go-chi/chi/v5/middleware"
"zerohost/dashboard/internal/config"
"zerohost/dashboard/internal/database"
"zerohost/dashboard/internal/embed"
"zerohost/dashboard/internal/middleware"
"zerohost/dashboard/internal/routes"
"zerohost/dashboard/internal/services"
)

func main() {
cfg := config.Load()

if err := database.InitPool(cfg.DatabaseDSN); err != nil {
log.Fatalf("Failed to connect to database: %v", err)
}
defer database.ClosePool()

database.RunMigrations()

services.PteroURL = cfg.PteroURL
services.PteroAPIKey = cfg.PteroAPIKey
services.PanelDBName = cfg.PanelDBName
services.ResendAPIKey = cfg.ResendAPIKey
services.ResendFrom = cfg.ResendFrom
services.BaseURL = cfg.BaseURL

if cfg.ResendAPIKey == "" {
log.Println("WARNING: RESEND_API_KEY not set — verification emails will not be sent")
}

if err := services.InitSecurity(cfg.IPQSKey, cfg.AbuseIPDBKey); err != nil {
log.Printf("Warning: security service init failed: %v", err)
}

if err := services.InitFileLogger("log.txt"); err != nil {
log.Printf("Warning: file logger init failed: %v", err)
}

services.StartScheduler()

r := chi.NewRouter()

r.Use(chimw.Logger)
r.Use(chimw.Recoverer)
r.Use(chimw.RealIP)
r.Use(middleware.SecurityHeaders)
r.Use(middleware.FileLogger)

fileServer := http.FileServer(http.FS(embed.SubFS))

r.Get("/*", func(w http.ResponseWriter, r *http.Request) {
cleanPath := strings.TrimPrefix(r.URL.Path, "/")
if cleanPath == "" {
cleanPath = "index.html"
}

if _, err := embed.SubFS.Open(cleanPath); err == nil {
fileServer.ServeHTTP(w, r)
return
}

index, err := embed.PublicFS.ReadFile("public/index.html")
if err != nil {
http.Error(w, "Not found", http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Write(index)
})

r.Route("/api", func(r chi.Router) {
routes.RegisterAuthRoutes(r)
routes.RegisterServerRoutes(r)
routes.RegisterAdminRoutes(r)
routes.RegisterNotificationRoutes(r)
r.Route("/auth", func(r chi.Router) {
routes.RegisterPasskeyRoutes(r)
routes.RegisterTOTPRoutes(r)
})
})

go func() {
addr := ":" + cfg.Port
log.Printf("ZeroHost Dashboard starting on %s", addr)
if err := http.ListenAndServe(addr, r); err != nil {
log.Fatalf("Server failed: %v", err)
}
}()

quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("Shutting down...")
}
36 changes: 0 additions & 36 deletions config/cap.js

This file was deleted.

Loading
Loading