mirror of
https://github.com/immich-app/immich
synced 2026-08-22 13:13:05 +00:00
feat: separate shared link domain input
This commit is contained in:
parent
4609adbb6d
commit
db5ef0f95b
24 changed files with 111 additions and 13 deletions
|
|
@ -213,6 +213,7 @@ The default configuration looks like this:
|
|||
},
|
||||
"server": {
|
||||
"externalDomain": "",
|
||||
"sharedLinkDomain": "",
|
||||
"loginPageMessage": "",
|
||||
"publicUsers": true
|
||||
},
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@ describe('/server', () => {
|
|||
userDeleteDelay: 7,
|
||||
isInitialized: true,
|
||||
externalDomain: '',
|
||||
sharedLinkDomain: '',
|
||||
publicUsers: true,
|
||||
isOnboarded: false,
|
||||
maintenanceMode: false,
|
||||
|
|
|
|||
|
|
@ -148,6 +148,7 @@ export const setupBaseMockApiRoutes = async (context: BrowserContext, adminUserI
|
|||
isInitialized: true,
|
||||
isOnboarded: true,
|
||||
externalDomain: '',
|
||||
sharedLinkDomain: '',
|
||||
publicUsers: true,
|
||||
mapDarkStyleUrl: 'https://tiles.immich.cloud/v1/style/dark.json',
|
||||
mapLightStyleUrl: 'https://tiles.immich.cloud/v1/style/light.json',
|
||||
|
|
|
|||
|
|
@ -342,6 +342,8 @@
|
|||
"send_welcome_email": "Send velkomst-email",
|
||||
"server_external_domain_settings": "Eksternt domæne",
|
||||
"server_external_domain_settings_description": "Domæne brugt til eksterne links",
|
||||
"server_shared_link_domain_settings": "Delt link domæne",
|
||||
"server_shared_link_domain_settings_description": "Domæne brugt til delte links",
|
||||
"server_public_users": "Offentlige brugere",
|
||||
"server_public_users_description": "Alle brugere (navn og e-mail) vises, når en bruger tilføjes til delte album. Når den er deaktiveret, vil brugerlisten kun være tilgængelig for administratorbrugere.",
|
||||
"server_settings": "Serverindstillinger",
|
||||
|
|
|
|||
|
|
@ -342,6 +342,8 @@
|
|||
"send_welcome_email": "Send welcome email",
|
||||
"server_external_domain_settings": "External domain",
|
||||
"server_external_domain_settings_description": "Domain used for external links",
|
||||
"server_shared_link_domain_settings": "Shared link domain",
|
||||
"server_shared_link_domain_settings_description": "Domain used for shared links",
|
||||
"server_public_users": "Public Users",
|
||||
"server_public_users_description": "All users (name and email) are listed when adding a user to shared albums. When disabled, the user list will only be available to admin users.",
|
||||
"server_settings": "Server Settings",
|
||||
|
|
|
|||
|
|
@ -342,6 +342,8 @@
|
|||
"send_welcome_email": "Send welcome email",
|
||||
"server_external_domain_settings": "External domain",
|
||||
"server_external_domain_settings_description": "Domain used for external links",
|
||||
"server_shared_link_domain_settings": "Shared link domain",
|
||||
"server_shared_link_domain_settings_description": "Domain used for shared links",
|
||||
"server_public_users": "Public Users",
|
||||
"server_public_users_description": "All users (name and email) are listed when adding a user to shared albums. When disabled, the user list will only be available to admin users.",
|
||||
"server_settings": "Server Settings",
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@ class SharedLinkEditPage extends HookConsumerWidget {
|
|||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final themeData = context.themeData;
|
||||
final colorScheme = context.colorScheme;
|
||||
final externalDomain = ref.watch(serverInfoProvider.select((s) => s.serverConfig.externalDomain));
|
||||
final displayServerUrl = externalDomain.isNotEmpty ? externalDomain : getServerUrl();
|
||||
final sharedLinkDomain = ref.watch(serverInfoProvider.select((s) => s.serverConfig.sharedLinkDomain));
|
||||
final displayServerUrl = sharedLinkDomain.isNotEmpty ? sharedLinkDomain : getServerUrl();
|
||||
final expiryPresets = <(Duration, String)>[
|
||||
(Duration.zero, context.t.never),
|
||||
(const Duration(minutes: 30), context.t.shared_link_edit_expire_after_option_minutes(count: 30)),
|
||||
|
|
@ -345,9 +345,9 @@ class SharedLinkEditPage extends HookConsumerWidget {
|
|||
if (!context.mounted) {
|
||||
return;
|
||||
}
|
||||
final externalDomain = ref.read(serverInfoProvider.select((s) => s.serverConfig.externalDomain));
|
||||
final sharedLinkDomain = ref.read(serverInfoProvider.select((s) => s.serverConfig.sharedLinkDomain));
|
||||
|
||||
final serverUrl = externalDomain.isNotEmpty ? externalDomain : getServerUrl();
|
||||
final serverUrl = sharedLinkDomain.isNotEmpty ? sharedLinkDomain : getServerUrl();
|
||||
|
||||
if (newLink != null) {
|
||||
newShareLink.value = buildSharedLinkUrl(baseUrl: serverUrl, slug: newLink.slug, key: newLink.key) ?? '';
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ class ServerInfoNotifier extends StateNotifier<ServerInfo> {
|
|||
trashDays: 30,
|
||||
oauthButtonText: '',
|
||||
externalDomain: '',
|
||||
sharedLinkDomain: '',
|
||||
mapLightStyleUrl: 'https://tiles.immich.cloud/v1/style/light.json',
|
||||
mapDarkStyleUrl: 'https://tiles.immich.cloud/v1/style/dark.json',
|
||||
),
|
||||
|
|
|
|||
11
mobile/openapi/lib/model/server_config_dto.dart
generated
11
mobile/openapi/lib/model/server_config_dto.dart
generated
|
|
@ -23,6 +23,7 @@ class ServerConfigDto {
|
|||
required this.minFaces,
|
||||
required this.oauthButtonText,
|
||||
required this.publicUsers,
|
||||
required this.sharedLinkDomain,
|
||||
required this.trashDays,
|
||||
required this.userDeleteDelay,
|
||||
});
|
||||
|
|
@ -60,6 +61,9 @@ class ServerConfigDto {
|
|||
/// Whether public user registration is enabled
|
||||
bool publicUsers;
|
||||
|
||||
/// Shared link domain URL
|
||||
String sharedLinkDomain;
|
||||
|
||||
/// Number of days before trashed assets are permanently deleted
|
||||
///
|
||||
/// Minimum value: -9007199254740991
|
||||
|
|
@ -84,6 +88,7 @@ class ServerConfigDto {
|
|||
other.minFaces == minFaces &&
|
||||
other.oauthButtonText == oauthButtonText &&
|
||||
other.publicUsers == publicUsers &&
|
||||
other.sharedLinkDomain == sharedLinkDomain &&
|
||||
other.trashDays == trashDays &&
|
||||
other.userDeleteDelay == userDeleteDelay;
|
||||
|
||||
|
|
@ -100,11 +105,12 @@ class ServerConfigDto {
|
|||
(minFaces.hashCode) +
|
||||
(oauthButtonText.hashCode) +
|
||||
(publicUsers.hashCode) +
|
||||
(sharedLinkDomain.hashCode) +
|
||||
(trashDays.hashCode) +
|
||||
(userDeleteDelay.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'ServerConfigDto[externalDomain=$externalDomain, isInitialized=$isInitialized, isOnboarded=$isOnboarded, loginPageMessage=$loginPageMessage, maintenanceMode=$maintenanceMode, mapDarkStyleUrl=$mapDarkStyleUrl, mapLightStyleUrl=$mapLightStyleUrl, minFaces=$minFaces, oauthButtonText=$oauthButtonText, publicUsers=$publicUsers, trashDays=$trashDays, userDeleteDelay=$userDeleteDelay]';
|
||||
String toString() => 'ServerConfigDto[externalDomain=$externalDomain, isInitialized=$isInitialized, isOnboarded=$isOnboarded, loginPageMessage=$loginPageMessage, maintenanceMode=$maintenanceMode, mapDarkStyleUrl=$mapDarkStyleUrl, mapLightStyleUrl=$mapLightStyleUrl, minFaces=$minFaces, oauthButtonText=$oauthButtonText, publicUsers=$publicUsers, sharedLinkDomain=$sharedLinkDomain, trashDays=$trashDays, userDeleteDelay=$userDeleteDelay]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
|
|
@ -118,6 +124,7 @@ class ServerConfigDto {
|
|||
json[r'minFaces'] = this.minFaces;
|
||||
json[r'oauthButtonText'] = this.oauthButtonText;
|
||||
json[r'publicUsers'] = this.publicUsers;
|
||||
json[r'sharedLinkDomain'] = this.sharedLinkDomain;
|
||||
json[r'trashDays'] = this.trashDays;
|
||||
json[r'userDeleteDelay'] = this.userDeleteDelay;
|
||||
return json;
|
||||
|
|
@ -142,6 +149,7 @@ class ServerConfigDto {
|
|||
minFaces: mapValueOfType<int>(json, r'minFaces')!,
|
||||
oauthButtonText: mapValueOfType<String>(json, r'oauthButtonText')!,
|
||||
publicUsers: mapValueOfType<bool>(json, r'publicUsers')!,
|
||||
sharedLinkDomain: mapValueOfType<String>(json, r'sharedLinkDomain')!,
|
||||
trashDays: mapValueOfType<int>(json, r'trashDays')!,
|
||||
userDeleteDelay: mapValueOfType<int>(json, r'userDeleteDelay')!,
|
||||
);
|
||||
|
|
@ -201,6 +209,7 @@ class ServerConfigDto {
|
|||
'minFaces',
|
||||
'oauthButtonText',
|
||||
'publicUsers',
|
||||
'sharedLinkDomain',
|
||||
'trashDays',
|
||||
'userDeleteDelay',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ class SystemConfigServerDto {
|
|||
required this.externalDomain,
|
||||
required this.loginPageMessage,
|
||||
required this.publicUsers,
|
||||
required this.sharedLinkDomain,
|
||||
});
|
||||
|
||||
/// External domain
|
||||
|
|
@ -27,27 +28,33 @@ class SystemConfigServerDto {
|
|||
/// Public users
|
||||
bool publicUsers;
|
||||
|
||||
/// Shared link domain
|
||||
String sharedLinkDomain;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is SystemConfigServerDto &&
|
||||
other.externalDomain == externalDomain &&
|
||||
other.loginPageMessage == loginPageMessage &&
|
||||
other.publicUsers == publicUsers;
|
||||
other.publicUsers == publicUsers &&
|
||||
other.sharedLinkDomain == sharedLinkDomain;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(externalDomain.hashCode) +
|
||||
(loginPageMessage.hashCode) +
|
||||
(publicUsers.hashCode);
|
||||
(publicUsers.hashCode) +
|
||||
(sharedLinkDomain.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SystemConfigServerDto[externalDomain=$externalDomain, loginPageMessage=$loginPageMessage, publicUsers=$publicUsers]';
|
||||
String toString() => 'SystemConfigServerDto[externalDomain=$externalDomain, loginPageMessage=$loginPageMessage, publicUsers=$publicUsers, sharedLinkDomain=$sharedLinkDomain]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'externalDomain'] = this.externalDomain;
|
||||
json[r'loginPageMessage'] = this.loginPageMessage;
|
||||
json[r'publicUsers'] = this.publicUsers;
|
||||
json[r'sharedLinkDomain'] = this.sharedLinkDomain;
|
||||
return json;
|
||||
}
|
||||
|
||||
|
|
@ -63,6 +70,7 @@ class SystemConfigServerDto {
|
|||
externalDomain: mapValueOfType<String>(json, r'externalDomain')!,
|
||||
loginPageMessage: mapValueOfType<String>(json, r'loginPageMessage')!,
|
||||
publicUsers: mapValueOfType<bool>(json, r'publicUsers')!,
|
||||
sharedLinkDomain: mapValueOfType<String>(json, r'sharedLinkDomain')!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
|
@ -113,6 +121,7 @@ class SystemConfigServerDto {
|
|||
'externalDomain',
|
||||
'loginPageMessage',
|
||||
'publicUsers',
|
||||
'sharedLinkDomain',
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22503,6 +22503,10 @@
|
|||
"description": "Whether public user registration is enabled",
|
||||
"type": "boolean"
|
||||
},
|
||||
"sharedLinkDomain": {
|
||||
"description": "Shared link domain URL",
|
||||
"type": "string"
|
||||
},
|
||||
"trashDays": {
|
||||
"description": "Number of days before trashed assets are permanently deleted",
|
||||
"maximum": 9007199254740991,
|
||||
|
|
@ -22527,6 +22531,7 @@
|
|||
"minFaces",
|
||||
"oauthButtonText",
|
||||
"publicUsers",
|
||||
"sharedLinkDomain",
|
||||
"trashDays",
|
||||
"userDeleteDelay"
|
||||
],
|
||||
|
|
@ -26412,12 +26417,17 @@
|
|||
"publicUsers": {
|
||||
"description": "Public users",
|
||||
"type": "boolean"
|
||||
},
|
||||
"sharedLinkDomain": {
|
||||
"description": "Shared link domain",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"externalDomain",
|
||||
"loginPageMessage",
|
||||
"publicUsers"
|
||||
"publicUsers",
|
||||
"sharedLinkDomain"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2013,6 +2013,8 @@ export type ServerConfigDto = {
|
|||
oauthButtonText: string;
|
||||
/** Whether public user registration is enabled */
|
||||
publicUsers: boolean;
|
||||
/** Shared link domain URL */
|
||||
sharedLinkDomain: string;
|
||||
/** Number of days before trashed assets are permanently deleted */
|
||||
trashDays: number;
|
||||
/** Delay in days before deleted users are permanently removed */
|
||||
|
|
@ -2581,6 +2583,8 @@ export type SystemConfigServerDto = {
|
|||
loginPageMessage: string;
|
||||
/** Public users */
|
||||
publicUsers: boolean;
|
||||
/** Shared link domain */
|
||||
sharedLinkDomain: string;
|
||||
};
|
||||
export type SystemConfigStorageTemplateDto = {
|
||||
/** Enabled */
|
||||
|
|
|
|||
|
|
@ -208,6 +208,7 @@ export type SystemConfig = {
|
|||
};
|
||||
server: {
|
||||
externalDomain: string;
|
||||
sharedLinkDomain: string;
|
||||
loginPageMessage: string;
|
||||
publicUsers: boolean;
|
||||
};
|
||||
|
|
@ -418,6 +419,7 @@ export const defaults = Object.freeze<SystemConfig>({
|
|||
},
|
||||
server: {
|
||||
externalDomain: '',
|
||||
sharedLinkDomain: '',
|
||||
loginPageMessage: '',
|
||||
publicUsers: true,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ const ServerConfigSchema = z
|
|||
isInitialized: z.boolean().describe('Whether the server has been initialized'),
|
||||
isOnboarded: z.boolean().describe('Whether the admin has completed onboarding'),
|
||||
externalDomain: z.string().describe('External domain URL'),
|
||||
sharedLinkDomain: z.string().describe('Shared link domain URL'),
|
||||
publicUsers: z.boolean().describe('Whether public user registration is enabled'),
|
||||
mapDarkStyleUrl: z.string().describe('Map dark style URL'),
|
||||
mapLightStyleUrl: z.string().describe('Map light style URL'),
|
||||
|
|
|
|||
|
|
@ -300,6 +300,12 @@ const SystemConfigServerSchema = z
|
|||
error: 'External domain must be an empty string or a valid URL',
|
||||
})
|
||||
.describe('External domain'),
|
||||
sharedLinkDomain: z
|
||||
.string()
|
||||
.refine((url) => url.length === 0 || z.url().safeParse(url).success, {
|
||||
error: 'Shared link domain must be an empty string or a valid URL',
|
||||
})
|
||||
.describe('Shared link domain'),
|
||||
loginPageMessage: z.string().describe('Login page message'),
|
||||
publicUsers: configBool.describe('Public users'),
|
||||
})
|
||||
|
|
|
|||
|
|
@ -164,6 +164,7 @@ describe(ServerService.name, () => {
|
|||
isInitialized: undefined,
|
||||
isOnboarded: false,
|
||||
externalDomain: '',
|
||||
sharedLinkDomain: '',
|
||||
publicUsers: true,
|
||||
mapDarkStyleUrl: 'https://tiles.immich.cloud/v1/style/dark.json',
|
||||
mapLightStyleUrl: 'https://tiles.immich.cloud/v1/style/light.json',
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ export class ServerService extends BaseService {
|
|||
isInitialized,
|
||||
isOnboarded: onboarding?.isOnboarded || false,
|
||||
externalDomain: config.server.externalDomain,
|
||||
sharedLinkDomain: config.server.sharedLinkDomain,
|
||||
publicUsers: config.server.publicUsers,
|
||||
mapDarkStyleUrl: config.map.darkStyle,
|
||||
mapLightStyleUrl: config.map.lightStyle,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import {
|
|||
} from 'src/dtos/shared-link.dto';
|
||||
import { Permission, SharedLinkType } from 'src/enum';
|
||||
import { BaseService } from 'src/services/base.service';
|
||||
import { getExternalDomain, OpenGraphTags } from 'src/utils/misc';
|
||||
import { getSharedLinkDomain, OpenGraphTags } from 'src/utils/misc';
|
||||
|
||||
@Injectable()
|
||||
export class SharedLinkService extends BaseService {
|
||||
|
|
@ -232,7 +232,7 @@ export class SharedLinkService extends BaseService {
|
|||
return {
|
||||
title: sharedLink.album ? sharedLink.album.albumName : 'Public Share',
|
||||
description: sharedLink.description || `${assetCount} shared photos & videos`,
|
||||
imageUrl: new URL(imagePath, getExternalDomain(config.server, defaultDomain)).href,
|
||||
imageUrl: new URL(imagePath, getSharedLinkDomain(config.server, defaultDomain)).href,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -181,6 +181,7 @@ const updatedConfig = Object.freeze<SystemConfig>({
|
|||
},
|
||||
server: {
|
||||
externalDomain: '',
|
||||
sharedLinkDomain: '',
|
||||
loginPageMessage: '',
|
||||
publicUsers: true,
|
||||
},
|
||||
|
|
@ -425,6 +426,28 @@ describe(SystemConfigService.name, () => {
|
|||
});
|
||||
}
|
||||
|
||||
const sharedLinkDomainTests = [
|
||||
{ should: 'with a trailing slash', sharedLinkDomain: 'https://demo.immich.app/' },
|
||||
{ should: 'without a trailing slash', sharedLinkDomain: 'https://demo.immich.app' },
|
||||
{ should: 'with a port', sharedLinkDomain: 'https://demo.immich.app:42', result: 'https://demo.immich.app:42' },
|
||||
{
|
||||
should: 'with basic auth',
|
||||
sharedLinkDomain: 'https://user:password@example.com:123',
|
||||
result: 'https://user:password@example.com:123',
|
||||
},
|
||||
];
|
||||
|
||||
for (const { should, sharedLinkDomain, result } of sharedLinkDomainTests) {
|
||||
it(`should normalize an external domain ${should}`, async () => {
|
||||
mocks.config.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' }));
|
||||
const partialConfig = { server: { sharedLinkDomain } };
|
||||
mocks.systemMetadata.readFile.mockResolvedValue(JSON.stringify(partialConfig));
|
||||
|
||||
const config = await sut.getSystemConfig();
|
||||
expect(config.server.sharedLinkDomain).toEqual(result ?? 'https://demo.immich.app');
|
||||
});
|
||||
}
|
||||
|
||||
it('should warn for unknown options in yaml', async () => {
|
||||
mocks.config.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.yaml' }));
|
||||
const partialConfig = `
|
||||
|
|
|
|||
|
|
@ -126,6 +126,17 @@ const buildConfig = async (repos: RepoDeps) => {
|
|||
config.server.externalDomain = externalDomain;
|
||||
}
|
||||
|
||||
if (config.server.sharedLinkDomain.length > 0) {
|
||||
const domain = new URL(config.server.sharedLinkDomain);
|
||||
|
||||
const sharedLinkDomain =
|
||||
domain.password && domain.username
|
||||
? `${domain.protocol}//${domain.username}:${domain.password}@${domain.host}`
|
||||
: domain.origin;
|
||||
|
||||
config.server.sharedLinkDomain = sharedLinkDomain;
|
||||
}
|
||||
|
||||
if (!config.ffmpeg.acceptedVideoCodecs.includes(config.ffmpeg.targetVideoCodec)) {
|
||||
config.ffmpeg.acceptedVideoCodecs.push(config.ffmpeg.targetVideoCodec);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,9 @@ export const getMethodNames = (instance: any) => {
|
|||
export const getExternalDomain = (server: SystemConfig['server'], defaultDomain = 'https://my.immich.app') =>
|
||||
server.externalDomain || defaultDomain;
|
||||
|
||||
export const getSharedLinkDomain = (server: SystemConfig['server'], defaultDomain = 'https://my.immich.app') =>
|
||||
server.sharedLinkDomain || defaultDomain;
|
||||
|
||||
/**
|
||||
* @returns a list of strings representing the keys of the object in dot notation
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { sharedLinkFactory } from '@test-data/factories/shared-link-factory';
|
|||
|
||||
vi.mock(import('$lib/managers/server-config-manager.svelte'), () => ({
|
||||
serverConfigManager: {
|
||||
value: { externalDomain: 'http://localhost:2283' } as ServerConfigDto,
|
||||
value: { sharedLinkDomain: 'http://localhost:2283' } as ServerConfigDto,
|
||||
init: vi.fn(),
|
||||
loadServerConfig: vi.fn(),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ export const getSharedLinkActions = ($t: MessageFormatter, sharedLink: SharedLin
|
|||
|
||||
export const asUrl = (sharedLink: SharedLinkResponseDto) => {
|
||||
const path = Route.viewSharedLink(sharedLink);
|
||||
return new URL(path, serverConfigManager.value.externalDomain || location.origin).href;
|
||||
return new URL(path, serverConfigManager.value.sharedLinkDomain || location.origin).href;
|
||||
};
|
||||
|
||||
export const handleCreateSharedLink = async (dto: SharedLinkCreateDto) => {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,14 @@
|
|||
isEdited={configToEdit.server.externalDomain !== config.server.externalDomain}
|
||||
/>
|
||||
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label={$t('admin.server_shared_link_domain_settings')}
|
||||
description={$t('admin.server_shared_link_domain_settings_description')}
|
||||
bind:value={configToEdit.server.sharedLinkDomain}
|
||||
isEdited={configToEdit.server.sharedLinkDomain !== config.server.sharedLinkDomain}
|
||||
/>
|
||||
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label={$t('admin.server_welcome_message')}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue