refactor(notification): make username and password optional (smtp) (#126)
Some checks failed
Release Workflow / determine-release-type (push) Has been cancelled
Release Workflow / build-images (push) Has been cancelled
Release Workflow / publish-release (push) Has been cancelled

This commit is contained in:
Nico 2025-12-10 21:36:18 +01:00 committed by GitHub
parent ef8fd7228f
commit c3acc26e05
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 83 additions and 31 deletions

View file

@ -164,6 +164,11 @@ export type ListVolumesResponses = {
version: '3' | '4' | '4.1';
port?: number;
readOnly?: boolean;
} | {
backend: 'rclone';
path: string;
remote: string;
readOnly?: boolean;
} | {
backend: 'smb';
password: string;
@ -191,7 +196,7 @@ export type ListVolumesResponses = {
name: string;
shortId: string;
status: 'error' | 'mounted' | 'unmounted';
type: 'directory' | 'nfs' | 'smb' | 'webdav';
type: 'directory' | 'nfs' | 'rclone' | 'smb' | 'webdav';
updatedAt: number;
}>;
};
@ -211,6 +216,11 @@ export type CreateVolumeData = {
version: '3' | '4' | '4.1';
port?: number;
readOnly?: boolean;
} | {
backend: 'rclone';
path: string;
remote: string;
readOnly?: boolean;
} | {
backend: 'smb';
password: string;
@ -255,6 +265,11 @@ export type CreateVolumeResponses = {
version: '3' | '4' | '4.1';
port?: number;
readOnly?: boolean;
} | {
backend: 'rclone';
path: string;
remote: string;
readOnly?: boolean;
} | {
backend: 'smb';
password: string;
@ -282,7 +297,7 @@ export type CreateVolumeResponses = {
name: string;
shortId: string;
status: 'error' | 'mounted' | 'unmounted';
type: 'directory' | 'nfs' | 'smb' | 'webdav';
type: 'directory' | 'nfs' | 'rclone' | 'smb' | 'webdav';
updatedAt: number;
};
};
@ -302,6 +317,11 @@ export type TestConnectionData = {
version: '3' | '4' | '4.1';
port?: number;
readOnly?: boolean;
} | {
backend: 'rclone';
path: string;
remote: string;
readOnly?: boolean;
} | {
backend: 'smb';
password: string;
@ -399,6 +419,11 @@ export type GetVolumeResponses = {
version: '3' | '4' | '4.1';
port?: number;
readOnly?: boolean;
} | {
backend: 'rclone';
path: string;
remote: string;
readOnly?: boolean;
} | {
backend: 'smb';
password: string;
@ -426,7 +451,7 @@ export type GetVolumeResponses = {
name: string;
shortId: string;
status: 'error' | 'mounted' | 'unmounted';
type: 'directory' | 'nfs' | 'smb' | 'webdav';
type: 'directory' | 'nfs' | 'rclone' | 'smb' | 'webdav';
updatedAt: number;
};
};
@ -448,6 +473,11 @@ export type UpdateVolumeData = {
version: '3' | '4' | '4.1';
port?: number;
readOnly?: boolean;
} | {
backend: 'rclone';
path: string;
remote: string;
readOnly?: boolean;
} | {
backend: 'smb';
password: string;
@ -501,6 +531,11 @@ export type UpdateVolumeResponses = {
version: '3' | '4' | '4.1';
port?: number;
readOnly?: boolean;
} | {
backend: 'rclone';
path: string;
remote: string;
readOnly?: boolean;
} | {
backend: 'smb';
password: string;
@ -528,7 +563,7 @@ export type UpdateVolumeResponses = {
name: string;
shortId: string;
status: 'error' | 'mounted' | 'unmounted';
type: 'directory' | 'nfs' | 'smb' | 'webdav';
type: 'directory' | 'nfs' | 'rclone' | 'smb' | 'webdav';
updatedAt: number;
};
};
@ -1397,6 +1432,11 @@ export type ListBackupSchedulesResponses = {
version: '3' | '4' | '4.1';
port?: number;
readOnly?: boolean;
} | {
backend: 'rclone';
path: string;
remote: string;
readOnly?: boolean;
} | {
backend: 'smb';
password: string;
@ -1424,7 +1464,7 @@ export type ListBackupSchedulesResponses = {
name: string;
shortId: string;
status: 'error' | 'mounted' | 'unmounted';
type: 'directory' | 'nfs' | 'smb' | 'webdav';
type: 'directory' | 'nfs' | 'rclone' | 'smb' | 'webdav';
updatedAt: number;
};
volumeId: number;
@ -1636,6 +1676,11 @@ export type GetBackupScheduleResponses = {
version: '3' | '4' | '4.1';
port?: number;
readOnly?: boolean;
} | {
backend: 'rclone';
path: string;
remote: string;
readOnly?: boolean;
} | {
backend: 'smb';
password: string;
@ -1663,7 +1708,7 @@ export type GetBackupScheduleResponses = {
name: string;
shortId: string;
status: 'error' | 'mounted' | 'unmounted';
type: 'directory' | 'nfs' | 'smb' | 'webdav';
type: 'directory' | 'nfs' | 'rclone' | 'smb' | 'webdav';
updatedAt: number;
};
volumeId: number;
@ -1856,6 +1901,11 @@ export type GetBackupScheduleForVolumeResponses = {
version: '3' | '4' | '4.1';
port?: number;
readOnly?: boolean;
} | {
backend: 'rclone';
path: string;
remote: string;
readOnly?: boolean;
} | {
backend: 'smb';
password: string;
@ -1883,7 +1933,7 @@ export type GetBackupScheduleForVolumeResponses = {
name: string;
shortId: string;
status: 'error' | 'mounted' | 'unmounted';
type: 'directory' | 'nfs' | 'smb' | 'webdav';
type: 'directory' | 'nfs' | 'rclone' | 'smb' | 'webdav';
updatedAt: number;
};
volumeId: number;
@ -1987,13 +2037,13 @@ export type GetScheduleNotificationsResponses = {
type: 'telegram';
} | {
from: string;
password: string;
smtpHost: string;
smtpPort: number;
to: Array<string>;
type: 'email';
useTLS: boolean;
username: string;
password?: string;
username?: string;
} | {
priority: 'default' | 'high' | 'low' | 'max' | 'min';
topic: string;
@ -2077,13 +2127,13 @@ export type UpdateScheduleNotificationsResponses = {
type: 'telegram';
} | {
from: string;
password: string;
smtpHost: string;
smtpPort: number;
to: Array<string>;
type: 'email';
useTLS: boolean;
username: string;
password?: string;
username?: string;
} | {
priority: 'default' | 'high' | 'low' | 'max' | 'min';
topic: string;
@ -2380,13 +2430,13 @@ export type ListNotificationDestinationsResponses = {
type: 'telegram';
} | {
from: string;
password: string;
smtpHost: string;
smtpPort: number;
to: Array<string>;
type: 'email';
useTLS: boolean;
username: string;
password?: string;
username?: string;
} | {
priority: 'default' | 'high' | 'low' | 'max' | 'min';
topic: string;
@ -2441,13 +2491,13 @@ export type CreateNotificationDestinationData = {
type: 'telegram';
} | {
from: string;
password: string;
smtpHost: string;
smtpPort: number;
to: Array<string>;
type: 'email';
useTLS: boolean;
username: string;
password?: string;
username?: string;
} | {
priority: 'default' | 'high' | 'low' | 'max' | 'min';
topic: string;
@ -2501,13 +2551,13 @@ export type CreateNotificationDestinationResponses = {
type: 'telegram';
} | {
from: string;
password: string;
smtpHost: string;
smtpPort: number;
to: Array<string>;
type: 'email';
useTLS: boolean;
username: string;
password?: string;
username?: string;
} | {
priority: 'default' | 'high' | 'low' | 'max' | 'min';
topic: string;
@ -2608,13 +2658,13 @@ export type GetNotificationDestinationResponses = {
type: 'telegram';
} | {
from: string;
password: string;
smtpHost: string;
smtpPort: number;
to: Array<string>;
type: 'email';
useTLS: boolean;
username: string;
password?: string;
username?: string;
} | {
priority: 'default' | 'high' | 'low' | 'max' | 'min';
topic: string;
@ -2669,13 +2719,13 @@ export type UpdateNotificationDestinationData = {
type: 'telegram';
} | {
from: string;
password: string;
smtpHost: string;
smtpPort: number;
to: Array<string>;
type: 'email';
useTLS: boolean;
username: string;
password?: string;
username?: string;
} | {
priority: 'default' | 'high' | 'low' | 'max' | 'min';
topic: string;
@ -2739,13 +2789,13 @@ export type UpdateNotificationDestinationResponses = {
type: 'telegram';
} | {
from: string;
password: string;
smtpHost: string;
smtpPort: number;
to: Array<string>;
type: 'email';
useTLS: boolean;
username: string;
password?: string;
username?: string;
} | {
priority: 'default' | 'high' | 'low' | 'max' | 'min';
topic: string;

View file

@ -198,7 +198,7 @@ export const CreateNotificationForm = ({ onSubmit, mode = "create", initialValue
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormLabel>Username (Optional)</FormLabel>
<FormControl>
<Input {...field} placeholder="user@example.com" />
</FormControl>
@ -211,7 +211,7 @@ export const CreateNotificationForm = ({ onSubmit, mode = "create", initialValue
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormLabel>Password (Optional)</FormLabel>
<FormControl>
<Input {...field} type="password" placeholder="••••••••" />
</FormControl>

View file

@ -17,8 +17,8 @@ export const emailNotificationConfigSchema = type({
type: "'email'",
smtpHost: "string",
smtpPort: "1 <= number <= 65535",
username: "string",
password: "string",
username: "string?",
password: "string?",
from: "string",
to: "string[]",
useTLS: "boolean",

View file

@ -1,10 +1,12 @@
import type { NotificationConfig } from "~/schemas/notifications";
export function buildEmailShoutrrrUrl(config: Extract<NotificationConfig, { type: "email" }>): string {
const auth = `${encodeURIComponent(config.username)}:${encodeURIComponent(config.password)}`;
const auth = config.username && config.password
? `${encodeURIComponent(config.username)}:${encodeURIComponent(config.password)}@`
: "";
const host = `${config.smtpHost}:${config.smtpPort}`;
const toRecipients = config.to.map((email) => encodeURIComponent(email)).join(",");
const useStartTLS = config.useTLS ? "yes" : "no";
return `smtp://${auth}@${host}/?from=${encodeURIComponent(config.from)}&to=${toRecipients}&starttls=${useStartTLS}`;
return `smtp://${auth}${host}/?from=${encodeURIComponent(config.from)}&to=${toRecipients}&starttls=${useStartTLS}`;
}

View file

@ -38,7 +38,7 @@ async function encryptSensitiveFields(config: NotificationConfig): Promise<Notif
case "email":
return {
...config,
password: await cryptoUtils.encrypt(config.password),
password: config.password ? await cryptoUtils.encrypt(config.password) : undefined,
};
case "slack":
return {
@ -85,7 +85,7 @@ async function decryptSensitiveFields(config: NotificationConfig): Promise<Notif
case "email":
return {
...config,
password: await cryptoUtils.decrypt(config.password),
password: config.password ? await cryptoUtils.decrypt(config.password) : undefined,
};
case "slack":
return {