Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
97 changes: 97 additions & 0 deletions eshtek/admin.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/**
* Admin API Types and Interfaces
*/

/**
* Response from the catalog status endpoint
*/
export interface CatalogStatusResponse {
totalApps: number;
totalNonDeprecated: number;
totalWithScripts: number;
totalWithScriptsNonDeprecated: number;
totalDeprecated: number;
lastSync: Date | null;
}

/**
* Individual app in the catalog
*/
export interface CatalogApp {
id: number;
name: string;
train: string | null;
version: string | null;
appVersion: string | null;
title: string | null;
description: string | null;
home: string | null;
iconUrl: string | null;
screenshots: string[];
sources: string[];
categories: string[];
keywords: string[];
maintainers: any[];
supported: boolean;
deprecated: boolean;
installScript: string | null;
requirements: any;
compatibility: string | null;
lastCatalogSync: Date | null;
metadata: any;
// Event statistics (detailed)
installsCompleted?: number;
installsFailed?: number;
uninstallsCompleted?: number;
uninstallsFailed?: number;
upgradesCompleted?: number;
upgradesFailed?: number;
updatesCompleted?: number;
updatesFailed?: number;
}

/**
* Response from the catalog data endpoint
*/
export interface AppsCatalogResponse {
apps: CatalogApp[];
totalApps: number;
totalWithScripts: number;
totalDeprecated: number;
lastSync: Date | null;
}

/**
* Drive data with event statistics
*/
export interface DriveData {
id: number;
manufacturer: string;
model: string;
name: string | null;
type: string | null;
size: number | null;
smr: boolean;
notes: string | null;
isUserDiscovered: boolean;
createdAt: Date;
updatedAt: Date;
// Event statistics
utilized: number;
replaced: number;
removed: number;
failed: number;
discovered: number;
}

/**
* Response from the drives data endpoint
*/
export interface DrivesResponse {
drives: DriveData[];
totalDrives: number;
totalSMR: number;
totalUtilized: number;
totalFailed: number;
totalRemoved: number;
}
26 changes: 24 additions & 2 deletions eshtek/apps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,28 @@ export interface AppRequirementsCheck {
};
}

export interface AppListing extends AvailableApp {
hexos: boolean;
export interface AppMaintainer {
name: string;
email: string;
}

export interface AppListing {
appId: string;
name: string;
train: "stable" | "community";
version: string;
appVersion: string;
description: string;
icon: string;
categories: string[];
keywords: string[];
maintainers: AppMaintainer[];
screenshots: string[];
sources: string[];
homepage: string;
recommended: boolean;
supported: boolean;
fresh: boolean;
installScript?: string;
requirements?: AppRequirements;
}
Expand Down Expand Up @@ -120,6 +140,7 @@ export interface InstallationQuestion {

interface AppsInstallScriptV1 {
version: 1;
requirements?: AppRequirements;
ensure_directories_exists?: Array<string | { path: string; network_share?: boolean; posix?: boolean }>;
ensure_permissions_exists?: Array<{
path: string;
Expand All @@ -132,6 +153,7 @@ interface AppsInstallScriptV1 {

interface AppsInstallScriptV2 {
version: 2;
requirements?: AppRequirements;
installation_questions?: InstallationQuestion[];
ensure_directories_exists?: Array<string | { path: string; network_share?: boolean; posix?: boolean }>;
ensure_permissions_exists?: Array<{
Expand Down
13 changes: 13 additions & 0 deletions eshtek/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -505,3 +505,16 @@ export function getStepSize(range: number, steps: number = 10, acceptableSteps:
return Math.abs(curr - rawStep) < Math.abs(prev - rawStep) ? curr : prev;
});
}

import type { HexTaskType } from './tasks';
import type { EventState, TaskEventName } from './events';

/**
* Generates a task event name from a task type and event state.
* @param {HexTaskType} taskType - The type of the task.
* @param {EventState} state - The state of the event (started, completed, failed).
* @returns {TaskEventName} - Returns the generated event name (e.g., 'app_install_started').
*/
export function getTaskEventName(taskType: HexTaskType, state: EventState): TaskEventName {
return `${taskType.toLowerCase()}_${state}` as TaskEventName;
}
71 changes: 71 additions & 0 deletions eshtek/events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import type { HexTaskType } from './tasks';

export interface BaseEvent {
eventName: string;
userId: string;
hostId?: string;
timestamp: Date;
properties?: Record<string, any>;
}

export interface TaskEvent extends BaseEvent {
taskId: string;
taskType: HexTaskType;
taskStatus: string;
errorMessage?: string;
}

export interface AppEvent extends TaskEvent {
appId: string;
appTrain?: string;
appVersion?: string;
appTrueNasVersion?: string;
}

export enum EventState {
STARTED = 'started',
COMPLETED = 'completed',
FAILED = 'failed',
DISMISSED = 'dismissed',
}

export type TaskEventName = `${Lowercase<`${HexTaskType}`>}_${EventState}`;

export const SystemEventNames = {
SERVER_CONNECTED: 'server_connected',
SERVER_DISCONNECTED: 'server_disconnected',
USER_LOGIN: 'user_login',
USER_LOGOUT: 'user_logout',
DRIVE_UTILIZED: 'drive_utilized', // Drive added to a pool
DRIVE_REPLACED: 'drive_replaced',
DRIVE_REMOVED: 'drive_removed',
DRIVE_FAILED: 'drive_failed',
DRIVE_HEALTHY: 'drive_healthy', // Drive has no errors
DRIVE_DISCOVERED: 'drive_discovered', // Drive found on system (assigned or unassigned)
APP_DISCOVERED: 'app_discovered', // App found on system
} as const;

export type SystemEventName = typeof SystemEventNames[keyof typeof SystemEventNames];
export type EventName = TaskEventName | SystemEventName;

export interface EventQueryOptions {
eventName?: EventName | EventName[];
userId?: string;
hostId?: string;
taskType?: HexTaskType;
appId?: string;
appTrain?: string;
appVersion?: string;
startDate?: Date;
endDate?: Date;
limit?: number;
}

export interface AppPopularityMetrics {
appId: string;
installCount: number;
uninstallCount: number;
upgradeCount: number;
failureCount: number;
lastInstalled: Date;
}
44 changes: 43 additions & 1 deletion eshtek/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,46 @@ export interface RequestAppInstall {

export interface RequestAppDelete {
deleteData?: boolean;
}
}

export enum AppSearchSortBy {
NAME = 'name',
POPULARITY = 'popularity',
CREATED_AT = 'createdAt',
UPDATED_AT = 'updatedAt',
}

export enum AppSearchSortOrder {
ASC = 'asc',
DESC = 'desc',
}

export interface RequestAppSearch {
appId?: string;
search?: string;
category?: string;
fresh?: boolean;
supported?: boolean;
recommended?: boolean;
train?: 'stable' | 'community';
sortBy?: AppSearchSortBy;
sortOrder?: AppSearchSortOrder;
popularityStartDate?: string;
popularityEndDate?: string;
page?: number;
pageSize?: number;
limit?: number; // -1 means return all results
}

export interface AppCategoryInfo {
name: string;
appCount: number;
}

export interface RequestAppCategories {
train?: 'stable' | 'community';
supported?: boolean;
fresh?: boolean;
}

export type ResponseAppCategories = Response<AppCategoryInfo[]>;
1 change: 1 addition & 0 deletions eshtek/server-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ export const serverDriveSchema = z.object({
existingData: z.boolean().optional(),
temperature: z.number().optional(),
healthDetails: topologyItemStatusSchema.optional(),
driveId: z.number().optional(),
});

export const serverPoolNewSchema = serverPoolBasicsSchema.extend({
Expand Down
1 change: 1 addition & 0 deletions eshtek/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ export interface ServerDrive {
existingData?: boolean;
temperature?: number;
healthDetails?: TopologyItemStatus;
driveId?: number; // Database ID from drives table
}

export interface ServerDrivesGroupedBySize {
Expand Down
2 changes: 1 addition & 1 deletion eshtek/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ export type HexTaskDataMap = {
export const HexTaskSettings: {
[K in HexTaskType]: HexTaskTypeInfo;
} = {
[HexTaskType.RESTART]: { canHaveMultiple: false, predictedSecondsToComplete: 120 },
[HexTaskType.RESTART]: { canHaveMultiple: false, predictedSecondsToComplete: 500 },
[HexTaskType.SHUTDOWN]: { canHaveMultiple: false, predictedSecondsToComplete: 120 },
[HexTaskType.NETWORK_UPDATE]: { canHaveMultiple: false, predictedSecondsToComplete: 90 },
[HexTaskType.POOL_CREATE]: { canHaveMultiple: true, predictedSecondsToComplete: 30 },
Expand Down