Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a3eefbe92c | |||
| 41679427c6 | |||
| c420a30341 | |||
| fe109f0953 | |||
| 012dce63b1 | |||
| 54780482c7 | |||
| 7ab0fb3c1f | |||
| 713fda2a86 | |||
| ec32c19300 | |||
| 7d1d91157c |
26
changelog.md
26
changelog.md
@@ -1,5 +1,31 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2026-03-16 - 1.18.3 - fix(deps)
|
||||||
|
bump @serve.zone/catalog to ^2.6.1
|
||||||
|
|
||||||
|
- Updates the @serve.zone/catalog runtime dependency from ^2.6.0 to ^2.6.1.
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@serve.zone/onebox",
|
"name": "@serve.zone/onebox",
|
||||||
"version": "1.17.3",
|
"version": "1.18.3",
|
||||||
"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
BIN
hello-world-running.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@serve.zone/onebox",
|
"name": "@serve.zone/onebox",
|
||||||
"version": "1.17.3",
|
"version": "1.18.3",
|
||||||
"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",
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
"@api.global/typedrequest-interfaces": "^3.0.19",
|
"@api.global/typedrequest-interfaces": "^3.0.19",
|
||||||
"@design.estate/dees-catalog": "^3.43.3",
|
"@design.estate/dees-catalog": "^3.43.3",
|
||||||
"@design.estate/dees-element": "^2.1.6",
|
"@design.estate/dees-element": "^2.1.6",
|
||||||
"@serve.zone/catalog": "^2.6.0"
|
"@serve.zone/catalog": "^2.6.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@git.zone/tsbundle": "^2.9.0",
|
"@git.zone/tsbundle": "^2.9.0",
|
||||||
|
|||||||
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
@@ -18,8 +18,8 @@ importers:
|
|||||||
specifier: ^2.1.6
|
specifier: ^2.1.6
|
||||||
version: 2.2.3
|
version: 2.2.3
|
||||||
'@serve.zone/catalog':
|
'@serve.zone/catalog':
|
||||||
specifier: ^2.6.0
|
specifier: ^2.6.1
|
||||||
version: 2.6.0(@tiptap/pm@2.27.2)
|
version: 2.6.1(@tiptap/pm@2.27.2)
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@git.zone/tsbundle':
|
'@git.zone/tsbundle':
|
||||||
specifier: ^2.9.0
|
specifier: ^2.9.0
|
||||||
@@ -836,8 +836,8 @@ packages:
|
|||||||
'@sec-ant/readable-stream@0.4.1':
|
'@sec-ant/readable-stream@0.4.1':
|
||||||
resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
|
resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
|
||||||
|
|
||||||
'@serve.zone/catalog@2.6.0':
|
'@serve.zone/catalog@2.6.1':
|
||||||
resolution: {integrity: sha512-Gp91Ed0MMLMPSAZrH2/UimGxU9AaTu5IPUobPD49PSvh3UnUl+HEFiy81kpKJhW16V37N3/vcNgZrn2VI7/LxQ==}
|
resolution: {integrity: sha512-8L78lyTvZPin3ELkaPOvUmeKuzqMNZunnyvF9LR2q4Eakxgc/KgbV0Q1M0fF0ENpGxQH+2kOWobwQsH2RfDmVA==}
|
||||||
|
|
||||||
'@tempfix/idb@8.0.3':
|
'@tempfix/idb@8.0.3':
|
||||||
resolution: {integrity: sha512-hPJQKO7+oAIY+pDNImrZ9QAINbz9KmwT+yO4iRVwdPanok2YKpaUxdJzIvCUwY0YgAawlvYdffbLvRLV5hbs2g==}
|
resolution: {integrity: sha512-hPJQKO7+oAIY+pDNImrZ9QAINbz9KmwT+yO4iRVwdPanok2YKpaUxdJzIvCUwY0YgAawlvYdffbLvRLV5hbs2g==}
|
||||||
@@ -3474,7 +3474,7 @@ snapshots:
|
|||||||
|
|
||||||
'@sec-ant/readable-stream@0.4.1': {}
|
'@sec-ant/readable-stream@0.4.1': {}
|
||||||
|
|
||||||
'@serve.zone/catalog@2.6.0(@tiptap/pm@2.27.2)':
|
'@serve.zone/catalog@2.6.1(@tiptap/pm@2.27.2)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@design.estate/dees-catalog': 3.48.5(@tiptap/pm@2.27.2)
|
'@design.estate/dees-catalog': 3.48.5(@tiptap/pm@2.27.2)
|
||||||
'@design.estate/dees-domtools': 2.5.1
|
'@design.estate/dees-domtools': 2.5.1
|
||||||
|
|||||||
BIN
sidebar-icons.png
Normal file
BIN
sidebar-icons.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
@@ -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.3',
|
||||||
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'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -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.3',
|
||||||
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'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
@@ -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,
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export class ObViewDashboard extends DeesElement {
|
|||||||
platformServices: [],
|
platformServices: [],
|
||||||
currentPlatformService: null,
|
currentPlatformService: null,
|
||||||
currentPlatformServiceStats: null,
|
currentPlatformServiceStats: null,
|
||||||
|
currentPlatformServiceLogs: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
|
|||||||
@@ -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';
|
||||||
}}
|
}}
|
||||||
|
|||||||
Reference in New Issue
Block a user