Compare commits

...

8 Commits

Author SHA1 Message Date
c420a30341 v1.18.2
Some checks failed
Publish to npm / npm-publish (push) Failing after 9s
CI / Type Check & Lint (push) Failing after 28s
CI / Build Test (Current Platform) (push) Successful in 59s
CI / Build All Platforms (push) Successful in 2m0s
Release / build-and-release (push) Successful in 3m41s
2026-03-16 14:14:55 +00:00
fe109f0953 fix(repo): no changes to commit 2026-03-16 14:14:55 +00:00
012dce63b1 v1.18.1
Some checks failed
Publish to npm / npm-publish (push) Failing after 10s
Release / build-and-release (push) Successful in 4m0s
2026-03-16 14:14:34 +00:00
54780482c7 fix(repo): no changes to commit 2026-03-16 14:14:34 +00:00
7ab0fb3c1f v1.18.0
Some checks failed
Publish to npm / npm-publish (push) Failing after 9s
CI / Type Check & Lint (push) Failing after 27s
CI / Build Test (Current Platform) (push) Successful in 58s
CI / Build All Platforms (push) Successful in 1m52s
Release / build-and-release (push) Successful in 2m58s
2026-03-16 13:51:43 +00:00
713fda2a86 feat(platform-services): add platform service log retrieval and display in the services UI 2026-03-16 13:51:43 +00:00
ec32c19300 v1.17.4
Some checks failed
CI / Type Check & Lint (push) Failing after 30s
Publish to npm / npm-publish (push) Failing after 24s
CI / Build Test (Current Platform) (push) Successful in 1m1s
CI / Build All Platforms (push) Successful in 2m12s
Release / build-and-release (push) Successful in 4m0s
2026-03-16 13:26:56 +00:00
7d1d91157c fix(docs): add hello world running screenshot for documentation 2026-03-16 13:26:56 +00:00
14 changed files with 120 additions and 17 deletions

View File

@@ -1,5 +1,26 @@
# Changelog # Changelog
## 2026-03-16 - 1.18.2 - fix(repo)
no changes to commit
## 2026-03-16 - 1.18.1 - fix(repo)
no changes to commit
## 2026-03-16 - 1.18.0 - feat(platform-services)
add platform service log retrieval and display in the services UI
- add typed request support in the ops server to fetch Docker logs for platform service containers
- store fetched platform service logs in web app state and load them when opening platform service details
- render platform service logs in the services detail view and add sidebar icons for main navigation tabs
## 2026-03-16 - 1.17.4 - fix(docs)
add hello world running screenshot for documentation
- Adds a new PNG asset showing the application in a running hello world state.
- Supports project documentation or README usage without changing runtime behavior.
## 2026-03-16 - 1.17.3 - fix(mongodb) ## 2026-03-16 - 1.17.3 - fix(mongodb)
downgrade the MongoDB service image to 4.4 and use the legacy mongo shell for container operations downgrade the MongoDB service image to 4.4 and use the legacy mongo shell for container operations

View File

@@ -1,6 +1,6 @@
{ {
"name": "@serve.zone/onebox", "name": "@serve.zone/onebox",
"version": "1.17.3", "version": "1.18.2",
"exports": "./mod.ts", "exports": "./mod.ts",
"tasks": { "tasks": {
"test": "deno test --allow-all test/", "test": "deno test --allow-all test/",

BIN
hello-world-running.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@@ -1,6 +1,6 @@
{ {
"name": "@serve.zone/onebox", "name": "@serve.zone/onebox",
"version": "1.17.3", "version": "1.18.2",
"description": "Self-hosted container platform with automatic SSL and DNS - a mini Heroku for single servers", "description": "Self-hosted container platform with automatic SSL and DNS - a mini Heroku for single servers",
"main": "mod.ts", "main": "mod.ts",
"type": "module", "type": "module",

BIN
sidebar-icons.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@serve.zone/onebox', name: '@serve.zone/onebox',
version: '1.17.3', version: '1.18.2',
description: 'Self-hosted container platform with automatic SSL and DNS - a mini Heroku for single servers' description: 'Self-hosted container platform with automatic SSL and DNS - a mini Heroku for single servers'
} }

View File

@@ -165,5 +165,42 @@ export class PlatformHandler {
}, },
), ),
); );
// Get platform service logs
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetPlatformServiceLogs>(
'getPlatformServiceLogs',
async (dataArg) => {
await requireValidIdentity(this.opsServerRef.adminHandler, dataArg);
const service = this.opsServerRef.oneboxRef.database.getPlatformServiceByType(dataArg.serviceType);
if (!service || !service.containerId) {
throw new plugins.typedrequest.TypedResponseError('Platform service has no container');
}
const tail = dataArg.tail || 100;
const rawLogs = await this.opsServerRef.oneboxRef.docker.getContainerLogs(service.containerId, tail);
// Parse raw log output into structured entries
const logLines = (rawLogs.stdout + rawLogs.stderr)
.split('\n')
.filter((line: string) => line.trim());
const logs = logLines.map((line: string, index: number) => {
const isError = line.toLowerCase().includes('error') || line.toLowerCase().includes('fatal');
const isWarn = line.toLowerCase().includes('warn');
return {
id: index,
serviceId: 0,
timestamp: Date.now(),
message: line,
level: (isError ? 'error' : isWarn ? 'warn' : 'info') as 'info' | 'warn' | 'error' | 'debug',
source: 'stdout' as const,
};
});
return { logs };
},
),
);
} }
} }

File diff suppressed because one or more lines are too long

View File

@@ -69,3 +69,18 @@ export interface IReq_GetPlatformServiceStats extends plugins.typedrequestInterf
stats: data.IContainerStats; stats: data.IContainerStats;
}; };
} }
export interface IReq_GetPlatformServiceLogs extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IReq_GetPlatformServiceLogs
> {
method: 'getPlatformServiceLogs';
request: {
identity: data.IIdentity;
serviceType: data.TPlatformServiceType;
tail?: number;
};
response: {
logs: data.ILogEntry[];
};
}

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@serve.zone/onebox', name: '@serve.zone/onebox',
version: '1.17.3', version: '1.18.2',
description: 'Self-hosted container platform with automatic SSL and DNS - a mini Heroku for single servers' description: 'Self-hosted container platform with automatic SSL and DNS - a mini Heroku for single servers'
} }

View File

@@ -27,6 +27,7 @@ export interface IServicesState {
platformServices: interfaces.data.IPlatformService[]; platformServices: interfaces.data.IPlatformService[];
currentPlatformService: interfaces.data.IPlatformService | null; currentPlatformService: interfaces.data.IPlatformService | null;
currentPlatformServiceStats: interfaces.data.IContainerStats | null; currentPlatformServiceStats: interfaces.data.IContainerStats | null;
currentPlatformServiceLogs: interfaces.data.ILogEntry[];
} }
export interface INetworkState { export interface INetworkState {
@@ -90,6 +91,7 @@ export const servicesStatePart = await appState.getStatePart<IServicesState>(
platformServices: [], platformServices: [],
currentPlatformService: null, currentPlatformService: null,
currentPlatformServiceStats: null, currentPlatformServiceStats: null,
currentPlatformServiceLogs: [],
}, },
'soft', 'soft',
); );
@@ -497,6 +499,27 @@ export const fetchPlatformServiceStatsAction = servicesStatePart.createAction<{
} }
}); });
export const fetchPlatformServiceLogsAction = servicesStatePart.createAction<{
serviceType: interfaces.data.TPlatformServiceType;
tail?: number;
}>(async (statePartArg, dataArg) => {
const context = getActionContext();
try {
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_GetPlatformServiceLogs
>('/typedrequest', 'getPlatformServiceLogs');
const response = await typedRequest.fire({
identity: context.identity!,
serviceType: dataArg.serviceType,
tail: dataArg.tail || 100,
});
return { ...statePartArg.getState(), currentPlatformServiceLogs: response.logs };
} catch (err) {
console.error('Failed to fetch platform service logs:', err);
return { ...statePartArg.getState(), currentPlatformServiceLogs: [] };
}
});
// ============================================================================ // ============================================================================
// Network Actions // Network Actions
// ============================================================================ // ============================================================================

View File

@@ -37,15 +37,15 @@ export class ObAppShell extends DeesElement {
accessor loginError: string = ''; accessor loginError: string = '';
private viewTabs = [ private viewTabs = [
{ name: 'Dashboard', element: (async () => (await import('./ob-view-dashboard.js')).ObViewDashboard)() }, { name: 'Dashboard', iconName: 'lucide:layoutDashboard', element: (async () => (await import('./ob-view-dashboard.js')).ObViewDashboard)() },
{ name: 'Services', element: (async () => (await import('./ob-view-services.js')).ObViewServices)() }, { name: 'Services', iconName: 'lucide:boxes', element: (async () => (await import('./ob-view-services.js')).ObViewServices)() },
{ name: 'Network', element: (async () => (await import('./ob-view-network.js')).ObViewNetwork)() }, { name: 'Network', iconName: 'lucide:network', element: (async () => (await import('./ob-view-network.js')).ObViewNetwork)() },
{ name: 'Registries', element: (async () => (await import('./ob-view-registries.js')).ObViewRegistries)() }, { name: 'Registries', iconName: 'lucide:package', element: (async () => (await import('./ob-view-registries.js')).ObViewRegistries)() },
{ name: 'Tokens', element: (async () => (await import('./ob-view-tokens.js')).ObViewTokens)() }, { name: 'Tokens', iconName: 'lucide:key', element: (async () => (await import('./ob-view-tokens.js')).ObViewTokens)() },
{ name: 'Settings', element: (async () => (await import('./ob-view-settings.js')).ObViewSettings)() }, { name: 'Settings', iconName: 'lucide:settings', element: (async () => (await import('./ob-view-settings.js')).ObViewSettings)() },
]; ];
private resolvedViewTabs: Array<{ name: string; element: any }> = []; private resolvedViewTabs: Array<{ name: string; iconName?: string; element: any }> = [];
constructor() { constructor() {
super(); super();
@@ -104,6 +104,7 @@ export class ObAppShell extends DeesElement {
this.resolvedViewTabs = await Promise.all( this.resolvedViewTabs = await Promise.all(
this.viewTabs.map(async (tab) => ({ this.viewTabs.map(async (tab) => ({
name: tab.name, name: tab.name,
iconName: tab.iconName,
element: await tab.element, element: await tab.element,
})), })),
); );

View File

@@ -25,6 +25,7 @@ export class ObViewDashboard extends DeesElement {
platformServices: [], platformServices: [],
currentPlatformService: null, currentPlatformService: null,
currentPlatformServiceStats: null, currentPlatformServiceStats: null,
currentPlatformServiceLogs: [],
}; };
@state() @state()

View File

@@ -108,6 +108,7 @@ export class ObViewServices extends DeesElement {
platformServices: [], platformServices: [],
currentPlatformService: null, currentPlatformService: null,
currentPlatformServiceStats: null, currentPlatformServiceStats: null,
currentPlatformServiceLogs: [],
}; };
@state() @state()
@@ -357,10 +358,10 @@ export class ObViewServices extends DeesElement {
private navigateToPlatformDetail(type: string): void { private navigateToPlatformDetail(type: string): void {
this.selectedPlatformType = type; this.selectedPlatformType = type;
// Fetch stats for this platform service // Fetch stats and logs for this platform service
appstate.servicesStatePart.dispatchAction(appstate.fetchPlatformServiceStatsAction, { const serviceType = type as interfaces.data.TPlatformServiceType;
serviceType: type as interfaces.data.TPlatformServiceType, appstate.servicesStatePart.dispatchAction(appstate.fetchPlatformServiceStatsAction, { serviceType });
}); appstate.servicesStatePart.dispatchAction(appstate.fetchPlatformServiceLogsAction, { serviceType });
this.currentView = 'platform-detail'; this.currentView = 'platform-detail';
} }
@@ -398,7 +399,11 @@ export class ObViewServices extends DeesElement {
metrics, metrics,
} }
: null} : null}
.logs=${[]} .logs=${this.servicesState.currentPlatformServiceLogs.map((log) => ({
timestamp: new Date(log.timestamp).toLocaleString(),
level: log.level,
message: log.message,
}))}
@back=${() => { @back=${() => {
this.currentView = 'list'; this.currentView = 'list';
}} }}