diff --git a/packages/backend/src/app.controller.ts b/packages/backend/src/app.controller.ts index eefc18165c..085477a70c 100644 --- a/packages/backend/src/app.controller.ts +++ b/packages/backend/src/app.controller.ts @@ -48,8 +48,8 @@ export class AppController { const apps = await this.marketplaceService.getAvailableApps(); const installedApps = await this.appsService.getInstalledApps(); - const updatesAvailable = installedApps.filter(({ app, updateInfo }) => { - return Number(app.version) < Number(updateInfo?.latestVersion ?? 0) && app.status !== 'updating'; + const updatesAvailable = installedApps.filter(({ app, metadata }) => { + return Number(app.version) < Number(metadata?.latestVersion ?? 0) && app.status !== 'updating'; }); return { version, userSettings, user: req.user as UserDto, apps, updatesAvailable: updatesAvailable.length }; diff --git a/packages/backend/src/metadata.ts b/packages/backend/src/metadata.ts index 8adb573969..5576999e39 100644 --- a/packages/backend/src/metadata.ts +++ b/packages/backend/src/metadata.ts @@ -9,5 +9,5 @@ export default async () => { ["./modules/links/dto/links.dto"]: await import("./modules/links/dto/links.dto"), ["./modules/system/dto/system.dto"]: await import("./modules/system/dto/system.dto") }; - return { "@nestjs/swagger": { "models": [[import("./modules/user/dto/user.dto"), { "UserDto": {} }], [import("./modules/marketplace/dto/marketplace.dto"), { "AppInfoSimpleDto": {}, "AppInfoDto": {}, "UpdateInfoDto": {}, "SearchAppsQueryDto": {}, "SearchAppsDto": {}, "AppDetailsDto": {}, "PullDto": {}, "AllAppStoresDto": {}, "UpdateAppStoreBodyDto": {}, "CreateAppStoreBodyDto": {} }], [import("./app.dto"), { "UserSettingsDto": {}, "PartialUserSettingsDto": {}, "AppContextDto": {}, "UserContextDto": {}, "AcknowledgeWelcomeBody": {} }], [import("./modules/queue/queue.entity"), { "Queue": {} }], [import("./modules/auth/dto/auth.dto"), { "LoginBody": {}, "VerifyTotpBody": {}, "LoginDto": {}, "RegisterBody": {}, "RegisterDto": {}, "ChangeUsernameBody": {}, "ChangePasswordBody": {}, "GetTotpUriBody": {}, "GetTotpUriDto": {}, "SetupTotpBody": {}, "DisableTotpBody": {}, "ResetPasswordBody": {}, "ResetPasswordDto": {}, "CheckResetPasswordRequestDto": {} }], [import("./modules/app-lifecycle/dto/app-lifecycle.dto"), { "AppFormBody": {}, "UninstallAppBody": {}, "UpdateAppBody": {} }], [import("./modules/apps/dto/app.dto"), { "AppDto": {}, "MyAppsDto": {}, "GuestAppsDto": {}, "GetAppDto": {} }], [import("./modules/backups/dto/backups.dto"), { "BackupDto": {}, "RestoreAppBackupDto": {}, "GetAppBackupsDto": {}, "GetAppBackupsQueryDto": {}, "DeleteAppBackupBodyDto": {} }], [import("./modules/links/dto/links.dto"), { "LinkBodyDto": {}, "EditLinkBodyDto": {}, "LinksDto": {} }], [import("./modules/system/dto/system.dto"), { "LoadDto": {} }]], "controllers": [[import("./app.controller"), { "AppController": { "userContext": { type: t["./app.dto"].UserContextDto }, "appContext": { type: t["./app.dto"].AppContextDto }, "updateUserSettings": {}, "acknowledgeWelcome": {} } }], [import("./modules/auth/auth.controller"), { "AuthController": { "login": { type: t["./modules/auth/dto/auth.dto"].LoginDto }, "verifyTotp": { type: t["./modules/auth/dto/auth.dto"].LoginDto }, "register": { type: t["./modules/auth/dto/auth.dto"].RegisterDto }, "logout": {}, "changeUsername": {}, "changePassword": {}, "getTotpUri": { type: t["./modules/auth/dto/auth.dto"].GetTotpUriDto }, "setupTotp": {}, "disableTotp": {}, "resetPassword": { type: t["./modules/auth/dto/auth.dto"].ResetPasswordDto }, "cancelResetPassword": {}, "checkResetPasswordRequest": { type: t["./modules/auth/dto/auth.dto"].CheckResetPasswordRequestDto } } }], [import("./modules/i18n/i18n.controller"), { "I18nController": { "getTranslation": { type: Object } } }], [import("./core/health/health.controller"), { "HealthController": { "check": { type: Object } } }], [import("./modules/marketplace/marketplace.controller"), { "MarketplaceController": { "searchApps": { type: t["./modules/marketplace/dto/marketplace.dto"].SearchAppsDto }, "getImage": {}, "pullAppStore": { type: t["./modules/marketplace/dto/marketplace.dto"].PullDto }, "createAppStore": {}, "getAllAppStores": { type: t["./modules/marketplace/dto/marketplace.dto"].AllAppStoresDto }, "getEnabledAppStores": { type: t["./modules/marketplace/dto/marketplace.dto"].AllAppStoresDto }, "updateAppStore": {}, "deleteAppStore": {} } }], [import("./modules/apps/apps.controller"), { "AppsController": { "getInstalledApps": { type: t["./modules/apps/dto/app.dto"].MyAppsDto }, "getGuestApps": { type: t["./modules/apps/dto/app.dto"].GuestAppsDto }, "getApp": { type: t["./modules/apps/dto/app.dto"].GetAppDto } } }], [import("./modules/backups/backups.controller"), { "BackupsController": { "backupApp": {}, "restoreAppBackup": {}, "getAppBackups": { type: t["./modules/backups/dto/backups.dto"].GetAppBackupsDto }, "deleteAppBackup": {} } }], [import("./modules/app-lifecycle/app-lifecycle.controller"), { "AppLifecycleController": { "installApp": {}, "startApp": {}, "stopApp": {}, "restartApp": {}, "uninstallApp": {}, "resetApp": {}, "updateApp": {}, "updateAllApps": {}, "updateAppConfig": {} } }], [import("./modules/links/links.controller"), { "LinksController": { "getLinks": { type: t["./modules/links/dto/links.dto"].LinksDto }, "createLink": {}, "editLink": {}, "deleteLink": {} } }], [import("./modules/system/system.controller"), { "SystemController": { "systemLoad": { type: t["./modules/system/dto/system.dto"].LoadDto }, "downloadLocalCertificate": {} } }]] } }; + return { "@nestjs/swagger": { "models": [[import("./modules/user/dto/user.dto"), { "UserDto": {} }], [import("./modules/marketplace/dto/marketplace.dto"), { "AppInfoSimpleDto": {}, "AppInfoDto": {}, "MetadataDto": {}, "SearchAppsQueryDto": {}, "SearchAppsDto": {}, "AppDetailsDto": {}, "PullDto": {}, "AllAppStoresDto": {}, "UpdateAppStoreBodyDto": {}, "CreateAppStoreBodyDto": {} }], [import("./app.dto"), { "UserSettingsDto": {}, "PartialUserSettingsDto": {}, "AppContextDto": {}, "UserContextDto": {}, "AcknowledgeWelcomeBody": {} }], [import("./modules/queue/queue.entity"), { "Queue": {} }], [import("./modules/auth/dto/auth.dto"), { "LoginBody": {}, "VerifyTotpBody": {}, "LoginDto": {}, "RegisterBody": {}, "RegisterDto": {}, "ChangeUsernameBody": {}, "ChangePasswordBody": {}, "GetTotpUriBody": {}, "GetTotpUriDto": {}, "SetupTotpBody": {}, "DisableTotpBody": {}, "ResetPasswordBody": {}, "ResetPasswordDto": {}, "CheckResetPasswordRequestDto": {} }], [import("./modules/app-lifecycle/dto/app-lifecycle.dto"), { "AppFormBody": {}, "UninstallAppBody": {}, "UpdateAppBody": {} }], [import("./modules/apps/dto/app.dto"), { "AppDto": {}, "MyAppsDto": {}, "GuestAppsDto": {}, "GetAppDto": {} }], [import("./modules/backups/dto/backups.dto"), { "BackupDto": {}, "RestoreAppBackupDto": {}, "GetAppBackupsDto": {}, "GetAppBackupsQueryDto": {}, "DeleteAppBackupBodyDto": {} }], [import("./modules/links/dto/links.dto"), { "LinkBodyDto": {}, "EditLinkBodyDto": {}, "LinksDto": {} }], [import("./modules/system/dto/system.dto"), { "LoadDto": {} }]], "controllers": [[import("./app.controller"), { "AppController": { "userContext": { type: t["./app.dto"].UserContextDto }, "appContext": { type: t["./app.dto"].AppContextDto }, "updateUserSettings": {}, "acknowledgeWelcome": {} } }], [import("./modules/auth/auth.controller"), { "AuthController": { "login": { type: t["./modules/auth/dto/auth.dto"].LoginDto }, "verifyTotp": { type: t["./modules/auth/dto/auth.dto"].LoginDto }, "register": { type: t["./modules/auth/dto/auth.dto"].RegisterDto }, "logout": {}, "changeUsername": {}, "changePassword": {}, "getTotpUri": { type: t["./modules/auth/dto/auth.dto"].GetTotpUriDto }, "setupTotp": {}, "disableTotp": {}, "resetPassword": { type: t["./modules/auth/dto/auth.dto"].ResetPasswordDto }, "cancelResetPassword": {}, "checkResetPasswordRequest": { type: t["./modules/auth/dto/auth.dto"].CheckResetPasswordRequestDto } } }], [import("./modules/i18n/i18n.controller"), { "I18nController": { "getTranslation": { type: Object } } }], [import("./core/health/health.controller"), { "HealthController": { "check": { type: Object } } }], [import("./modules/marketplace/marketplace.controller"), { "MarketplaceController": { "searchApps": { type: t["./modules/marketplace/dto/marketplace.dto"].SearchAppsDto }, "getImage": {}, "pullAppStore": { type: t["./modules/marketplace/dto/marketplace.dto"].PullDto }, "createAppStore": {}, "getAllAppStores": { type: t["./modules/marketplace/dto/marketplace.dto"].AllAppStoresDto }, "getEnabledAppStores": { type: t["./modules/marketplace/dto/marketplace.dto"].AllAppStoresDto }, "updateAppStore": {}, "deleteAppStore": {} } }], [import("./modules/apps/apps.controller"), { "AppsController": { "getInstalledApps": { type: t["./modules/apps/dto/app.dto"].MyAppsDto }, "getGuestApps": { type: t["./modules/apps/dto/app.dto"].GuestAppsDto }, "getApp": { type: t["./modules/apps/dto/app.dto"].GetAppDto } } }], [import("./modules/backups/backups.controller"), { "BackupsController": { "backupApp": {}, "restoreAppBackup": {}, "getAppBackups": { type: t["./modules/backups/dto/backups.dto"].GetAppBackupsDto }, "deleteAppBackup": {} } }], [import("./modules/app-lifecycle/app-lifecycle.controller"), { "AppLifecycleController": { "installApp": {}, "startApp": {}, "stopApp": {}, "restartApp": {}, "uninstallApp": {}, "resetApp": {}, "updateApp": {}, "updateAllApps": {}, "updateAppConfig": {} } }], [import("./modules/links/links.controller"), { "LinksController": { "getLinks": { type: t["./modules/links/dto/links.dto"].LinksDto }, "createLink": {}, "editLink": {}, "deleteLink": {} } }], [import("./modules/system/system.controller"), { "SystemController": { "systemLoad": { type: t["./modules/system/dto/system.dto"].LoadDto }, "downloadLocalCertificate": {} } }]] } }; }; \ No newline at end of file diff --git a/packages/backend/src/modules/app-lifecycle/app-lifecycle.service.ts b/packages/backend/src/modules/app-lifecycle/app-lifecycle.service.ts index ef80bab1ce..2ae38d427c 100644 --- a/packages/backend/src/modules/app-lifecycle/app-lifecycle.service.ts +++ b/packages/backend/src/modules/app-lifecycle/app-lifecycle.service.ts @@ -379,7 +379,7 @@ export class AppLifecycleService { async updateAllApps() { const installedApps = await this.appsService.getInstalledApps(); - const availableUpdates = installedApps.filter(({ app, updateInfo }) => Number(app.version) < Number(updateInfo.latestVersion)); + const availableUpdates = installedApps.filter(({ app, metadata }) => Number(app.version) < Number(metadata.latestVersion)); const updatePromises = availableUpdates.map(async ({ app }) => { try { diff --git a/packages/backend/src/modules/app-stores/app-store-files-manager.ts b/packages/backend/src/modules/app-stores/app-store-files-manager.ts index 2e848d7c3d..25ff38d70c 100644 --- a/packages/backend/src/modules/app-stores/app-store-files-manager.ts +++ b/packages/backend/src/modules/app-stores/app-store-files-manager.ts @@ -56,7 +56,7 @@ export class AppStoreFilesManager { if (parsedConfig.success && parsedConfig.data.available) { const description = (await this.filesystem.readTextFile(path.join(appRepoDir, 'metadata', 'description.md'))) ?? ''; - return { ...parsedConfig.data, id: namespacedId, description, userConfig: false }; + return { ...parsedConfig.data, id: namespacedId, description }; } } } catch (error) { diff --git a/packages/backend/src/modules/apps/app-files-manager.ts b/packages/backend/src/modules/apps/app-files-manager.ts index e87e03b0f8..1635069c0b 100644 --- a/packages/backend/src/modules/apps/app-files-manager.ts +++ b/packages/backend/src/modules/apps/app-files-manager.ts @@ -51,10 +51,8 @@ export class AppFilesManager { if (parsedConfig.success && parsedConfig.data.available) { const description = (await this.filesystem.readTextFile(path.join(appInstalledDir, 'metadata', 'description.md'))) ?? ''; - const userCompose = await this.getUserComposeFile(id); - const userEnv = await this.getUserEnv(id); - const userConfig = userCompose.content != null || userEnv.content != null; - return { ...parsedConfig.data, id, description, userConfig }; + + return { ...parsedConfig.data, id, description }; } } } catch (error) { diff --git a/packages/backend/src/modules/apps/apps.service.ts b/packages/backend/src/modules/apps/apps.service.ts index cde981805d..cdbb98f8f0 100644 --- a/packages/backend/src/modules/apps/apps.service.ts +++ b/packages/backend/src/modules/apps/apps.service.ts @@ -33,7 +33,7 @@ export class AppsService { this.logger.debug(`App ${app.id} not found in app files`); return null; } - return { app, info: appInfo, updateInfo }; + return { app, info: appInfo, metadata: updateInfo }; }); }), ); @@ -66,6 +66,10 @@ export class AppsService { let info = await this.appFilesManager.getInstalledAppInfo(id); + const userCompose = await this.appFilesManager.getUserComposeFile(id); + const userEnv = await this.appFilesManager.getUserEnv(id); + const hasCustomConfig = Boolean(userCompose.content) || Boolean(userEnv.content); + if (!info) { info = await this.marketplaceService.getAppInfoFromAppStore(id); } @@ -74,6 +78,11 @@ export class AppsService { throw new TranslatableError('APP_ERROR_APP_NOT_FOUND'); } - return { app, info, updateInfo }; + const metadata = { + hasCustomConfig, + ...updateInfo, + }; + + return { app, info, metadata }; } } diff --git a/packages/backend/src/modules/apps/dto/app.dto.ts b/packages/backend/src/modules/apps/dto/app.dto.ts index 03ec95fedc..a8b7409a87 100644 --- a/packages/backend/src/modules/apps/dto/app.dto.ts +++ b/packages/backend/src/modules/apps/dto/app.dto.ts @@ -1,5 +1,5 @@ import { APP_STATUS } from '@/core/database/drizzle/types'; -import { AppInfoDto, AppInfoSimpleDto, UpdateInfoDto } from '@/modules/marketplace/dto/marketplace.dto'; +import { AppInfoDto, AppInfoSimpleDto, MetadataDto } from '@/modules/marketplace/dto/marketplace.dto'; import { createZodDto } from 'nestjs-zod'; import { z } from 'zod'; @@ -27,7 +27,7 @@ export class MyAppsDto extends createZodDto( z.object({ app: AppDto.schema, info: AppInfoSimpleDto.schema, - updateInfo: UpdateInfoDto.schema, + metadata: MetadataDto.schema, }), ), }), @@ -48,6 +48,6 @@ export class GetAppDto extends createZodDto( z.object({ app: AppDto.schema.nullish(), info: AppInfoDto.schema, - updateInfo: UpdateInfoDto.schema, + metadata: MetadataDto.schema, }), ) {} diff --git a/packages/backend/src/modules/marketplace/dto/marketplace.dto.ts b/packages/backend/src/modules/marketplace/dto/marketplace.dto.ts index aa5ba63038..db60229933 100644 --- a/packages/backend/src/modules/marketplace/dto/marketplace.dto.ts +++ b/packages/backend/src/modules/marketplace/dto/marketplace.dto.ts @@ -83,10 +83,10 @@ export const appInfoSchema = z.object({ .refine((v) => v < Date.now()) .optional() .default(0), - userConfig: z.boolean().default(false), }); // Derived types +export type AppInfoInput = z.input; export type AppInfo = z.output; export type FormField = z.output; @@ -101,14 +101,14 @@ export class AppInfoSimpleDto extends createZodDto( created_at: true, supported_architectures: true, available: true, - userConfig: true, }), ) {} export class AppInfoDto extends createZodDto(appInfoSchema) {} -export class UpdateInfoDto extends createZodDto( +export class MetadataDto extends createZodDto( z.object({ + hasCustomConfig: z.boolean().optional(), latestVersion: z.number(), minTipiVersion: z.string().optional(), latestDockerVersion: z.string().optional(), @@ -137,7 +137,7 @@ export class SearchAppsDto extends createZodDto( export class AppDetailsDto extends createZodDto( z.object({ info: AppInfoDto.schema, - updateInfo: UpdateInfoDto.schema, + metadata: MetadataDto.schema, }), ) {} diff --git a/packages/backend/src/swagger.json b/packages/backend/src/swagger.json index 8b54e4907b..5cf393ee16 100644 --- a/packages/backend/src/swagger.json +++ b/packages/backend/src/swagger.json @@ -1665,10 +1665,6 @@ }, "available": { "type": "boolean" - }, - "userConfig": { - "type": "boolean", - "default": false } }, "required": [ @@ -2118,10 +2114,6 @@ }, "available": { "type": "boolean" - }, - "userConfig": { - "type": "boolean", - "default": false } }, "required": [ @@ -2131,9 +2123,12 @@ "available" ] }, - "updateInfo": { + "metadata": { "type": "object", "properties": { + "hasCustomConfig": { + "type": "boolean" + }, "latestVersion": { "type": "number" }, @@ -2152,7 +2147,7 @@ "required": [ "app", "info", - "updateInfo" + "metadata" ] } } @@ -2464,10 +2459,6 @@ "minimum": 0, "exclusiveMinimum": false, "default": 0 - }, - "userConfig": { - "type": "boolean", - "default": false } }, "required": [ @@ -2792,10 +2783,6 @@ "minimum": 0, "exclusiveMinimum": false, "default": 0 - }, - "userConfig": { - "type": "boolean", - "default": false } }, "required": [ @@ -2809,9 +2796,12 @@ "source" ] }, - "updateInfo": { + "metadata": { "type": "object", "properties": { + "hasCustomConfig": { + "type": "boolean" + }, "latestVersion": { "type": "number" }, @@ -2829,7 +2819,7 @@ }, "required": [ "info", - "updateInfo" + "metadata" ] }, "SearchAppsDto": { @@ -2895,10 +2885,6 @@ }, "available": { "type": "boolean" - }, - "userConfig": { - "type": "boolean", - "default": false } }, "required": [ diff --git a/packages/backend/src/tests/utils/create-app-in-store.ts b/packages/backend/src/tests/utils/create-app-in-store.ts index 10aeeecbd0..c2abb85cf1 100644 --- a/packages/backend/src/tests/utils/create-app-in-store.ts +++ b/packages/backend/src/tests/utils/create-app-in-store.ts @@ -1,11 +1,11 @@ import fs from 'node:fs'; import path from 'node:path'; import { DATA_DIR } from '@/common/constants'; -import type { AppInfo } from '@/modules/marketplace/dto/marketplace.dto'; +import type { AppInfo, AppInfoInput } from '@/modules/marketplace/dto/marketplace.dto'; import { faker } from '@faker-js/faker'; export const createAppInStore = async (storeId: number, app: Partial = {}) => { - const appInfo: AppInfo = { + const appInfo: AppInfoInput = { id: faker.string.uuid(), name: faker.lorem.words(2), port: faker.number.int({ min: 1000, max: 9999 }), @@ -29,7 +29,6 @@ export const createAppInStore = async (storeId: number, app: Partial = force_expose: false, generate_vapid_keys: false, form_fields: [], - userConfig: false, ...app, }; diff --git a/packages/frontend/src/api-client/types.gen.ts b/packages/frontend/src/api-client/types.gen.ts index 149963a057..d8e53d3726 100644 --- a/packages/frontend/src/api-client/types.gen.ts +++ b/packages/frontend/src/api-client/types.gen.ts @@ -71,7 +71,6 @@ export type AppContextDto = { created_at?: number; supported_architectures?: Array<'arm64' | 'amd64'>; available: boolean; - userConfig?: boolean; }>; updatesAvailable: number; }; @@ -220,9 +219,9 @@ export type GetAppDto = { min_tipi_version?: string; created_at?: number; updated_at?: number; - userConfig?: boolean; }; - updateInfo: { + metadata: { + hasCustomConfig?: boolean; latestVersion: number; minTipiVersion?: string; latestDockerVersion?: string; @@ -344,7 +343,6 @@ export type GuestAppsDto = { min_tipi_version?: string; created_at?: number; updated_at?: number; - userConfig?: boolean; }; }>; }; @@ -442,9 +440,9 @@ export type MyAppsDto = { created_at?: number; supported_architectures?: Array<'arm64' | 'amd64'>; available: boolean; - userConfig?: boolean; }; - updateInfo: { + metadata: { + hasCustomConfig?: boolean; latestVersion: number; minTipiVersion?: string; latestDockerVersion?: string; @@ -524,7 +522,6 @@ export type SearchAppsDto = { created_at?: number; supported_architectures?: Array<'arm64' | 'amd64'>; available: boolean; - userConfig?: boolean; }>; nextCursor?: string; total: number; diff --git a/packages/frontend/src/modules/app/containers/app-actions/app-actions.tsx b/packages/frontend/src/modules/app/containers/app-actions/app-actions.tsx index d8b66dcf0f..04704a89ef 100644 --- a/packages/frontend/src/modules/app/containers/app-actions/app-actions.tsx +++ b/packages/frontend/src/modules/app/containers/app-actions/app-actions.tsx @@ -27,7 +27,7 @@ import toast from 'react-hot-toast'; import { useTranslation } from 'react-i18next'; import './app-actions.css'; import { startAppMutation } from '@/api-client/@tanstack/react-query.gen'; -import type { AppDetails, AppInfo, AppUpdateInfo } from '@/types/app.types'; +import type { AppDetails, AppInfo, AppMetadata } from '@/types/app.types'; import type { TranslatableError } from '@/types/error.types'; import { useMutation } from '@tanstack/react-query'; import { InstallDialog } from '../../components/dialogs/install-dialog/install-dialog'; @@ -42,7 +42,7 @@ import { useAppStatus } from '../../helpers/use-app-status'; interface IProps { app?: AppDetails | null; info: AppInfo; - updateInfo?: AppUpdateInfo; + metadata?: AppMetadata; localDomain?: string; } @@ -65,7 +65,7 @@ const ActionButton: React.FC = (props) => { type OpenType = 'local' | 'domain' | 'local_domain'; -export const AppActions = ({ app, info, localDomain, updateInfo }: IProps) => { +export const AppActions = ({ app, info, localDomain, metadata }: IProps) => { const installDisclosure = useDisclosure(); const stopDisclosure = useDisclosure(); const restartDisclosure = useDisclosure(); @@ -78,7 +78,7 @@ export const AppActions = ({ app, info, localDomain, updateInfo }: IProps) => { const { setOptimisticStatus } = useAppStatus(); const hostname = typeof window !== 'undefined' ? window.location.hostname : ''; - const updateAvailable = Number(app?.version ?? 0) < Number(updateInfo?.latestVersion || 0); + const updateAvailable = Number(app?.version ?? 0) < Number(metadata?.latestVersion || 0); const buttons: JSX.Element[] = []; @@ -222,9 +222,7 @@ export const AppActions = ({ app, info, localDomain, updateInfo }: IProps) => { }, 300); }; - const newVersion = [updateInfo?.latestDockerVersion ? `${updateInfo?.latestDockerVersion}` : '', `(${String(updateInfo?.latestVersion)})`].join( - ' ', - ); + const newVersion = [metadata?.latestDockerVersion ? `${metadata?.latestDockerVersion}` : '', `(${String(metadata?.latestVersion)})`].join(' '); return ( <> diff --git a/packages/frontend/src/modules/app/containers/app-details-tabs/app-details-tabs.tsx b/packages/frontend/src/modules/app/containers/app-details-tabs/app-details-tabs.tsx index e4cc174ce2..b4f5973165 100644 --- a/packages/frontend/src/modules/app/containers/app-details-tabs/app-details-tabs.tsx +++ b/packages/frontend/src/modules/app/containers/app-details-tabs/app-details-tabs.tsx @@ -1,7 +1,7 @@ import { Markdown } from '@/components/markdown/markdown'; import { DataGrid, DataGridItem } from '@/components/ui/DataGrid'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; -import type { AppDetails, AppInfo } from '@/types/app.types'; +import type { AppDetails, AppInfo, AppMetadata } from '@/types/app.types'; import { IconAlertCircle, IconExternalLink } from '@tabler/icons-react'; import { Suspense, lazy } from 'react'; import { useTranslation } from 'react-i18next'; @@ -13,9 +13,10 @@ const AppLogs = lazy(() => import('../app-logs/app-logs').then((module) => ({ de interface IProps { info: AppInfo; app?: AppDetails | null; + metadata?: AppMetadata; } -export const AppDetailsTabs = ({ info, app }: IProps) => { +export const AppDetailsTabs = ({ info, app, metadata }: IProps) => { const { t } = useTranslation(); const [params] = useSearchParams(); @@ -100,9 +101,9 @@ export const AppDetailsTabs = ({ info, app }: IProps) => { )} - {app && ( + {metadata && ( - {info.userConfig ? t('YES') : t('NO') } + {metadata.hasCustomConfig ? t('YES') : t('NO')} )} diff --git a/packages/frontend/src/modules/app/pages/app-details-page.tsx b/packages/frontend/src/modules/app/pages/app-details-page.tsx index 324c4cd629..36d3389370 100644 --- a/packages/frontend/src/modules/app/pages/app-details-page.tsx +++ b/packages/frontend/src/modules/app/pages/app-details-page.tsx @@ -19,7 +19,7 @@ export const AppDetailsPage = () => { const { userSettings } = useAppContext(); - const { info, app, updateInfo } = getApp.data; + const { info, app, metadata } = getApp.data; return (
@@ -34,10 +34,10 @@ export const AppDetailsPage = () => {
- +
- + ); }; diff --git a/packages/frontend/src/modules/app/pages/my-apps-page.tsx b/packages/frontend/src/modules/app/pages/my-apps-page.tsx index 0fa258d724..39d5c84290 100644 --- a/packages/frontend/src/modules/app/pages/my-apps-page.tsx +++ b/packages/frontend/src/modules/app/pages/my-apps-page.tsx @@ -22,8 +22,8 @@ export const MyAppsPage = () => { const { installed } = apps; const { links: customLinks = [] } = links; - const renderApp = ({ info, app, updateInfo }: (typeof installed)[number]) => { - const updateAvailable = Number(app.version) < Number(updateInfo.latestVersion); + const renderApp = ({ info, app, metadata }: (typeof installed)[number]) => { + const updateAvailable = Number(app.version) < Number(metadata.latestVersion); const [appId, storeId] = app.id.split('_'); diff --git a/packages/frontend/src/types/app.types.ts b/packages/frontend/src/types/app.types.ts index 9c18376c51..02a9826940 100644 --- a/packages/frontend/src/types/app.types.ts +++ b/packages/frontend/src/types/app.types.ts @@ -2,7 +2,7 @@ import type { GetAllAppStoresResponse, GetAppBackupsDto, GetAppDto, LinksDto, Se export type FormField = NonNullable[number]; export type AppInfo = GetAppDto['info']; -export type AppUpdateInfo = GetAppDto['updateInfo']; +export type AppMetadata = GetAppDto['metadata']; export type AppDetails = NonNullable; export type AppStatus = NonNullable['status'];