diff --git a/app/routes/page.tsx b/src/routes/routes.tsx
similarity index 80%
rename from app/routes/page.tsx
rename to src/routes/routes.tsx
index 531b6d82..d238e6f9 100644
--- a/app/routes/page.tsx
+++ b/src/routes/routes.tsx
@@ -1,8 +1,13 @@
+import { createFileRoute } from '@tanstack/react-router'
import RoutePageContent from '@/components/routes/content/Content'
import RouteContentDialog from '@/components/routes/content/ContentDialog'
import RoutesSidebar from '@/components/routes/Sidebar'
-export default function RoutesPage() {
+export const Route = createFileRoute('/routes')({
+ component: RoutesPage,
+})
+
+function RoutesPage() {
return (
<>
diff --git a/app/servers/page.tsx b/src/routes/servers.tsx
similarity index 71%
rename from app/servers/page.tsx
rename to src/routes/servers.tsx
index 5e3ab384..5faec76b 100644
--- a/app/servers/page.tsx
+++ b/src/routes/servers.tsx
@@ -1,9 +1,14 @@
+import { createFileRoute } from '@tanstack/react-router'
+import { Suspense } from 'react'
import AllSystemInfoProvider from '@/components/servers/AllSystemInfoProvider'
import ServerContent from '@/components/servers/content/Content'
import ServersSidebar from '@/components/servers/Sidebar'
-import { Suspense } from 'react'
-export default function ServersPage() {
+export const Route = createFileRoute('/servers')({
+ component: ServersPage,
+})
+
+function ServersPage() {
return (
diff --git a/src/routes/wiki.$.tsx b/src/routes/wiki.$.tsx
new file mode 100644
index 00000000..d7c8a62b
--- /dev/null
+++ b/src/routes/wiki.$.tsx
@@ -0,0 +1,44 @@
+import { extname, join } from 'node:path'
+import { createFileRoute } from '@tanstack/react-router'
+
+function isSafe(pathSegs: string[]): boolean {
+ return !pathSegs.some(seg => seg === '..')
+}
+
+export const Route = createFileRoute('/wiki/$')({
+ server: {
+ handlers: {
+ GET: async ({ request }) => {
+ // decodeURLComponent to resolve %2e path traversal
+ let pathSegs = decodeURIComponent(new URL(request.url).pathname)
+ .slice('/wiki'.length)
+ .split('/')
+
+ if (!isSafe(pathSegs)) {
+ return new Response('Forbidden', { status: 403 })
+ }
+
+ let fileName = pathSegs.at(-1)
+ if (!fileName) {
+ pathSegs = []
+ fileName = 'index.html'
+ } else {
+ pathSegs.pop()
+ if (!extname(fileName)) {
+ fileName = `${fileName}.html`
+ }
+ }
+ const fullPath = join('public', 'wiki', ...pathSegs, fileName)
+ const file = Bun.file(fullPath)
+ if (await file.exists()) {
+ return new Response(await file.bytes(), {
+ headers: {
+ 'Content-Type': file.type,
+ },
+ })
+ }
+ return new Response(`${fullPath} not found`, { status: 404 })
+ },
+ },
+ },
+})
diff --git a/site-config.ts b/src/site-config.ts
similarity index 100%
rename from site-config.ts
rename to src/site-config.ts
diff --git a/app/globals.css b/src/styles.css
similarity index 92%
rename from app/globals.css
rename to src/styles.css
index bc38950b..ed168872 100644
--- a/app/globals.css
+++ b/src/styles.css
@@ -7,9 +7,15 @@
--color-background: var(--background);
--color-foreground: var(--foreground);
- --font-sans: var(--font-sans);
- --font-mono: var(--font-geist-mono);
- --font-serif: var(--font-serif);
+ --font-geist: var(--font-geist-sans);
+ --font-geist-mono: var(--font-geist-mono);
+
+ --font-sans:
+ var(--font-geist), ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
+ 'Segoe UI Symbol', 'Noto Color Emoji';
+ --font-mono:
+ var(--font-geist-mono), ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
+ 'Liberation Mono', 'Courier New', monospace;
--color-sidebar-ring: var(--sidebar-ring);
--color-sidebar-border: var(--sidebar-border);
@@ -69,6 +75,13 @@
}
:root {
+ --font-geist-sans:
+ 'Geist', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
+ sans-serif;
+ --font-geist-mono:
+ 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
+ 'Courier New', monospace;
+
--background: oklch(0.9821 0 0);
--foreground: oklch(0.2435 0 0);
--card: oklch(0.9911 0 0);
diff --git a/types/compose-spec.json b/src/types/compose-spec.json
similarity index 100%
rename from types/compose-spec.json
rename to src/types/compose-spec.json
diff --git a/types/compose-spec.ts b/src/types/compose-spec.ts
similarity index 100%
rename from types/compose-spec.ts
rename to src/types/compose-spec.ts
diff --git a/types/file.ts b/src/types/file.ts
similarity index 100%
rename from types/file.ts
rename to src/types/file.ts
diff --git a/types/godoxy/acl.schema.json b/src/types/godoxy/acl.schema.json
similarity index 100%
rename from types/godoxy/acl.schema.json
rename to src/types/godoxy/acl.schema.json
diff --git a/types/godoxy/autocert.schema.json b/src/types/godoxy/autocert.schema.json
similarity index 100%
rename from types/godoxy/autocert.schema.json
rename to src/types/godoxy/autocert.schema.json
diff --git a/types/godoxy/config.schema.json b/src/types/godoxy/config.schema.json
similarity index 100%
rename from types/godoxy/config.schema.json
rename to src/types/godoxy/config.schema.json
diff --git a/types/godoxy/config/access_log.ts b/src/types/godoxy/config/access_log.ts
similarity index 100%
rename from types/godoxy/config/access_log.ts
rename to src/types/godoxy/config/access_log.ts
diff --git a/types/godoxy/config/acl.ts b/src/types/godoxy/config/acl.ts
similarity index 100%
rename from types/godoxy/config/acl.ts
rename to src/types/godoxy/config/acl.ts
diff --git a/types/godoxy/config/autocert.ts b/src/types/godoxy/config/autocert.ts
similarity index 95%
rename from types/godoxy/config/autocert.ts
rename to src/types/godoxy/config/autocert.ts
index eacecc7b..659a4f93 100644
--- a/types/godoxy/config/autocert.ts
+++ b/src/types/godoxy/config/autocert.ts
@@ -34,11 +34,10 @@ export type AutocertConfigWithoutExtra =
| PorkbunOptions
| OtherOptions
-export type AutocertConfig =
- AutocertConfigWithoutExtra & {
- /** Extra certificates */
- extra?: AutocertExtra[]
- }
+export type AutocertConfig = AutocertConfigWithoutExtra & {
+ /** Extra certificates */
+ extra?: AutocertExtra[]
+}
export interface AutocertConfigBase {
/** ACME email */
diff --git a/types/godoxy/config/config.ts b/src/types/godoxy/config/config.ts
similarity index 100%
rename from types/godoxy/config/config.ts
rename to src/types/godoxy/config/config.ts
diff --git a/types/godoxy/config/docker.ts b/src/types/godoxy/config/docker.ts
similarity index 92%
rename from types/godoxy/config/docker.ts
rename to src/types/godoxy/config/docker.ts
index 58d7f3fd..6af0cb3f 100644
--- a/types/godoxy/config/docker.ts
+++ b/src/types/godoxy/config/docker.ts
@@ -1,6 +1,6 @@
import type { Hostname, Port, URL } from '../types'
-export { type DockerConfig, type DockerProviderMap }
+export type { DockerConfig, DockerProviderMap }
/** Name-value mapping of docker hosts to retrieve routes from
*
diff --git a/types/godoxy/config/entrypoint.ts b/src/types/godoxy/config/entrypoint.ts
similarity index 100%
rename from types/godoxy/config/entrypoint.ts
rename to src/types/godoxy/config/entrypoint.ts
diff --git a/types/godoxy/config/homepage.ts b/src/types/godoxy/config/homepage.ts
similarity index 100%
rename from types/godoxy/config/homepage.ts
rename to src/types/godoxy/config/homepage.ts
diff --git a/types/godoxy/config/maxmind.ts b/src/types/godoxy/config/maxmind.ts
similarity index 100%
rename from types/godoxy/config/maxmind.ts
rename to src/types/godoxy/config/maxmind.ts
diff --git a/types/godoxy/config/notification.ts b/src/types/godoxy/config/notification.ts
similarity index 100%
rename from types/godoxy/config/notification.ts
rename to src/types/godoxy/config/notification.ts
diff --git a/types/godoxy/config/providers.ts b/src/types/godoxy/config/providers.ts
similarity index 100%
rename from types/godoxy/config/providers.ts
rename to src/types/godoxy/config/providers.ts
diff --git a/types/godoxy/config/rules.ts b/src/types/godoxy/config/rules.ts
similarity index 100%
rename from types/godoxy/config/rules.ts
rename to src/types/godoxy/config/rules.ts
diff --git a/types/godoxy/deref.ts b/src/types/godoxy/deref.ts
similarity index 100%
rename from types/godoxy/deref.ts
rename to src/types/godoxy/deref.ts
diff --git a/types/godoxy/docker.ts b/src/types/godoxy/docker.ts
similarity index 100%
rename from types/godoxy/docker.ts
rename to src/types/godoxy/docker.ts
diff --git a/types/godoxy/docker_routes.schema.json b/src/types/godoxy/docker_routes.schema.json
similarity index 100%
rename from types/godoxy/docker_routes.schema.json
rename to src/types/godoxy/docker_routes.schema.json
diff --git a/types/godoxy/index.ts b/src/types/godoxy/index.ts
similarity index 91%
rename from types/godoxy/index.ts
rename to src/types/godoxy/index.ts
index a29073f3..b1204a4f 100644
--- a/types/godoxy/index.ts
+++ b/src/types/godoxy/index.ts
@@ -1,3 +1,4 @@
+/** biome-ignore-all assist/source/organizeImports: */
import type * as AccessLog from './config/access_log'
import type * as ACL from './config/acl'
import type * as Autocert from './config/autocert'
@@ -37,6 +38,13 @@ import type MaxmindSchemaDeref from './maxmind.schema.deref.json'
import type MiddlewareComposeSchemaDeref from './middleware_compose.schema.deref.json'
import type RoutesSchemaDeref from './routes.schema.deref.json'
+// fixes: ReferenceError: Buffer is not defined on client side
+if (typeof window !== 'undefined') {
+ window.Buffer = {
+ isBuffer: () => false,
+ } as unknown as typeof Buffer
+}
+
import $ from '@apidevtools/json-schema-ref-parser'
const ACLSchema = (await $.dereference(_ACLSchema)) as typeof ACLSchemaDeref
@@ -75,5 +83,5 @@ export {
type Notification,
type Providers,
type Proxmox,
- type Routes
+ type Routes,
}
diff --git a/types/godoxy/maxmind.schema.json b/src/types/godoxy/maxmind.schema.json
similarity index 100%
rename from types/godoxy/maxmind.schema.json
rename to src/types/godoxy/maxmind.schema.json
diff --git a/types/godoxy/middleware_compose.schema.json b/src/types/godoxy/middleware_compose.schema.json
similarity index 100%
rename from types/godoxy/middleware_compose.schema.json
rename to src/types/godoxy/middleware_compose.schema.json
diff --git a/types/godoxy/middlewares/middleware_compose.ts b/src/types/godoxy/middlewares/middleware_compose.ts
similarity index 100%
rename from types/godoxy/middlewares/middleware_compose.ts
rename to src/types/godoxy/middlewares/middleware_compose.ts
diff --git a/types/godoxy/middlewares/middlewares.ts b/src/types/godoxy/middlewares/middlewares.ts
similarity index 99%
rename from types/godoxy/middlewares/middlewares.ts
rename to src/types/godoxy/middlewares/middlewares.ts
index 27f23dcc..a5ac0462 100644
--- a/types/godoxy/middlewares/middlewares.ts
+++ b/src/types/godoxy/middlewares/middlewares.ts
@@ -77,8 +77,7 @@ type KeyOptMapping = Record,
+ extends KeyOptMapping,
KeyOptMapping,
KeyOptMapping,
KeyOptMapping,
@@ -263,7 +262,7 @@ export type Crowdsec = {
/** Crowdsec AppSec endpoint path
*
* @default "/"
- */
+ */
endpoint?: string
/** Log blocked requests
*
diff --git a/types/godoxy/providers/healthcheck.ts b/src/types/godoxy/providers/healthcheck.ts
similarity index 100%
rename from types/godoxy/providers/healthcheck.ts
rename to src/types/godoxy/providers/healthcheck.ts
diff --git a/types/godoxy/providers/homepage.ts b/src/types/godoxy/providers/homepage.ts
similarity index 100%
rename from types/godoxy/providers/homepage.ts
rename to src/types/godoxy/providers/homepage.ts
diff --git a/types/godoxy/providers/idlewatcher.ts b/src/types/godoxy/providers/idlewatcher.ts
similarity index 100%
rename from types/godoxy/providers/idlewatcher.ts
rename to src/types/godoxy/providers/idlewatcher.ts
diff --git a/types/godoxy/providers/loadbalance.ts b/src/types/godoxy/providers/loadbalance.ts
similarity index 100%
rename from types/godoxy/providers/loadbalance.ts
rename to src/types/godoxy/providers/loadbalance.ts
diff --git a/types/godoxy/providers/proxmox.ts b/src/types/godoxy/providers/proxmox.ts
similarity index 100%
rename from types/godoxy/providers/proxmox.ts
rename to src/types/godoxy/providers/proxmox.ts
diff --git a/types/godoxy/providers/routes.ts b/src/types/godoxy/providers/routes.ts
similarity index 100%
rename from types/godoxy/providers/routes.ts
rename to src/types/godoxy/providers/routes.ts
diff --git a/types/godoxy/routes.schema.json b/src/types/godoxy/routes.schema.json
similarity index 100%
rename from types/godoxy/routes.schema.json
rename to src/types/godoxy/routes.schema.json
diff --git a/types/godoxy/types.ts b/src/types/godoxy/types.ts
similarity index 100%
rename from types/godoxy/types.ts
rename to src/types/godoxy/types.ts
diff --git a/types/health.ts b/src/types/health.ts
similarity index 100%
rename from types/health.ts
rename to src/types/health.ts
diff --git a/types/schema.ts b/src/types/schema.ts
similarity index 98%
rename from types/schema.ts
rename to src/types/schema.ts
index e58966d6..1fe2e7f8 100644
--- a/types/schema.ts
+++ b/src/types/schema.ts
@@ -289,9 +289,9 @@ function getDefaultValue(
function isObjectishSchema(inner: JSONSchema): boolean {
return Boolean(
isSchemaType(inner, 'object') ||
- inner.properties ||
- inner.additionalProperties ||
- inner.patternProperties
+ inner.properties ||
+ inner.additionalProperties ||
+ inner.patternProperties
)
}
diff --git a/test-run.compose.yml b/test-run.compose.yml
index 3a681bae..bcc36f4a 100644
--- a/test-run.compose.yml
+++ b/test-run.compose.yml
@@ -7,31 +7,11 @@ services:
env_file: .env
read_only: true
tmpfs:
- - /app/.next/cache
+ - /tmp:rw
+ - /app/node_modules/.cache:rw
security_opt:
- no-new-privileges:true
cap_drop:
- all
labels:
proxy.aliases: godoxy-next
- proxy.godoxy-next.rules: |
- - name: login page
- on: path /login
- do: pass
- - name: protected
- on: |
- !path regex("(_next/static|_next/image|favicon.ico).*")
- !path glob("/api/v1/auth/*")
- !path glob("/auth/*")
- !path regex("[A-Za-z0-9_-]+\.(svg|png|jpg|jpeg|gif|ico|webp|woff2?|eot|ttf|otf|txt)(\?.+)?")
- !path /api/v1/version
- !path /manifest.json
- do: require_auth
- - name: proxy to backend
- on: path glob("/api/v1/*")
- do: proxy http://$${API_ADDR}/
- - name: proxy to auth api
- on: path glob("/auth/*")
- do: |
- rewrite /auth /api/v1/auth
- proxy http://$${API_ADDR}/
diff --git a/tsconfig.json b/tsconfig.json
index 196cc338..498d6b4c 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -2,6 +2,7 @@
"compilerOptions": {
"target": "ES2022",
"lib": ["dom", "dom.iterable", "esnext"],
+ "types": ["vite/client","vite-plugin-pwa/client"],
"allowJs": true,
"skipLibCheck": true,
"useDefineForClassFields": true,
@@ -24,25 +25,13 @@
"isolatedModules": true,
"jsx": "react-jsx",
"incremental": true,
- "plugins": [
- {
- "name": "next"
- }
- ],
"paths": {
- "@/*": ["./*"],
+ "@/*": ["./src/*"],
"juststore": ["./juststore/src/index.ts"],
"@/components/store/*": ["./juststore-shadcn/src/components/store/*"],
"@/public/*": ["./public/*"]
}
},
- "include": [
- "next-env.d.ts",
- "**/*.ts",
- "**/*.tsx",
- "**/*.d.ts",
- ".next/types/**/*.ts",
- ".next/dev/types/**/*.ts"
- ],
+ "include": ["**/*.ts", "**/*.tsx"],
"exclude": ["node_modules", "wiki"]
}
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 00000000..8432f37c
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,88 @@
+///
+
+import tailwindcss from '@tailwindcss/vite'
+import { devtools } from '@tanstack/devtools-vite'
+import { tanstackStart } from '@tanstack/react-start/plugin/vite'
+import viteReact from '@vitejs/plugin-react'
+import { nitro } from 'nitro/vite'
+import { defineConfig } from 'vite'
+import { VitePWA } from 'vite-plugin-pwa'
+import tsconfigPaths from 'vite-tsconfig-paths'
+import { siteConfig } from '@/site-config'
+
+const isDemoSite = process.env.DEMO_SITE === 'true'
+
+export default defineConfig({
+ server: {
+ port: 3000,
+ allowedHosts: true,
+ },
+ optimizeDeps: {
+ include: ['juststore'],
+ },
+ plugins: [
+ devtools(),
+ tailwindcss(),
+ tsconfigPaths(),
+ nitro({
+ preset: isDemoSite ? 'cloudflare_pages' : 'bun',
+ minify: true,
+ sourcemap: false,
+ cloudflare: isDemoSite
+ ? {
+ deployConfig: true,
+ nodeCompat: true,
+ }
+ : undefined,
+ }),
+ tanstackStart(),
+ viteReact({
+ babel: {
+ plugins: [
+ [
+ 'babel-plugin-react-compiler',
+ {
+ compilationMode: 'annotation',
+ },
+ ],
+ ],
+ },
+ }),
+ pwaConfig('.output/public'),
+ pwaConfig('.output/server'),
+ ],
+})
+
+function pwaConfig(outdir: string) {
+ return VitePWA({
+ registerType: 'autoUpdate',
+ includeAssets: ['favicon.ico', 'apple-icon.png'],
+ outDir: outdir,
+ workbox: {
+ navigateFallback: null,
+ },
+ manifest: {
+ name: siteConfig.metadata.title,
+ short_name: siteConfig.metadata.title,
+ description: siteConfig.metadata.description,
+ start_url: '/',
+ icons: [
+ {
+ src: '/web-app-manifest-192x192.png',
+ sizes: '192x192',
+ type: 'image/png',
+ purpose: 'maskable',
+ },
+ {
+ src: '/web-app-manifest-512x512.png',
+ sizes: '512x512',
+ type: 'image/png',
+ purpose: 'maskable',
+ },
+ ],
+ theme_color: '#fffbfb',
+ background_color: '#000000',
+ display: 'standalone',
+ },
+ })
+}