From f78cab7dd8577799a5ed2b5162cd511ba87c16f7 Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Tue, 24 Feb 2026 14:36:29 +0000 Subject: [PATCH] feat(opsserver): Serve bundled frontend from a dedicated dist_serve directory and update frontend UI/packaging --- .gitignore | 2 +- changelog.md | 10 ++++ index.html => html/index.html | 0 npmextra.json | 8 +-- package.json | 1 + test/{test.basic.ts => test.basic_test.ts} | 0 ts/00_commitinfo_data.ts | 2 +- ts/opsserver/classes.opsserver.ts | 18 +++---- ts_web/00_commitinfo_data.ts | 2 +- ts_web/elements/gitops-dashboard.ts | 17 ++++--- ts_web/elements/shared/css.ts | 8 +-- ts_web/elements/views/overview/index.ts | 59 ++++------------------ 12 files changed, 48 insertions(+), 79 deletions(-) rename index.html => html/index.html (100%) rename test/{test.basic.ts => test.basic_test.ts} (100%) diff --git a/.gitignore b/.gitignore index 2901136..eadd5f2 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ deno.lock node_modules/ # Build outputs -ts_bundled/ +dist_serve/ # Development .nogit/ diff --git a/changelog.md b/changelog.md index 135cc16..22e8aa7 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,15 @@ # Changelog +## 2026-02-24 - 2.2.0 - feat(opsserver) +Serve bundled frontend from a dedicated dist_serve directory and update frontend UI/packaging + +- Serve static site using UtilityWebsiteServer with serveDir set to ./dist_serve and pass port into server.start() +- Update bundler config: output bundle to ./dist_serve/bundle.js, change outputMode to 'bundle', and include html/index.html +- Move root index.html into html/index.html and update .gitignore to ignore dist_serve/ (replace ts_bundled) +- Frontend enhancements: add iconName to view tabs and resolvedViewTabs, add Lucide icons for each tab, replace manual stats markup with dees-statsgrid using IStatsTile tiles +- Adjust shared CSS: center content, set max-width 1280px and adjust padding +- Add npm test script and rename/update tests (test.basic.ts -> test.basic_test.ts) + ## 2026-02-24 - 2.1.0 - feat(opsserver) switch to TypedServer and serve bundled UI assets; add index.html; update bundling output and dev watch configuration diff --git a/index.html b/html/index.html similarity index 100% rename from index.html rename to html/index.html diff --git a/npmextra.json b/npmextra.json index 384ce54..8cfdf0a 100644 --- a/npmextra.json +++ b/npmextra.json @@ -3,11 +3,11 @@ "bundles": [ { "from": "./ts_web/index.ts", - "to": "./ts_bundled/bundle.ts", - "outputMode": "base64ts", + "to": "./dist_serve/bundle.js", + "outputMode": "bundle", "bundler": "esbuild", "production": true, - "includeFiles": ["./index.html"] + "includeFiles": ["./html/index.html"] } ] }, @@ -15,7 +15,7 @@ "bundles": [ { "from": "./ts_web/index.ts", - "to": "./ts_bundled/bundle.ts", + "to": "./dist_serve/bundle.js", "watchPatterns": ["./ts_web/**/*"], "triggerReload": true } diff --git a/package.json b/package.json index 130d10e..5d78530 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "main": "mod.ts", "type": "module", "scripts": { + "test": "deno task test", "build": "tsbundle", "startTs": "deno run --allow-all mod.ts server", "watch": "tswatch" diff --git a/test/test.basic.ts b/test/test.basic_test.ts similarity index 100% rename from test/test.basic.ts rename to test/test.basic_test.ts diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 9d90490..18ed3c8 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@serve.zone/gitops', - version: '2.1.0', + version: '2.2.0', description: 'GitOps management app for Gitea and GitLab - manage secrets, browse projects, view CI pipelines, and stream build logs' } diff --git a/ts/opsserver/classes.opsserver.ts b/ts/opsserver/classes.opsserver.ts index 73b6c3a..f702df3 100644 --- a/ts/opsserver/classes.opsserver.ts +++ b/ts/opsserver/classes.opsserver.ts @@ -2,12 +2,11 @@ import * as plugins from '../plugins.ts'; import { logger } from '../logging.ts'; import type { GitopsApp } from '../classes/gitopsapp.ts'; import * as handlers from './handlers/index.ts'; -import { files as bundledFiles } from '../../ts_bundled/bundle.ts'; export class OpsServer { public gitopsAppRef: GitopsApp; public typedrouter = new plugins.typedrequest.TypedRouter(); - public server!: plugins.typedserver.TypedServer; + public server!: plugins.typedserver.utilityservers.UtilityWebsiteServer; // Handler instances public adminHandler!: handlers.AdminHandler; @@ -23,14 +22,11 @@ export class OpsServer { } public async start(port = 3000) { - this.server = new plugins.typedserver.TypedServer({ - port, - cors: true, - bundledContent: bundledFiles, - spaFallback: true, - injectReload: true, - watch: true, - compression: true, + const absoluteServeDir = plugins.path.resolve('./dist_serve'); + this.server = new plugins.typedserver.utilityservers.UtilityWebsiteServer({ + domain: 'localhost', + feedMetadata: undefined, + serveDir: absoluteServeDir, }); // Chain typedrouters @@ -39,7 +35,7 @@ export class OpsServer { // Set up all handlers await this.setupHandlers(); - await this.server.start(); + await this.server.start(port); logger.success(`OpsServer started on http://localhost:${port}`); } diff --git a/ts_web/00_commitinfo_data.ts b/ts_web/00_commitinfo_data.ts index 9d90490..18ed3c8 100644 --- a/ts_web/00_commitinfo_data.ts +++ b/ts_web/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@serve.zone/gitops', - version: '2.1.0', + version: '2.2.0', description: 'GitOps management app for Gitea and GitLab - manage secrets, browse projects, view CI pipelines, and stream build logs' } diff --git a/ts_web/elements/gitops-dashboard.ts b/ts_web/elements/gitops-dashboard.ts index 76c35dc..9c55df7 100644 --- a/ts_web/elements/gitops-dashboard.ts +++ b/ts_web/elements/gitops-dashboard.ts @@ -32,16 +32,16 @@ export class GitopsDashboard extends DeesElement { }; private viewTabs = [ - { name: 'Overview', element: (async () => (await import('./views/overview/index.js')).GitopsViewOverview)() }, - { name: 'Connections', element: (async () => (await import('./views/connections/index.js')).GitopsViewConnections)() }, - { name: 'Projects', element: (async () => (await import('./views/projects/index.js')).GitopsViewProjects)() }, - { name: 'Groups', element: (async () => (await import('./views/groups/index.js')).GitopsViewGroups)() }, - { name: 'Secrets', element: (async () => (await import('./views/secrets/index.js')).GitopsViewSecrets)() }, - { name: 'Pipelines', element: (async () => (await import('./views/pipelines/index.js')).GitopsViewPipelines)() }, - { name: 'Build Log', element: (async () => (await import('./views/buildlog/index.js')).GitopsViewBuildlog)() }, + { name: 'Overview', iconName: 'lucide:layoutDashboard', element: (async () => (await import('./views/overview/index.js')).GitopsViewOverview)() }, + { name: 'Connections', iconName: 'lucide:plug', element: (async () => (await import('./views/connections/index.js')).GitopsViewConnections)() }, + { name: 'Projects', iconName: 'lucide:folderGit2', element: (async () => (await import('./views/projects/index.js')).GitopsViewProjects)() }, + { name: 'Groups', iconName: 'lucide:users', element: (async () => (await import('./views/groups/index.js')).GitopsViewGroups)() }, + { name: 'Secrets', iconName: 'lucide:key', element: (async () => (await import('./views/secrets/index.js')).GitopsViewSecrets)() }, + { name: 'Pipelines', iconName: 'lucide:play', element: (async () => (await import('./views/pipelines/index.js')).GitopsViewPipelines)() }, + { name: 'Build Log', iconName: 'lucide:scrollText', element: (async () => (await import('./views/buildlog/index.js')).GitopsViewBuildlog)() }, ]; - private resolvedViewTabs: Array<{ name: string; element: any }> = []; + private resolvedViewTabs: Array<{ name: string; iconName: string; element: any }> = []; constructor() { super(); @@ -100,6 +100,7 @@ export class GitopsDashboard extends DeesElement { this.resolvedViewTabs = await Promise.all( this.viewTabs.map(async (tab) => ({ name: tab.name, + iconName: tab.iconName, element: await tab.element, })), ); diff --git a/ts_web/elements/shared/css.ts b/ts_web/elements/shared/css.ts index 71bd27d..b9c8400 100644 --- a/ts_web/elements/shared/css.ts +++ b/ts_web/elements/shared/css.ts @@ -3,11 +3,11 @@ import { css } from '@design.estate/dees-element'; export const viewHostCss = css` :host { display: block; - width: 100%; - height: 100%; - padding: 24px; - box-sizing: border-box; + margin: auto; + max-width: 1280px; + padding: 16px 16px; color: #fff; + box-sizing: border-box; } .view-title { font-size: 24px; diff --git a/ts_web/elements/views/overview/index.ts b/ts_web/elements/views/overview/index.ts index da7a4e2..b6290dd 100644 --- a/ts_web/elements/views/overview/index.ts +++ b/ts_web/elements/views/overview/index.ts @@ -10,6 +10,7 @@ import { cssManager, type TemplateResult, } from '@design.estate/dees-element'; +import { type IStatsTile } from '@design.estate/dees-catalog'; @customElement('gitops-view-overview') export class GitopsViewOverview extends DeesElement { @@ -45,33 +46,6 @@ export class GitopsViewOverview extends DeesElement { public static styles = [ cssManager.defaultStyles, viewHostCss, - css` - .stats-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); - gap: 16px; - margin-bottom: 24px; - } - .stat-card { - background: #1a1a2e; - border: 1px solid #333; - border-radius: 8px; - padding: 20px; - text-align: center; - } - .stat-value { - font-size: 36px; - font-weight: 700; - color: #00acff; - margin-bottom: 8px; - } - .stat-label { - font-size: 14px; - color: #999; - text-transform: uppercase; - letter-spacing: 1px; - } - `, ]; public render(): TemplateResult { @@ -81,31 +55,18 @@ export class GitopsViewOverview extends DeesElement { const pipelineCount = this.dataState.pipelines.length; const failedPipelines = this.dataState.pipelines.filter((p) => p.status === 'failed').length; + const tiles: IStatsTile[] = [ + { id: 'connections', title: 'Connections', value: connCount, type: 'number', icon: 'lucide:plug', color: '#00acff' }, + { id: 'projects', title: 'Projects', value: projCount, type: 'number', icon: 'lucide:folderGit2', color: '#00acff' }, + { id: 'groups', title: 'Groups', value: groupCount, type: 'number', icon: 'lucide:users', color: '#00acff' }, + { id: 'pipelines', title: 'Pipelines', value: pipelineCount, type: 'number', icon: 'lucide:play', color: '#00acff' }, + { id: 'failed', title: 'Failed Pipelines', value: failedPipelines, type: 'number', icon: 'lucide:triangleAlert', color: failedPipelines > 0 ? '#ff4444' : '#00ff88' }, + ]; + return html`
Overview
GitOps dashboard - manage your Gitea and GitLab instances
-
-
-
${connCount}
-
Connections
-
-
-
${projCount}
-
Projects
-
-
-
${groupCount}
-
Groups
-
-
-
${pipelineCount}
-
Pipelines
-
-
-
${failedPipelines}
-
Failed Pipelines
-
-
+ `; } }