Skip to content

Commit

Permalink
feat(web): send test email button (immich-app#10011)
Browse files Browse the repository at this point in the history
* feat(web): test email button

* openapi

* UI button

* Show notification

* pr feedback

* remove jobs

* send email directly from repository and add feedback

* avoid sending many emails

* linter

* pr feedback

* lint

* lint

* lint
  • Loading branch information
alextran1502 authored Jun 7, 2024
1 parent d5f3d98 commit 9ac2ac2
Show file tree
Hide file tree
Showing 15 changed files with 380 additions and 17 deletions.
Binary file modified docs/static/img/ios-app-store-badge.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions mobile/openapi/README.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions mobile/openapi/lib/api.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 57 additions & 0 deletions mobile/openapi/lib/api/notifications_api.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 35 additions & 0 deletions open-api/immich-openapi-specs.json
Original file line number Diff line number Diff line change
Expand Up @@ -3466,6 +3466,41 @@
]
}
},
"/notifications/test-email": {
"post": {
"operationId": "sendTestEmail",
"parameters": [],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SystemConfigSmtpDto"
}
}
},
"required": true
},
"responses": {
"200": {
"description": ""
}
},
"security": [
{
"bearer": []
},
{
"cookie": []
},
{
"api_key": []
}
],
"tags": [
"Notifications"
]
}
},
"/oauth/authorize": {
"post": {
"operationId": "startOAuth",
Expand Down
35 changes: 22 additions & 13 deletions open-api/typescript-sdk/src/fetch-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,19 @@ export type MemoryUpdateDto = {
memoryAt?: string;
seenAt?: string;
};
export type SystemConfigSmtpTransportDto = {
host: string;
ignoreCert: boolean;
password: string;
port: number;
username: string;
};
export type SystemConfigSmtpDto = {
enabled: boolean;
"from": string;
replyTo: string;
transport: SystemConfigSmtpTransportDto;
};
export type OAuthConfigDto = {
redirectUri: string;
};
Expand Down Expand Up @@ -990,19 +1003,6 @@ export type SystemConfigMapDto = {
export type SystemConfigNewVersionCheckDto = {
enabled: boolean;
};
export type SystemConfigSmtpTransportDto = {
host: string;
ignoreCert: boolean;
password: string;
port: number;
username: string;
};
export type SystemConfigSmtpDto = {
enabled: boolean;
"from": string;
replyTo: string;
transport: SystemConfigSmtpTransportDto;
};
export type SystemConfigNotificationsDto = {
smtp: SystemConfigSmtpDto;
};
Expand Down Expand Up @@ -2022,6 +2022,15 @@ export function addMemoryAssets({ id, bulkIdsDto }: {
body: bulkIdsDto
})));
}
export function sendTestEmail({ systemConfigSmtpDto }: {
systemConfigSmtpDto: SystemConfigSmtpDto;
}, opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchText("/notifications/test-email", oazapfts.json({
...opts,
method: "POST",
body: systemConfigSmtpDto
})));
}
export function startOAuth({ oAuthConfigDto }: {
oAuthConfigDto: OAuthConfigDto;
}, opts?: Oazapfts.RequestOpts) {
Expand Down
2 changes: 2 additions & 0 deletions server/src/controllers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { JobController } from 'src/controllers/job.controller';
import { LibraryController } from 'src/controllers/library.controller';
import { MapController } from 'src/controllers/map.controller';
import { MemoryController } from 'src/controllers/memory.controller';
import { NotificationController } from 'src/controllers/notification.controller';
import { OAuthController } from 'src/controllers/oauth.controller';
import { PartnerController } from 'src/controllers/partner.controller';
import { PersonController } from 'src/controllers/person.controller';
Expand Down Expand Up @@ -46,6 +47,7 @@ export const controllers = [
LibraryController,
MapController,
MemoryController,
NotificationController,
OAuthController,
PartnerController,
PersonController,
Expand Down
19 changes: 19 additions & 0 deletions server/src/controllers/notification.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Body, Controller, HttpCode, Post } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { AuthDto } from 'src/dtos/auth.dto';
import { SystemConfigSmtpDto } from 'src/dtos/system-config.dto';
import { Auth, Authenticated } from 'src/middleware/auth.guard';
import { NotificationService } from 'src/services/notification.service';

@ApiTags('Notifications')
@Controller('notifications')
export class NotificationController {
constructor(private service: NotificationService) {}

@Post('test-email')
@HttpCode(200)
@Authenticated({ admin: true })
sendTestEmail(@Auth() auth: AuthDto, @Body() dto: SystemConfigSmtpDto) {
return this.service.sendTestEmail(auth.user.id, dto);
}
}
2 changes: 1 addition & 1 deletion server/src/dtos/system-config.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ class SystemConfigSmtpTransportDto {
password!: string;
}

class SystemConfigSmtpDto {
export class SystemConfigSmtpDto {
@IsBoolean()
enabled!: boolean;

Expand Down
134 changes: 134 additions & 0 deletions server/src/emails/test.email.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import {
Body,
Button,
Column,
Container,
Head,
Hr,
Html,
Img,
Link,
Preview,
Row,
Section,
Text,
} from '@react-email/components';
import * as CSS from 'csstype';
import * as React from 'react';
import { TestEmailProps } from 'src/interfaces/notification.interface';

export const TestEmail = ({ baseUrl, displayName }: TestEmailProps) => (
<Html>
<Head />
<Preview>This is a test email from Immich</Preview>
<Body
style={{
margin: 0,
padding: 0,
backgroundColor: '#ffffff',
color: 'rgb(66, 80, 175)',
fontFamily: 'Overpass, sans-serif',
fontSize: '18px',
lineHeight: '24px',
}}
>
<Container
style={{
width: '480px',
maxWidth: '100%',
padding: '10px',
margin: '0 auto',
}}
>
<Section
style={{
padding: '36px',
tableLayout: 'fixed',
backgroundColor: 'rgb(226, 232, 240)',
border: 'solid 0px rgb(248 113 113)',
borderRadius: '50px',
textAlign: 'center' as const,
}}
>
<Img
src="https://immich.app/img/immich-logo-inline-light.png"
alt="Immich"
style={{
height: 'auto',
margin: '0 auto 48px auto',
width: '50%',
alignSelf: 'center',
color: 'white',
}}
/>

<Text style={text}>
Hey <strong>{displayName}</strong>, this is the test email from your Immich Instance
</Text>

<Row>
<Link style={{ marginTop: '50px' }} href={baseUrl}>
{baseUrl}
</Link>
</Row>
</Section>

<Hr style={{ color: 'rgb(66, 80, 175)', marginTop: '24px' }} />

<Section style={{ textAlign: 'center' }}>
<Row>
<Column align="center">
<Link href="https://play.google.com/store/apps/details?id=app.alextran.immich">
<Img src={`https://immich.app/img/google-play-badge.png`} height="96px" alt="Immich" />
</Link>
<Link href="https://apps.apple.com/sg/app/immich/id1613945652">
<Img
src={`https://immich.app/img/ios-app-store-badge.png`}
alt="Immich"
style={{ height: '72px', padding: '14px' }}
/>
</Link>
</Column>
</Row>
</Section>

<Text
style={{
color: '#6a737d',
fontSize: '0.8rem',
textAlign: 'center' as const,
marginTop: '12px',
}}
>
<Link href="https://immich.app">Immich</Link> project is available under GNU AGPL v3 license.
</Text>
</Container>
</Body>
</Html>
);

TestEmail.PreviewProps = {
baseUrl: 'https://demo.immich.app/auth/login',
displayName: 'Alan Turing',
} as TestEmailProps;

export default TestEmail;

const text = {
margin: '0 0 24px 0',
textAlign: 'left' as const,
fontSize: '18px',
lineHeight: '24px',
};

const button: CSS.Properties = {
backgroundColor: 'rgb(66, 80, 175)',
margin: '1em 0',
padding: '0.75em 3em',
color: '#fff',
fontSize: '1em',
fontWeight: 700,
lineHeight: 1.5,
textTransform: 'uppercase',
borderRadius: '9999px',
};
10 changes: 10 additions & 0 deletions server/src/interfaces/notification.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export type SmtpOptions = {
};

export enum EmailTemplate {
TEST_EMAIL = 'test',

// AUTH
WELCOME = 'welcome',
RESET_PASSWORD = 'reset-password',
Expand All @@ -39,6 +41,10 @@ interface BaseEmailProps {
baseUrl: string;
}

export interface TestEmailProps extends BaseEmailProps {
displayName: string;
}

export interface WelcomeEmailProps extends BaseEmailProps {
displayName: string;
username: string;
Expand All @@ -61,6 +67,10 @@ export interface AlbumUpdateEmailProps extends BaseEmailProps {
}

export type EmailRenderRequest =
| {
template: EmailTemplate.TEST_EMAIL;
data: TestEmailProps;
}
| {
template: EmailTemplate.WELCOME;
data: WelcomeEmailProps;
Expand Down
Loading

0 comments on commit 9ac2ac2

Please sign in to comment.