+
+## Notification templates
+
+You can override the default notification text with custom templates in HTML format. You can use tags to show dynamic tags in your templates.
+
+
diff --git a/docs/docs/administration/img/user-notifications-templates.png b/docs/docs/administration/img/user-notifications-templates.png
new file mode 100644
index 000000000..150d39b7a
Binary files /dev/null and b/docs/docs/administration/img/user-notifications-templates.png differ
diff --git a/docs/docs/administration/system-settings.md b/docs/docs/administration/system-settings.md
index 9f35ed101..59012c99b 100644
--- a/docs/docs/administration/system-settings.md
+++ b/docs/docs/administration/system-settings.md
@@ -157,6 +157,10 @@ Immich supports [Reverse Geocoding](/docs/features/reverse-geocoding) using data
SMTP server setup, for user creation notifications, new albums, etc. More information can be found [here](/docs/administration/email-notification)
+## Notification Templates
+
+Override the default notifications text with notification templates. More information can be found [here](/docs/administration/email-notification)
+
## Server Settings
### External Domain
diff --git a/i18n/en.json b/i18n/en.json
index 907f5df18..9741c10b2 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -252,6 +252,16 @@
"storage_template_user_label": "{label} is the user's Storage Label",
"system_settings": "System Settings",
"tag_cleanup_job": "Tag cleanup",
+ "template_email_preview": "Preview",
+ "template_email_settings": "Email Templates",
+ "template_email_settings_description": "Manage custom email notification templates",
+ "template_email_welcome": "Welcome email template",
+ "template_email_invite_album": "Invite Album Template",
+ "template_email_update_album": "Update Album Template",
+ "template_settings": "Notification Templates",
+ "template_settings_description": "Manage custom templates for notifications.",
+ "template_email_if_empty": "If the template is empty, the default email will be used.",
+ "template_email_available_tags": "You can use the following variables in your template: {tags}",
"theme_custom_css_settings": "Custom CSS",
"theme_custom_css_settings_description": "Cascading Style Sheets allow the design of Immich to be customized.",
"theme_settings": "Theme Settings",
@@ -1325,4 +1335,4 @@
"zoom_image": "Zoom Image",
"timeline": "Timeline",
"total": "Total"
-}
+}
\ No newline at end of file
diff --git a/i18n/nl.json b/i18n/nl.json
index ade7a5092..3420c5d10 100644
--- a/i18n/nl.json
+++ b/i18n/nl.json
@@ -247,6 +247,16 @@
"storage_template_user_label": "{label} is het opslaglabel van de gebruiker",
"system_settings": "Systeeminstellingen",
"tag_cleanup_job": "Tag opschoning",
+ "template_email_settings": "Email",
+ "template_email_settings_description": "Beheer aangepaste email melding sjablonen",
+ "template_email_preview": "Voorbeeld",
+ "template_email_welcome": "Welkom email sjabloon",
+ "template_email_invite_album": "Uitgenodigd in album sjabloon",
+ "template_email_update_album": "Update in album sjabloon",
+ "template_settings": "Melding sjablonen",
+ "template_settings_description": "Beheer aangepast sjablonen voor meldingen.",
+ "template_email_if_empty": "Wanneer het sjabloon leeg is, wordt de standaard mail gebruikt.",
+ "template_email_available_tags": "Je kan de volgende tags gebruiken in een template: {tags}",
"theme_custom_css_settings": "Aangepaste CSS",
"theme_custom_css_settings_description": "Met Cascading Style Sheets kan het ontwerp van Immich worden aangepast.",
"theme_settings": "Thema instellingen",
diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md
index 778093590..b97ff5411 100644
Binary files a/mobile/openapi/README.md and b/mobile/openapi/README.md differ
diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart
index e1c343ad5..73eb02d89 100644
Binary files a/mobile/openapi/lib/api.dart and b/mobile/openapi/lib/api.dart differ
diff --git a/mobile/openapi/lib/api/notifications_api.dart b/mobile/openapi/lib/api/notifications_api.dart
index 0681d5824..323fbcc3d 100644
Binary files a/mobile/openapi/lib/api/notifications_api.dart and b/mobile/openapi/lib/api/notifications_api.dart differ
diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart
index b71e6f45f..a6f8d551d 100644
Binary files a/mobile/openapi/lib/api_client.dart and b/mobile/openapi/lib/api_client.dart differ
diff --git a/mobile/openapi/lib/model/system_config_dto.dart b/mobile/openapi/lib/model/system_config_dto.dart
index 421595390..59d5f09fc 100644
Binary files a/mobile/openapi/lib/model/system_config_dto.dart and b/mobile/openapi/lib/model/system_config_dto.dart differ
diff --git a/mobile/openapi/lib/model/system_config_template_emails_dto.dart b/mobile/openapi/lib/model/system_config_template_emails_dto.dart
new file mode 100644
index 000000000..9db85509f
Binary files /dev/null and b/mobile/openapi/lib/model/system_config_template_emails_dto.dart differ
diff --git a/mobile/openapi/lib/model/system_config_templates_dto.dart b/mobile/openapi/lib/model/system_config_templates_dto.dart
new file mode 100644
index 000000000..a5e883497
Binary files /dev/null and b/mobile/openapi/lib/model/system_config_templates_dto.dart differ
diff --git a/mobile/openapi/lib/model/template_dto.dart b/mobile/openapi/lib/model/template_dto.dart
new file mode 100644
index 000000000..f818e0508
Binary files /dev/null and b/mobile/openapi/lib/model/template_dto.dart differ
diff --git a/mobile/openapi/lib/model/template_response_dto.dart b/mobile/openapi/lib/model/template_response_dto.dart
new file mode 100644
index 000000000..3c3224a54
Binary files /dev/null and b/mobile/openapi/lib/model/template_response_dto.dart differ
diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json
index bc32a32e0..43985cae8 100644
--- a/open-api/immich-openapi-specs.json
+++ b/open-api/immich-openapi-specs.json
@@ -3430,6 +3430,57 @@
]
}
},
+ "/notifications/templates/{name}": {
+ "post": {
+ "operationId": "getNotificationTemplate",
+ "parameters": [
+ {
+ "name": "name",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/TemplateDto"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/TemplateResponseDto"
+ }
+ }
+ },
+ "description": ""
+ }
+ },
+ "security": [
+ {
+ "bearer": []
+ },
+ {
+ "cookie": []
+ },
+ {
+ "api_key": []
+ }
+ ],
+ "tags": [
+ "Notifications"
+ ]
+ }
+ },
"/notifications/test-email": {
"post": {
"operationId": "sendTestEmail",
@@ -11538,6 +11589,9 @@
"storageTemplate": {
"$ref": "#/components/schemas/SystemConfigStorageTemplateDto"
},
+ "templates": {
+ "$ref": "#/components/schemas/SystemConfigTemplatesDto"
+ },
"theme": {
"$ref": "#/components/schemas/SystemConfigThemeDto"
},
@@ -11565,6 +11619,7 @@
"reverseGeocoding",
"server",
"storageTemplate",
+ "templates",
"theme",
"trash",
"user"
@@ -12111,6 +12166,25 @@
],
"type": "object"
},
+ "SystemConfigTemplateEmailsDto": {
+ "properties": {
+ "albumInviteTemplate": {
+ "type": "string"
+ },
+ "albumUpdateTemplate": {
+ "type": "string"
+ },
+ "welcomeTemplate": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "albumInviteTemplate",
+ "albumUpdateTemplate",
+ "welcomeTemplate"
+ ],
+ "type": "object"
+ },
"SystemConfigTemplateStorageOptionDto": {
"properties": {
"dayOptions": {
@@ -12174,6 +12248,17 @@
],
"type": "object"
},
+ "SystemConfigTemplatesDto": {
+ "properties": {
+ "email": {
+ "$ref": "#/components/schemas/SystemConfigTemplateEmailsDto"
+ }
+ },
+ "required": [
+ "email"
+ ],
+ "type": "object"
+ },
"SystemConfigThemeDto": {
"properties": {
"customCss": {
@@ -12352,6 +12437,32 @@
},
"type": "object"
},
+ "TemplateDto": {
+ "properties": {
+ "template": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "template"
+ ],
+ "type": "object"
+ },
+ "TemplateResponseDto": {
+ "properties": {
+ "html": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "html",
+ "name"
+ ],
+ "type": "object"
+ },
"TestEmailResponseDto": {
"properties": {
"messageId": {
diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts
index d786139ab..20d0c5715 100644
--- a/open-api/typescript-sdk/src/fetch-client.ts
+++ b/open-api/typescript-sdk/src/fetch-client.ts
@@ -634,6 +634,13 @@ export type MemoryUpdateDto = {
memoryAt?: string;
seenAt?: string;
};
+export type TemplateDto = {
+ template: string;
+};
+export type TemplateResponseDto = {
+ html: string;
+ name: string;
+};
export type SystemConfigSmtpTransportDto = {
host: string;
ignoreCert: boolean;
@@ -1232,6 +1239,14 @@ export type SystemConfigStorageTemplateDto = {
hashVerificationEnabled: boolean;
template: string;
};
+export type SystemConfigTemplateEmailsDto = {
+ albumInviteTemplate: string;
+ albumUpdateTemplate: string;
+ welcomeTemplate: string;
+};
+export type SystemConfigTemplatesDto = {
+ email: SystemConfigTemplateEmailsDto;
+};
export type SystemConfigThemeDto = {
customCss: string;
};
@@ -1259,6 +1274,7 @@ export type SystemConfigDto = {
reverseGeocoding: SystemConfigReverseGeocodingDto;
server: SystemConfigServerDto;
storageTemplate: SystemConfigStorageTemplateDto;
+ templates: SystemConfigTemplatesDto;
theme: SystemConfigThemeDto;
trash: SystemConfigTrashDto;
user: SystemConfigUserDto;
@@ -2227,6 +2243,19 @@ export function addMemoryAssets({ id, bulkIdsDto }: {
body: bulkIdsDto
})));
}
+export function getNotificationTemplate({ name, templateDto }: {
+ name: string;
+ templateDto: TemplateDto;
+}, opts?: Oazapfts.RequestOpts) {
+ return oazapfts.ok(oazapfts.fetchJson<{
+ status: 200;
+ data: TemplateResponseDto;
+ }>(`/notifications/templates/${encodeURIComponent(name)}`, oazapfts.json({
+ ...opts,
+ method: "POST",
+ body: templateDto
+ })));
+}
export function sendTestEmail({ systemConfigSmtpDto }: {
systemConfigSmtpDto: SystemConfigSmtpDto;
}, opts?: Oazapfts.RequestOpts) {
diff --git a/server/src/config.ts b/server/src/config.ts
index dd850e063..265897420 100644
--- a/server/src/config.ts
+++ b/server/src/config.ts
@@ -146,6 +146,13 @@ export interface SystemConfig {
};
};
};
+ templates: {
+ email: {
+ welcomeTemplate: string;
+ albumInviteTemplate: string;
+ albumUpdateTemplate: string;
+ };
+ };
server: {
externalDomain: string;
loginPageMessage: string;
@@ -313,6 +320,13 @@ export const defaults = Object.freeze