mirror of
https://github.com/immich-app/immich.git
synced 2025-12-17 09:13:17 +03:00
Compare commits
1 Commits
feat/disab
...
deps/immic
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6bf2f3371d |
@@ -43,7 +43,6 @@ These environment variables are used by the `docker-compose.yml` file and do **N
|
|||||||
| `IMMICH_PROCESS_INVALID_IMAGES` | When `true`, generate thumbnails for invalid images | | server | microservices |
|
| `IMMICH_PROCESS_INVALID_IMAGES` | When `true`, generate thumbnails for invalid images | | server | microservices |
|
||||||
| `IMMICH_TRUSTED_PROXIES` | List of comma-separated IPs set as trusted proxies | | server | api |
|
| `IMMICH_TRUSTED_PROXIES` | List of comma-separated IPs set as trusted proxies | | server | api |
|
||||||
| `IMMICH_IGNORE_MOUNT_CHECK_ERRORS` | See [System Integrity](/administration/system-integrity) | | server | api, microservices |
|
| `IMMICH_IGNORE_MOUNT_CHECK_ERRORS` | See [System Integrity](/administration/system-integrity) | | server | api, microservices |
|
||||||
| `IMMICH_ALLOW_SETUP` | When `false` disables the `/auth/admin-sign-up` endpoint | `true` | server | api |
|
|
||||||
|
|
||||||
\*1: `TZ` should be set to a `TZ identifier` from [this list][tz-list]. For example, `TZ="Etc/UTC"`.
|
\*1: `TZ` should be set to a `TZ identifier` from [this list][tz-list]. For example, `TZ="Etc/UTC"`.
|
||||||
`TZ` is used by `exiftool` as a fallback in case the timezone cannot be determined from the image metadata. It is also used for logfile timestamps and cron job execution.
|
`TZ` is used by `exiftool` as a fallback in case the timezone cannot be determined from the image metadata. It is also used for logfile timestamps and cron job execution.
|
||||||
|
|||||||
18
pnpm-lock.yaml
generated
18
pnpm-lock.yaml
generated
@@ -717,8 +717,8 @@ importers:
|
|||||||
specifier: file:../open-api/typescript-sdk
|
specifier: file:../open-api/typescript-sdk
|
||||||
version: link:../open-api/typescript-sdk
|
version: link:../open-api/typescript-sdk
|
||||||
'@immich/ui':
|
'@immich/ui':
|
||||||
specifier: ^0.50.1
|
specifier: ^0.51.0
|
||||||
version: 0.50.1(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)
|
version: 0.51.0(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)
|
||||||
'@mapbox/mapbox-gl-rtl-text':
|
'@mapbox/mapbox-gl-rtl-text':
|
||||||
specifier: 0.2.3
|
specifier: 0.2.3
|
||||||
version: 0.2.3(mapbox-gl@1.13.3)
|
version: 0.2.3(mapbox-gl@1.13.3)
|
||||||
@@ -3012,8 +3012,8 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
svelte: ^5.0.0
|
svelte: ^5.0.0
|
||||||
|
|
||||||
'@immich/ui@0.50.1':
|
'@immich/ui@0.51.0':
|
||||||
resolution: {integrity: sha512-fNlQGh75ZFa/UZAgJaYk9/ItHOXHNNzN4CunjCmE7WocVVkUZbUxopN9Ku3F5GULSqD/zJ5gNO6PQAZ1ZoSaaQ==}
|
resolution: {integrity: sha512-suCArLt/h0fPwT1BnpDpycuyx0cZYkuZaW8NOKvzTTAulIT6q67SWCz2Gb+Ae3blKh/b7r5Qoz65v5FzWqZdPQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
svelte: ^5.0.0
|
svelte: ^5.0.0
|
||||||
|
|
||||||
@@ -10424,6 +10424,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-i/w5Ie4tENfGYbdCo2iJ+oies0vOFd8QXWHopKOUzudfLCvnmeheF2PpHp89Z2azpc+c2su3lMiWO/SpP+429A==}
|
resolution: {integrity: sha512-i/w5Ie4tENfGYbdCo2iJ+oies0vOFd8QXWHopKOUzudfLCvnmeheF2PpHp89Z2azpc+c2su3lMiWO/SpP+429A==}
|
||||||
engines: {node: '>=0.12.18'}
|
engines: {node: '>=0.12.18'}
|
||||||
|
|
||||||
|
simple-icons@16.2.0:
|
||||||
|
resolution: {integrity: sha512-SCV4cAX8O2M2WQ3QJm+3k4QdcUIJiBiwKNViQiOwPjWbFWgypzelviW+Bp0A2ij6r/DIEZpTTDcLgD/BE4aexQ==}
|
||||||
|
engines: {node: '>=0.12.18'}
|
||||||
|
|
||||||
sirv@2.0.4:
|
sirv@2.0.4:
|
||||||
resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==}
|
resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
@@ -14653,14 +14657,14 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
svelte: 5.43.3
|
svelte: 5.43.3
|
||||||
|
|
||||||
'@immich/ui@0.50.1(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)':
|
'@immich/ui@0.51.0(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@immich/svelte-markdown-preprocess': 0.1.0(svelte@5.43.3)
|
'@immich/svelte-markdown-preprocess': 0.1.0(svelte@5.43.3)
|
||||||
'@internationalized/date': 3.10.0
|
'@internationalized/date': 3.10.0
|
||||||
'@mdi/js': 7.4.47
|
'@mdi/js': 7.4.47
|
||||||
bits-ui: 2.14.4(@internationalized/date@3.10.0)(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)
|
bits-ui: 2.14.4(@internationalized/date@3.10.0)(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)
|
||||||
luxon: 3.7.2
|
luxon: 3.7.2
|
||||||
simple-icons: 15.22.0
|
simple-icons: 16.2.0
|
||||||
svelte: 5.43.3
|
svelte: 5.43.3
|
||||||
svelte-highlight: 7.9.0
|
svelte-highlight: 7.9.0
|
||||||
tailwind-merge: 3.4.0
|
tailwind-merge: 3.4.0
|
||||||
@@ -23452,6 +23456,8 @@ snapshots:
|
|||||||
|
|
||||||
simple-icons@15.22.0: {}
|
simple-icons@15.22.0: {}
|
||||||
|
|
||||||
|
simple-icons@16.2.0: {}
|
||||||
|
|
||||||
sirv@2.0.4:
|
sirv@2.0.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@polka/url': 1.0.0-next.29
|
'@polka/url': 1.0.0-next.29
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export class EnvDto {
|
|||||||
IMMICH_MICROSERVICES_METRICS_PORT?: number;
|
IMMICH_MICROSERVICES_METRICS_PORT?: number;
|
||||||
|
|
||||||
@ValidateBoolean({ optional: true })
|
@ValidateBoolean({ optional: true })
|
||||||
IMMICH_ALLOW_EXTERNAL_PLUGINS?: boolean;
|
IMMICH_PLUGINS_ENABLED?: boolean;
|
||||||
|
|
||||||
@Optional()
|
@Optional()
|
||||||
@Matches(/^\//, { message: 'IMMICH_PLUGINS_INSTALL_FOLDER must be an absolute path' })
|
@Matches(/^\//, { message: 'IMMICH_PLUGINS_INSTALL_FOLDER must be an absolute path' })
|
||||||
@@ -113,9 +113,6 @@ export class EnvDto {
|
|||||||
@Optional()
|
@Optional()
|
||||||
IMMICH_THIRD_PARTY_SUPPORT_URL?: string;
|
IMMICH_THIRD_PARTY_SUPPORT_URL?: string;
|
||||||
|
|
||||||
@ValidateBoolean({ optional: true })
|
|
||||||
IMMICH_ALLOW_SETUP?: boolean;
|
|
||||||
|
|
||||||
@IsIPRange({ requireCIDR: false }, { each: true })
|
@IsIPRange({ requireCIDR: false }, { each: true })
|
||||||
@Transform(({ value }) =>
|
@Transform(({ value }) =>
|
||||||
value && typeof value === 'string'
|
value && typeof value === 'string'
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ const getEnv = () => {
|
|||||||
|
|
||||||
const resetEnv = () => {
|
const resetEnv = () => {
|
||||||
for (const env of [
|
for (const env of [
|
||||||
'IMMICH_ALLOW_EXTERNAL_PLUGINS',
|
|
||||||
'IMMICH_ALLOW_SETUP',
|
|
||||||
'IMMICH_ENV',
|
'IMMICH_ENV',
|
||||||
'IMMICH_WORKERS_INCLUDE',
|
'IMMICH_WORKERS_INCLUDE',
|
||||||
'IMMICH_WORKERS_EXCLUDE',
|
'IMMICH_WORKERS_EXCLUDE',
|
||||||
@@ -77,9 +75,6 @@ describe('getEnv', () => {
|
|||||||
configFile: undefined,
|
configFile: undefined,
|
||||||
logLevel: undefined,
|
logLevel: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(config.plugins.external).toEqual({ allow: false });
|
|
||||||
expect(config.setup).toEqual({ allow: true });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('IMMICH_MEDIA_LOCATION', () => {
|
describe('IMMICH_MEDIA_LOCATION', () => {
|
||||||
@@ -89,32 +84,6 @@ describe('getEnv', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('IMMICH_ALLOW_EXTERNAL_PLUGINS', () => {
|
|
||||||
it('should disable plugins', () => {
|
|
||||||
process.env.IMMICH_ALLOW_EXTERNAL_PLUGINS = 'false';
|
|
||||||
const config = getEnv();
|
|
||||||
expect(config.plugins.external).toEqual({ allow: false });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw an error for invalid value', () => {
|
|
||||||
process.env.IMMICH_ALLOW_EXTERNAL_PLUGINS = 'invalid';
|
|
||||||
expect(() => getEnv()).toThrowError('IMMICH_ALLOW_EXTERNAL_PLUGINS must be a boolean value');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('IMMICH_ALLOW_SETUP', () => {
|
|
||||||
it('should disable setup', () => {
|
|
||||||
process.env.IMMICH_ALLOW_SETUP = 'false';
|
|
||||||
const { setup } = getEnv();
|
|
||||||
expect(setup).toEqual({ allow: false });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw an error for invalid value', () => {
|
|
||||||
process.env.IMMICH_ALLOW_SETUP = 'invalid';
|
|
||||||
expect(() => getEnv()).toThrowError('IMMICH_ALLOW_SETUP must be a boolean value');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('database', () => {
|
describe('database', () => {
|
||||||
it('should use defaults', () => {
|
it('should use defaults', () => {
|
||||||
const { database } = getEnv();
|
const { database } = getEnv();
|
||||||
|
|||||||
@@ -90,10 +90,6 @@ export interface EnvData {
|
|||||||
|
|
||||||
redis: RedisOptions;
|
redis: RedisOptions;
|
||||||
|
|
||||||
setup: {
|
|
||||||
allow: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
telemetry: {
|
telemetry: {
|
||||||
apiPort: number;
|
apiPort: number;
|
||||||
microservicesPort: number;
|
microservicesPort: number;
|
||||||
@@ -108,11 +104,9 @@ export interface EnvData {
|
|||||||
workers: ImmichWorker[];
|
workers: ImmichWorker[];
|
||||||
|
|
||||||
plugins: {
|
plugins: {
|
||||||
external: {
|
enabled: boolean;
|
||||||
allow: boolean;
|
|
||||||
installFolder?: string;
|
installFolder?: string;
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
noColor: boolean;
|
noColor: boolean;
|
||||||
nodeVersion?: string;
|
nodeVersion?: string;
|
||||||
@@ -319,10 +313,6 @@ const getEnv = (): EnvData => {
|
|||||||
corePlugin: join(buildFolder, 'corePlugin'),
|
corePlugin: join(buildFolder, 'corePlugin'),
|
||||||
},
|
},
|
||||||
|
|
||||||
setup: {
|
|
||||||
allow: dto.IMMICH_ALLOW_SETUP ?? true,
|
|
||||||
},
|
|
||||||
|
|
||||||
storage: {
|
storage: {
|
||||||
ignoreMountCheckErrors: !!dto.IMMICH_IGNORE_MOUNT_CHECK_ERRORS,
|
ignoreMountCheckErrors: !!dto.IMMICH_IGNORE_MOUNT_CHECK_ERRORS,
|
||||||
mediaLocation: dto.IMMICH_MEDIA_LOCATION,
|
mediaLocation: dto.IMMICH_MEDIA_LOCATION,
|
||||||
@@ -337,11 +327,9 @@ const getEnv = (): EnvData => {
|
|||||||
workers,
|
workers,
|
||||||
|
|
||||||
plugins: {
|
plugins: {
|
||||||
external: {
|
enabled: !!dto.IMMICH_PLUGINS_ENABLED,
|
||||||
allow: dto.IMMICH_ALLOW_EXTERNAL_PLUGINS ?? false,
|
|
||||||
installFolder: dto.IMMICH_PLUGINS_INSTALL_FOLDER,
|
installFolder: dto.IMMICH_PLUGINS_INSTALL_FOLDER,
|
||||||
},
|
},
|
||||||
},
|
|
||||||
|
|
||||||
noColor: !!dto.NO_COLOR,
|
noColor: !!dto.NO_COLOR,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -165,11 +165,6 @@ export class AuthService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async adminSignUp(dto: SignUpDto): Promise<UserAdminResponseDto> {
|
async adminSignUp(dto: SignUpDto): Promise<UserAdminResponseDto> {
|
||||||
const { setup } = this.configRepository.getEnv();
|
|
||||||
if (!setup.allow) {
|
|
||||||
throw new BadRequestException('Admin setup is disabled');
|
|
||||||
}
|
|
||||||
|
|
||||||
const adminUser = await this.userRepository.getAdmin();
|
const adminUser = await this.userRepository.getAdmin();
|
||||||
if (adminUser) {
|
if (adminUser) {
|
||||||
throw new BadRequestException('The server already has an admin');
|
throw new BadRequestException('The server already has an admin');
|
||||||
|
|||||||
@@ -80,8 +80,8 @@ export class PluginService extends BaseService {
|
|||||||
this.logger.log(`Successfully processed core plugin: ${coreManifest.name} (version ${coreManifest.version})`);
|
this.logger.log(`Successfully processed core plugin: ${coreManifest.name} (version ${coreManifest.version})`);
|
||||||
|
|
||||||
// Load external plugins
|
// Load external plugins
|
||||||
if (plugins.external.allow && plugins.external.installFolder) {
|
if (plugins.enabled && plugins.installFolder) {
|
||||||
await this.loadExternalPlugins(plugins.external.installFolder);
|
await this.loadExternalPlugins(plugins.installFolder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -115,9 +115,8 @@ export class ServerService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getSystemConfig(): Promise<ServerConfigDto> {
|
async getSystemConfig(): Promise<ServerConfigDto> {
|
||||||
const { setup } = this.configRepository.getEnv();
|
|
||||||
const config = await this.getConfig({ withCache: false });
|
const config = await this.getConfig({ withCache: false });
|
||||||
const isInitialized = !setup.allow || (await this.userRepository.hasAdmin());
|
const isInitialized = await this.userRepository.hasAdmin();
|
||||||
const onboarding = await this.systemMetadataRepository.get(SystemMetadataKey.AdminOnboarding);
|
const onboarding = await this.systemMetadataRepository.get(SystemMetadataKey.AdminOnboarding);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -75,10 +75,6 @@ const envData: EnvData = {
|
|||||||
corePlugin: '/build/corePlugin',
|
corePlugin: '/build/corePlugin',
|
||||||
},
|
},
|
||||||
|
|
||||||
setup: {
|
|
||||||
allow: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
storage: {
|
storage: {
|
||||||
ignoreMountCheckErrors: false,
|
ignoreMountCheckErrors: false,
|
||||||
},
|
},
|
||||||
@@ -92,11 +88,9 @@ const envData: EnvData = {
|
|||||||
workers: [ImmichWorker.Api, ImmichWorker.Microservices],
|
workers: [ImmichWorker.Api, ImmichWorker.Microservices],
|
||||||
|
|
||||||
plugins: {
|
plugins: {
|
||||||
external: {
|
enabled: true,
|
||||||
allow: true,
|
|
||||||
installFolder: '/app/data/plugins',
|
installFolder: '/app/data/plugins',
|
||||||
},
|
},
|
||||||
},
|
|
||||||
|
|
||||||
noColor: false,
|
noColor: false,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
"@formatjs/icu-messageformat-parser": "^2.9.8",
|
"@formatjs/icu-messageformat-parser": "^2.9.8",
|
||||||
"@immich/justified-layout-wasm": "^0.4.3",
|
"@immich/justified-layout-wasm": "^0.4.3",
|
||||||
"@immich/sdk": "file:../open-api/typescript-sdk",
|
"@immich/sdk": "file:../open-api/typescript-sdk",
|
||||||
"@immich/ui": "^0.50.1",
|
"@immich/ui": "^0.51.0",
|
||||||
"@mapbox/mapbox-gl-rtl-text": "0.2.3",
|
"@mapbox/mapbox-gl-rtl-text": "0.2.3",
|
||||||
"@mdi/js": "^7.4.47",
|
"@mdi/js": "^7.4.47",
|
||||||
"@photo-sphere-viewer/core": "^5.14.0",
|
"@photo-sphere-viewer/core": "^5.14.0",
|
||||||
|
|||||||
@@ -21,7 +21,14 @@
|
|||||||
import { copyToClipboard, getReleaseType, semverToName } from '$lib/utils';
|
import { copyToClipboard, getReleaseType, semverToName } from '$lib/utils';
|
||||||
import { maintenanceShouldRedirect } from '$lib/utils/maintenance';
|
import { maintenanceShouldRedirect } from '$lib/utils/maintenance';
|
||||||
import { isAssetViewerRoute } from '$lib/utils/navigation';
|
import { isAssetViewerRoute } from '$lib/utils/navigation';
|
||||||
import { CommandPaletteContext, modalManager, setTranslations, toastManager, type ActionItem } from '@immich/ui';
|
import {
|
||||||
|
CommandPaletteContext,
|
||||||
|
TooltipProvider,
|
||||||
|
modalManager,
|
||||||
|
setTranslations,
|
||||||
|
toastManager,
|
||||||
|
type ActionItem,
|
||||||
|
} from '@immich/ui';
|
||||||
import { mdiAccountMultipleOutline, mdiBookshelf, mdiCog, mdiServer, mdiSync, mdiThemeLightDark } from '@mdi/js';
|
import { mdiAccountMultipleOutline, mdiBookshelf, mdiCog, mdiServer, mdiSync, mdiThemeLightDark } from '@mdi/js';
|
||||||
import { onMount, type Snippet } from 'svelte';
|
import { onMount, type Snippet } from 'svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
@@ -228,6 +235,7 @@
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<TooltipProvider>
|
||||||
{#if page.data.error}
|
{#if page.data.error}
|
||||||
<ErrorLayout error={page.data.error}></ErrorLayout>
|
<ErrorLayout error={page.data.error}></ErrorLayout>
|
||||||
{:else}
|
{:else}
|
||||||
@@ -240,3 +248,4 @@
|
|||||||
|
|
||||||
<DownloadPanel />
|
<DownloadPanel />
|
||||||
<UploadPanel />
|
<UploadPanel />
|
||||||
|
</TooltipProvider>
|
||||||
|
|||||||
Reference in New Issue
Block a user