mirror of
https://github.com/trailbaseio/trailbase.git
synced 2026-01-07 10:20:22 -06:00
Allow configuring SMTP encryption type: None, TLS, STARTTLS. #122
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-ts_proto v2.7.0
|
||||
// protoc-gen-ts_proto v2.7.7
|
||||
// protoc v3.21.12
|
||||
// source: config.proto
|
||||
|
||||
@@ -9,6 +9,51 @@ import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
||||
|
||||
export const protobufPackage = "config";
|
||||
|
||||
export enum SmtpEncryption {
|
||||
SMTP_ENCRYPTION_UNDEFINED = 0,
|
||||
SMTP_ENCRYPTION_NONE = 1,
|
||||
SMTP_ENCRYPTION_STARTTLS = 2,
|
||||
SMTP_ENCRYPTION_TLS = 3,
|
||||
UNRECOGNIZED = -1,
|
||||
}
|
||||
|
||||
export function smtpEncryptionFromJSON(object: any): SmtpEncryption {
|
||||
switch (object) {
|
||||
case 0:
|
||||
case "SMTP_ENCRYPTION_UNDEFINED":
|
||||
return SmtpEncryption.SMTP_ENCRYPTION_UNDEFINED;
|
||||
case 1:
|
||||
case "SMTP_ENCRYPTION_NONE":
|
||||
return SmtpEncryption.SMTP_ENCRYPTION_NONE;
|
||||
case 2:
|
||||
case "SMTP_ENCRYPTION_STARTTLS":
|
||||
return SmtpEncryption.SMTP_ENCRYPTION_STARTTLS;
|
||||
case 3:
|
||||
case "SMTP_ENCRYPTION_TLS":
|
||||
return SmtpEncryption.SMTP_ENCRYPTION_TLS;
|
||||
case -1:
|
||||
case "UNRECOGNIZED":
|
||||
default:
|
||||
return SmtpEncryption.UNRECOGNIZED;
|
||||
}
|
||||
}
|
||||
|
||||
export function smtpEncryptionToJSON(object: SmtpEncryption): string {
|
||||
switch (object) {
|
||||
case SmtpEncryption.SMTP_ENCRYPTION_UNDEFINED:
|
||||
return "SMTP_ENCRYPTION_UNDEFINED";
|
||||
case SmtpEncryption.SMTP_ENCRYPTION_NONE:
|
||||
return "SMTP_ENCRYPTION_NONE";
|
||||
case SmtpEncryption.SMTP_ENCRYPTION_STARTTLS:
|
||||
return "SMTP_ENCRYPTION_STARTTLS";
|
||||
case SmtpEncryption.SMTP_ENCRYPTION_TLS:
|
||||
return "SMTP_ENCRYPTION_TLS";
|
||||
case SmtpEncryption.UNRECOGNIZED:
|
||||
default:
|
||||
return "UNRECOGNIZED";
|
||||
}
|
||||
}
|
||||
|
||||
export enum OAuthProviderId {
|
||||
OAUTH_PROVIDER_ID_UNDEFINED = 0,
|
||||
TEST = 1,
|
||||
@@ -285,7 +330,11 @@ export interface EmailConfig {
|
||||
smtpHost?: string | undefined;
|
||||
smtpPort?: number | undefined;
|
||||
smtpUsername?: string | undefined;
|
||||
smtpPassword?: string | undefined;
|
||||
smtpPassword?:
|
||||
| string
|
||||
| undefined;
|
||||
/** Which encryption method to use. STARTTLS by default. */
|
||||
smtpEncryption?: SmtpEncryption | undefined;
|
||||
senderName?: string | undefined;
|
||||
senderAddress?: string | undefined;
|
||||
userVerificationTemplate?: EmailTemplate | undefined;
|
||||
@@ -340,6 +389,13 @@ export interface AuthConfig {
|
||||
| undefined;
|
||||
/** / Map of configured OAuth providers. */
|
||||
oauthProviders: { [key: string]: OAuthProviderConfig };
|
||||
/**
|
||||
* / List of custom URI schemes allowed as auth redirects.
|
||||
* /
|
||||
* / This is useful for mobile apps, desktop or SPAs where an app registers a
|
||||
* / custom scheme for calls it wants to handle.
|
||||
*/
|
||||
customUriSchemes: string[];
|
||||
}
|
||||
|
||||
export interface AuthConfig_OauthProvidersEntry {
|
||||
@@ -385,7 +441,11 @@ export interface ServerConfig {
|
||||
| number
|
||||
| undefined;
|
||||
/** / If present will use S3 setup over local file-system based storage. */
|
||||
s3StorageConfig?: S3StorageConfig | undefined;
|
||||
s3StorageConfig?:
|
||||
| S3StorageConfig
|
||||
| undefined;
|
||||
/** / If enabled, batches of transactions can be submitted for attomic execution */
|
||||
enableRecordTransactions?: boolean | undefined;
|
||||
}
|
||||
|
||||
export interface SystemJob {
|
||||
@@ -393,7 +453,10 @@ export interface SystemJob {
|
||||
id?:
|
||||
| SystemJobId
|
||||
| undefined;
|
||||
/** / Cron spec: shorthand or 7-components: (sec, min, hour, day of month, / month, day of week, year). */
|
||||
/**
|
||||
* / Cron spec: shorthand or 7-components: (sec, min, hour, day of month, /
|
||||
* / month, day of week, year).
|
||||
*/
|
||||
schedule?:
|
||||
| string
|
||||
| undefined;
|
||||
@@ -468,7 +531,8 @@ export interface RecordApiConfig {
|
||||
* / matches the current authenticated user's id. One can also construct
|
||||
* / arbitrary validations including sub-queries, e.g.:
|
||||
* /
|
||||
* / _USER_.id = _REQ_.owner AND EXISTS(SELECT FROM allowed WHERE allowed.user = _USER_.id)
|
||||
* / _USER_.id = _REQ_.owner AND EXISTS(SELECT FROM allowed WHERE
|
||||
* / allowed.user = _USER_.id)
|
||||
*/
|
||||
createAccessRule?: string | undefined;
|
||||
readAccessRule?: string | undefined;
|
||||
@@ -486,6 +550,8 @@ export interface RecordApiConfig {
|
||||
* / allowed to be expanded.
|
||||
*/
|
||||
expand: string[];
|
||||
/** / Hard limit for listing records (default: 1024). */
|
||||
listingHardLimit?: number | undefined;
|
||||
}
|
||||
|
||||
export interface JsonSchemaConfig {
|
||||
@@ -524,7 +590,7 @@ export const EmailTemplate: MessageFns<EmailTemplate> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): EmailTemplate {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseEmailTemplate();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -601,6 +667,9 @@ export const EmailConfig: MessageFns<EmailConfig> = {
|
||||
if (message.smtpPassword !== undefined && message.smtpPassword !== "") {
|
||||
writer.uint32(34).string(message.smtpPassword);
|
||||
}
|
||||
if (message.smtpEncryption !== undefined && message.smtpEncryption !== 0) {
|
||||
writer.uint32(40).int32(message.smtpEncryption);
|
||||
}
|
||||
if (message.senderName !== undefined && message.senderName !== "") {
|
||||
writer.uint32(90).string(message.senderName);
|
||||
}
|
||||
@@ -621,7 +690,7 @@ export const EmailConfig: MessageFns<EmailConfig> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): EmailConfig {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseEmailConfig();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -658,6 +727,14 @@ export const EmailConfig: MessageFns<EmailConfig> = {
|
||||
message.smtpPassword = reader.string();
|
||||
continue;
|
||||
}
|
||||
case 5: {
|
||||
if (tag !== 40) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.smtpEncryption = reader.int32() as any;
|
||||
continue;
|
||||
}
|
||||
case 11: {
|
||||
if (tag !== 90) {
|
||||
break;
|
||||
@@ -713,6 +790,7 @@ export const EmailConfig: MessageFns<EmailConfig> = {
|
||||
smtpPort: isSet(object.smtpPort) ? globalThis.Number(object.smtpPort) : undefined,
|
||||
smtpUsername: isSet(object.smtpUsername) ? globalThis.String(object.smtpUsername) : undefined,
|
||||
smtpPassword: isSet(object.smtpPassword) ? globalThis.String(object.smtpPassword) : undefined,
|
||||
smtpEncryption: isSet(object.smtpEncryption) ? smtpEncryptionFromJSON(object.smtpEncryption) : undefined,
|
||||
senderName: isSet(object.senderName) ? globalThis.String(object.senderName) : undefined,
|
||||
senderAddress: isSet(object.senderAddress) ? globalThis.String(object.senderAddress) : undefined,
|
||||
userVerificationTemplate: isSet(object.userVerificationTemplate)
|
||||
@@ -741,6 +819,9 @@ export const EmailConfig: MessageFns<EmailConfig> = {
|
||||
if (message.smtpPassword !== undefined && message.smtpPassword !== "") {
|
||||
obj.smtpPassword = message.smtpPassword;
|
||||
}
|
||||
if (message.smtpEncryption !== undefined && message.smtpEncryption !== 0) {
|
||||
obj.smtpEncryption = smtpEncryptionToJSON(message.smtpEncryption);
|
||||
}
|
||||
if (message.senderName !== undefined && message.senderName !== "") {
|
||||
obj.senderName = message.senderName;
|
||||
}
|
||||
@@ -768,6 +849,7 @@ export const EmailConfig: MessageFns<EmailConfig> = {
|
||||
message.smtpPort = object.smtpPort ?? 0;
|
||||
message.smtpUsername = object.smtpUsername ?? "";
|
||||
message.smtpPassword = object.smtpPassword ?? "";
|
||||
message.smtpEncryption = object.smtpEncryption ?? 0;
|
||||
message.senderName = object.senderName ?? "";
|
||||
message.senderAddress = object.senderAddress ?? "";
|
||||
message.userVerificationTemplate =
|
||||
@@ -817,7 +899,7 @@ export const OAuthProviderConfig: MessageFns<OAuthProviderConfig> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): OAuthProviderConfig {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseOAuthProviderConfig();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -942,7 +1024,7 @@ export const OAuthProviderConfig: MessageFns<OAuthProviderConfig> = {
|
||||
};
|
||||
|
||||
function createBaseAuthConfig(): AuthConfig {
|
||||
return { oauthProviders: {} };
|
||||
return { oauthProviders: {}, customUriSchemes: [] };
|
||||
}
|
||||
|
||||
export const AuthConfig: MessageFns<AuthConfig> = {
|
||||
@@ -977,12 +1059,15 @@ export const AuthConfig: MessageFns<AuthConfig> = {
|
||||
Object.entries(message.oauthProviders).forEach(([key, value]) => {
|
||||
AuthConfig_OauthProvidersEntry.encode({ key: key as any, value }, writer.uint32(90).fork()).join();
|
||||
});
|
||||
for (const v of message.customUriSchemes) {
|
||||
writer.uint32(170).string(v!);
|
||||
}
|
||||
return writer;
|
||||
},
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): AuthConfig {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseAuthConfig();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -1054,6 +1139,14 @@ export const AuthConfig: MessageFns<AuthConfig> = {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
case 21: {
|
||||
if (tag !== 170) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.customUriSchemes.push(reader.string());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break;
|
||||
@@ -1088,6 +1181,9 @@ export const AuthConfig: MessageFns<AuthConfig> = {
|
||||
return acc;
|
||||
}, {})
|
||||
: {},
|
||||
customUriSchemes: globalThis.Array.isArray(object?.customUriSchemes)
|
||||
? object.customUriSchemes.map((e: any) => globalThis.String(e))
|
||||
: [],
|
||||
};
|
||||
},
|
||||
|
||||
@@ -1129,6 +1225,9 @@ export const AuthConfig: MessageFns<AuthConfig> = {
|
||||
});
|
||||
}
|
||||
}
|
||||
if (message.customUriSchemes?.length) {
|
||||
obj.customUriSchemes = message.customUriSchemes;
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
|
||||
@@ -1153,6 +1252,7 @@ export const AuthConfig: MessageFns<AuthConfig> = {
|
||||
},
|
||||
{},
|
||||
);
|
||||
message.customUriSchemes = object.customUriSchemes?.map((e) => e) || [];
|
||||
return message;
|
||||
},
|
||||
};
|
||||
@@ -1174,7 +1274,7 @@ export const AuthConfig_OauthProvidersEntry: MessageFns<AuthConfig_OauthProvider
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): AuthConfig_OauthProvidersEntry {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseAuthConfig_OauthProvidersEntry();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -1263,7 +1363,7 @@ export const S3StorageConfig: MessageFns<S3StorageConfig> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): S3StorageConfig {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseS3StorageConfig();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -1379,12 +1479,15 @@ export const ServerConfig: MessageFns<ServerConfig> = {
|
||||
if (message.s3StorageConfig !== undefined) {
|
||||
S3StorageConfig.encode(message.s3StorageConfig, writer.uint32(106).fork()).join();
|
||||
}
|
||||
if (message.enableRecordTransactions !== undefined && message.enableRecordTransactions !== false) {
|
||||
writer.uint32(112).bool(message.enableRecordTransactions);
|
||||
}
|
||||
return writer;
|
||||
},
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): ServerConfig {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseServerConfig();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -1421,6 +1524,14 @@ export const ServerConfig: MessageFns<ServerConfig> = {
|
||||
message.s3StorageConfig = S3StorageConfig.decode(reader, reader.uint32());
|
||||
continue;
|
||||
}
|
||||
case 14: {
|
||||
if (tag !== 112) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.enableRecordTransactions = reader.bool();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break;
|
||||
@@ -1436,6 +1547,9 @@ export const ServerConfig: MessageFns<ServerConfig> = {
|
||||
siteUrl: isSet(object.siteUrl) ? globalThis.String(object.siteUrl) : undefined,
|
||||
logsRetentionSec: isSet(object.logsRetentionSec) ? globalThis.Number(object.logsRetentionSec) : undefined,
|
||||
s3StorageConfig: isSet(object.s3StorageConfig) ? S3StorageConfig.fromJSON(object.s3StorageConfig) : undefined,
|
||||
enableRecordTransactions: isSet(object.enableRecordTransactions)
|
||||
? globalThis.Boolean(object.enableRecordTransactions)
|
||||
: undefined,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -1453,6 +1567,9 @@ export const ServerConfig: MessageFns<ServerConfig> = {
|
||||
if (message.s3StorageConfig !== undefined) {
|
||||
obj.s3StorageConfig = S3StorageConfig.toJSON(message.s3StorageConfig);
|
||||
}
|
||||
if (message.enableRecordTransactions !== undefined && message.enableRecordTransactions !== false) {
|
||||
obj.enableRecordTransactions = message.enableRecordTransactions;
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
|
||||
@@ -1467,6 +1584,7 @@ export const ServerConfig: MessageFns<ServerConfig> = {
|
||||
message.s3StorageConfig = (object.s3StorageConfig !== undefined && object.s3StorageConfig !== null)
|
||||
? S3StorageConfig.fromPartial(object.s3StorageConfig)
|
||||
: undefined;
|
||||
message.enableRecordTransactions = object.enableRecordTransactions ?? false;
|
||||
return message;
|
||||
},
|
||||
};
|
||||
@@ -1491,7 +1609,7 @@ export const SystemJob: MessageFns<SystemJob> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): SystemJob {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseSystemJob();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -1577,7 +1695,7 @@ export const JobsConfig: MessageFns<JobsConfig> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): JobsConfig {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseJobsConfig();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -1646,16 +1764,12 @@ export const RecordApiConfig: MessageFns<RecordApiConfig> = {
|
||||
if (message.enableSubscriptions !== undefined && message.enableSubscriptions !== false) {
|
||||
writer.uint32(72).bool(message.enableSubscriptions);
|
||||
}
|
||||
writer.uint32(58).fork();
|
||||
for (const v of message.aclWorld) {
|
||||
writer.int32(v);
|
||||
writer.uint32(56).int32(v!);
|
||||
}
|
||||
writer.join();
|
||||
writer.uint32(66).fork();
|
||||
for (const v of message.aclAuthenticated) {
|
||||
writer.int32(v);
|
||||
writer.uint32(64).int32(v!);
|
||||
}
|
||||
writer.join();
|
||||
for (const v of message.excludedColumns) {
|
||||
writer.uint32(82).string(v!);
|
||||
}
|
||||
@@ -1677,12 +1791,15 @@ export const RecordApiConfig: MessageFns<RecordApiConfig> = {
|
||||
for (const v of message.expand) {
|
||||
writer.uint32(170).string(v!);
|
||||
}
|
||||
if (message.listingHardLimit !== undefined && message.listingHardLimit !== 0) {
|
||||
writer.uint32(176).uint64(message.listingHardLimit);
|
||||
}
|
||||
return writer;
|
||||
},
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): RecordApiConfig {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseRecordApiConfig();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -1819,6 +1936,14 @@ export const RecordApiConfig: MessageFns<RecordApiConfig> = {
|
||||
message.expand.push(reader.string());
|
||||
continue;
|
||||
}
|
||||
case 22: {
|
||||
if (tag !== 176) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.listingHardLimit = longToNumber(reader.uint64());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break;
|
||||
@@ -1856,6 +1981,7 @@ export const RecordApiConfig: MessageFns<RecordApiConfig> = {
|
||||
deleteAccessRule: isSet(object.deleteAccessRule) ? globalThis.String(object.deleteAccessRule) : undefined,
|
||||
schemaAccessRule: isSet(object.schemaAccessRule) ? globalThis.String(object.schemaAccessRule) : undefined,
|
||||
expand: globalThis.Array.isArray(object?.expand) ? object.expand.map((e: any) => globalThis.String(e)) : [],
|
||||
listingHardLimit: isSet(object.listingHardLimit) ? globalThis.Number(object.listingHardLimit) : undefined,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -1903,6 +2029,9 @@ export const RecordApiConfig: MessageFns<RecordApiConfig> = {
|
||||
if (message.expand?.length) {
|
||||
obj.expand = message.expand;
|
||||
}
|
||||
if (message.listingHardLimit !== undefined && message.listingHardLimit !== 0) {
|
||||
obj.listingHardLimit = Math.round(message.listingHardLimit);
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
|
||||
@@ -1925,6 +2054,7 @@ export const RecordApiConfig: MessageFns<RecordApiConfig> = {
|
||||
message.deleteAccessRule = object.deleteAccessRule ?? "";
|
||||
message.schemaAccessRule = object.schemaAccessRule ?? "";
|
||||
message.expand = object.expand?.map((e) => e) || [];
|
||||
message.listingHardLimit = object.listingHardLimit ?? 0;
|
||||
return message;
|
||||
},
|
||||
};
|
||||
@@ -1946,7 +2076,7 @@ export const JsonSchemaConfig: MessageFns<JsonSchemaConfig> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): JsonSchemaConfig {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseJsonSchemaConfig();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -2034,7 +2164,7 @@ export const Config: MessageFns<Config> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): Config {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseConfig();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-ts_proto v2.7.0
|
||||
// protoc-gen-ts_proto v2.7.7
|
||||
// protoc v3.21.12
|
||||
// source: config_api.proto
|
||||
|
||||
@@ -37,7 +37,7 @@ export const GetConfigResponse: MessageFns<GetConfigResponse> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): GetConfigResponse {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseGetConfigResponse();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -115,7 +115,7 @@ export const UpdateConfigRequest: MessageFns<UpdateConfigRequest> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): UpdateConfigRequest {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseUpdateConfigRequest();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-ts_proto v2.7.0
|
||||
// protoc-gen-ts_proto v2.7.7
|
||||
// protoc v3.21.12
|
||||
// source: google/protobuf/descriptor.proto
|
||||
|
||||
@@ -1251,7 +1251,7 @@ export const FileDescriptorSet: MessageFns<FileDescriptorSet> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): FileDescriptorSet {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseFileDescriptorSet();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -1320,16 +1320,12 @@ export const FileDescriptorProto: MessageFns<FileDescriptorProto> = {
|
||||
for (const v of message.dependency) {
|
||||
writer.uint32(26).string(v!);
|
||||
}
|
||||
writer.uint32(82).fork();
|
||||
for (const v of message.publicDependency) {
|
||||
writer.int32(v);
|
||||
writer.uint32(80).int32(v!);
|
||||
}
|
||||
writer.join();
|
||||
writer.uint32(90).fork();
|
||||
for (const v of message.weakDependency) {
|
||||
writer.int32(v);
|
||||
writer.uint32(88).int32(v!);
|
||||
}
|
||||
writer.join();
|
||||
for (const v of message.messageType) {
|
||||
DescriptorProto.encode(v!, writer.uint32(34).fork()).join();
|
||||
}
|
||||
@@ -1356,7 +1352,7 @@ export const FileDescriptorProto: MessageFns<FileDescriptorProto> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): FileDescriptorProto {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseFileDescriptorProto();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -1633,7 +1629,7 @@ export const DescriptorProto: MessageFns<DescriptorProto> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): DescriptorProto {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseDescriptorProto();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -1834,7 +1830,7 @@ export const DescriptorProto_ExtensionRange: MessageFns<DescriptorProto_Extensio
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): DescriptorProto_ExtensionRange {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseDescriptorProto_ExtensionRange();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -1927,7 +1923,7 @@ export const DescriptorProto_ReservedRange: MessageFns<DescriptorProto_ReservedR
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): DescriptorProto_ReservedRange {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseDescriptorProto_ReservedRange();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -2002,7 +1998,7 @@ export const ExtensionRangeOptions: MessageFns<ExtensionRangeOptions> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): ExtensionRangeOptions {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseExtensionRangeOptions();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -2094,7 +2090,7 @@ export const FieldDescriptorProto: MessageFns<FieldDescriptorProto> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): FieldDescriptorProto {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseFieldDescriptorProto();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -2289,7 +2285,7 @@ export const OneofDescriptorProto: MessageFns<OneofDescriptorProto> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): OneofDescriptorProto {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseOneofDescriptorProto();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -2376,7 +2372,7 @@ export const EnumDescriptorProto: MessageFns<EnumDescriptorProto> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): EnumDescriptorProto {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseEnumDescriptorProto();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -2500,7 +2496,7 @@ export const EnumDescriptorProto_EnumReservedRange: MessageFns<EnumDescriptorPro
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): EnumDescriptorProto_EnumReservedRange {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseEnumDescriptorProto_EnumReservedRange();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -2583,7 +2579,7 @@ export const EnumValueDescriptorProto: MessageFns<EnumValueDescriptorProto> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): EnumValueDescriptorProto {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseEnumValueDescriptorProto();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -2677,7 +2673,7 @@ export const ServiceDescriptorProto: MessageFns<ServiceDescriptorProto> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): ServiceDescriptorProto {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseServiceDescriptorProto();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -2782,7 +2778,7 @@ export const MethodDescriptorProto: MessageFns<MethodDescriptorProto> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): MethodDescriptorProto {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseMethodDescriptorProto();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -2969,7 +2965,7 @@ export const FileOptions: MessageFns<FileOptions> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): FileOptions {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseFileOptions();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -3311,7 +3307,7 @@ export const MessageOptions: MessageFns<MessageOptions> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): MessageOptions {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseMessageOptions();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -3450,7 +3446,7 @@ export const FieldOptions: MessageFns<FieldOptions> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): FieldOptions {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseFieldOptions();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -3603,7 +3599,7 @@ export const OneofOptions: MessageFns<OneofOptions> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): OneofOptions {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseOneofOptions();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -3671,7 +3667,7 @@ export const EnumOptions: MessageFns<EnumOptions> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): EnumOptions {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseEnumOptions();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -3762,7 +3758,7 @@ export const EnumValueOptions: MessageFns<EnumValueOptions> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): EnumValueOptions {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseEnumValueOptions();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -3840,7 +3836,7 @@ export const ServiceOptions: MessageFns<ServiceOptions> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): ServiceOptions {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseServiceOptions();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -3921,7 +3917,7 @@ export const MethodOptions: MessageFns<MethodOptions> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): MethodOptions {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseMethodOptions();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -4029,7 +4025,7 @@ export const UninterpretedOption: MessageFns<UninterpretedOption> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): UninterpretedOption {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseUninterpretedOption();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -4172,7 +4168,7 @@ export const UninterpretedOption_NamePart: MessageFns<UninterpretedOption_NamePa
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): UninterpretedOption_NamePart {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseUninterpretedOption_NamePart();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -4245,7 +4241,7 @@ export const SourceCodeInfo: MessageFns<SourceCodeInfo> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): SourceCodeInfo {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseSourceCodeInfo();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -4323,7 +4319,7 @@ export const SourceCodeInfo_Location: MessageFns<SourceCodeInfo_Location> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): SourceCodeInfo_Location {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseSourceCodeInfo_Location();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -4457,7 +4453,7 @@ export const GeneratedCodeInfo: MessageFns<GeneratedCodeInfo> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): GeneratedCodeInfo {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseGeneratedCodeInfo();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -4530,7 +4526,7 @@ export const GeneratedCodeInfo_Annotation: MessageFns<GeneratedCodeInfo_Annotati
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): GeneratedCodeInfo_Annotation {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseGeneratedCodeInfo_Annotation();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-ts_proto v2.7.0
|
||||
// protoc-gen-ts_proto v2.7.7
|
||||
// protoc v3.21.12
|
||||
// source: vault.proto
|
||||
|
||||
@@ -32,7 +32,7 @@ export const Vault: MessageFns<Vault> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): Vault {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseVault();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
@@ -114,7 +114,7 @@ export const Vault_SecretsEntry: MessageFns<Vault_SecretsEntry> = {
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): Vault_SecretsEntry {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseVault_SecretsEntry();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
|
||||
@@ -11,12 +11,22 @@ message EmailTemplate {
|
||||
optional string body = 2;
|
||||
}
|
||||
|
||||
enum SmtpEncryption {
|
||||
SMTP_ENCRYPTION_UNDEFINED = 0;
|
||||
SMTP_ENCRYPTION_NONE = 1;
|
||||
SMTP_ENCRYPTION_STARTTLS = 2;
|
||||
SMTP_ENCRYPTION_TLS = 3;
|
||||
}
|
||||
|
||||
message EmailConfig {
|
||||
optional string smtp_host = 1;
|
||||
optional uint32 smtp_port = 2;
|
||||
optional string smtp_username = 3;
|
||||
optional string smtp_password = 4 [ (secret) = true ];
|
||||
|
||||
// Which encryption method to use. STARTTLS by default.
|
||||
optional SmtpEncryption smtp_encryption = 5;
|
||||
|
||||
optional string sender_name = 11;
|
||||
optional string sender_address = 12;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ use log::*;
|
||||
use prost_reflect::{
|
||||
DynamicMessage, ExtensionDescriptor, FieldDescriptor, Kind, MapKey, ReflectMessage, Value,
|
||||
};
|
||||
use proto::{EmailTemplate, OAuthProviderId};
|
||||
use proto::{EmailTemplate, OAuthProviderId, SmtpEncryption};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::convert::TryFrom;
|
||||
use std::str::FromStr;
|
||||
@@ -480,10 +480,6 @@ pub(crate) fn validate_config(
|
||||
tables: &SchemaMetadataCache,
|
||||
config: &proto::Config,
|
||||
) -> Result<(), ConfigError> {
|
||||
fn ierr(msg: impl Into<String>) -> Result<(), ConfigError> {
|
||||
return Err(ConfigError::Invalid(msg.into()));
|
||||
}
|
||||
|
||||
// Check server settings.
|
||||
let Some(ref app_name) = config.server.application_name else {
|
||||
return ierr("Missing application name");
|
||||
@@ -583,79 +579,7 @@ pub(crate) fn validate_config(
|
||||
}
|
||||
|
||||
// Check email config.
|
||||
{
|
||||
let email = &config.email;
|
||||
|
||||
let mut num_smtp_fields = 0;
|
||||
if let Some(ref host) = email.smtp_host {
|
||||
if !format!("http://{host}/").validate_url() {
|
||||
return ierr(format!("Invalid SMTP host {host}."));
|
||||
}
|
||||
num_smtp_fields += 1;
|
||||
}
|
||||
|
||||
if let Some(port) = email.smtp_port {
|
||||
let port = u16::try_from(port).map_err(|_| ConfigError::Invalid("not a u16".into()))?;
|
||||
if port == 0 {
|
||||
return ierr("Invalid SMTP port.");
|
||||
}
|
||||
num_smtp_fields += 1;
|
||||
}
|
||||
|
||||
if let Some(ref username) = email.smtp_username {
|
||||
if username.is_empty() {
|
||||
return ierr("Invalid SMTP username.");
|
||||
}
|
||||
num_smtp_fields += 1;
|
||||
}
|
||||
|
||||
if let Some(ref password) = email.smtp_password {
|
||||
if password.is_empty() {
|
||||
return ierr("Invalid SMTP username.");
|
||||
}
|
||||
num_smtp_fields += 1;
|
||||
}
|
||||
|
||||
if num_smtp_fields != 0 && num_smtp_fields != 4 {
|
||||
return ierr("Only a subset of SMTP settings provided");
|
||||
}
|
||||
|
||||
if let Some(ref sender_address) = email.sender_address {
|
||||
if !sender_address.validate_email() {
|
||||
return ierr("Invalid sender address.");
|
||||
};
|
||||
if email.sender_name.is_none() {
|
||||
return ierr("Sender address but missing sender name.");
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_template(template: Option<&EmailTemplate>) -> Result<(), ConfigError> {
|
||||
// NOTE: It's ok for either subject or body to be empty, we'll simply fall back to the
|
||||
// defaults.
|
||||
|
||||
// Check that VERIFICATION_URL is present.
|
||||
if let Some(ref body) = template.as_ref().and_then(|t| t.body.as_ref()) {
|
||||
lazy_static! {
|
||||
static ref URL_PATTERN: regex::Regex =
|
||||
regex::Regex::new(r#"\{\{[ ]*VERIFICATION_URL[ ]*\}\}"#).expect("static");
|
||||
static ref CODE_PATTERN: regex::Regex =
|
||||
regex::Regex::new(r#"\{\{[ ]*CODE[ ]*\}\}"#).expect("static");
|
||||
};
|
||||
|
||||
if !(URL_PATTERN.is_match(body) || CODE_PATTERN.is_match(body)) {
|
||||
return ierr(format!(
|
||||
"Body needs to contain '{{{{ VERIFICATION_URL }}}}, got: {body}'"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
validate_template(email.user_verification_template.as_ref())?;
|
||||
validate_template(email.change_email_template.as_ref())?;
|
||||
validate_template(email.password_reset_template.as_ref())?;
|
||||
}
|
||||
validate_email_config(&config.email)?;
|
||||
|
||||
// Check job config.
|
||||
for job in &config.jobs.system_jobs {
|
||||
@@ -675,6 +599,108 @@ pub(crate) fn validate_config(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub(crate) fn validate_email_config(email: &proto::EmailConfig) -> Result<(), ConfigError> {
|
||||
validate_email_template(email.user_verification_template.as_ref())?;
|
||||
validate_email_template(email.change_email_template.as_ref())?;
|
||||
validate_email_template(email.password_reset_template.as_ref())?;
|
||||
|
||||
let Some(_host) = &email.smtp_host else {
|
||||
match (email.smtp_port, &email.smtp_username, &email.smtp_password) {
|
||||
(None, None, None) => {
|
||||
// No SMTP configured
|
||||
return Ok(());
|
||||
}
|
||||
_ => {
|
||||
return ierr("Partial SMTP configuration provided. Host missing.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: check that `_host` is a valid hostname or IP.
|
||||
|
||||
// NOTE: When no explicit sender is given, we fall back to noreply@host.
|
||||
if let Some(ref sender_address) = email.sender_address {
|
||||
if !sender_address.validate_email() {
|
||||
return ierr("Invalid sender address.");
|
||||
};
|
||||
if email.sender_name.is_none() {
|
||||
return ierr("Sender address but missing sender name.");
|
||||
}
|
||||
}
|
||||
|
||||
let _port: u16 = match email.smtp_port {
|
||||
Some(port) => {
|
||||
// NOTE: Protobuf doesn't support uint16 types natively, so we have to range-check.
|
||||
let port = u16::try_from(port).map_err(|_| ConfigError::Invalid("not a u16".into()))?;
|
||||
if port == 0 {
|
||||
return ierr("Invalid SMTP port.");
|
||||
}
|
||||
port
|
||||
}
|
||||
None => {
|
||||
return ierr("SMTP port missing.");
|
||||
}
|
||||
};
|
||||
|
||||
let user = &email.smtp_username;
|
||||
let pw = &email.smtp_password;
|
||||
|
||||
match email.smtp_encryption() {
|
||||
SmtpEncryption::None => {
|
||||
return match (user, pw) {
|
||||
(None, None) => Ok(()),
|
||||
_ => ierr("SMTP username or password provided, though encryption turned off."),
|
||||
};
|
||||
}
|
||||
_enc => {
|
||||
if let Some(user) = user {
|
||||
if user.is_empty() {
|
||||
return ierr("Invalid SMTP username.");
|
||||
}
|
||||
} else {
|
||||
return ierr("Missing SMTP username.");
|
||||
}
|
||||
|
||||
if let Some(pw) = pw {
|
||||
if pw.is_empty() {
|
||||
return ierr("Invalid SMTP username.");
|
||||
}
|
||||
} else {
|
||||
return ierr("Missing SMTP password.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
fn validate_email_template(template: Option<&EmailTemplate>) -> Result<(), ConfigError> {
|
||||
// NOTE: It's ok for either subject or body to be empty, we'll simply fall back to the
|
||||
// defaults.
|
||||
|
||||
// Check that VERIFICATION_URL is present.
|
||||
if let Some(ref body) = template.as_ref().and_then(|t| t.body.as_ref()) {
|
||||
lazy_static! {
|
||||
static ref URL_PATTERN: regex::Regex =
|
||||
regex::Regex::new(r#"\{\{[ ]*VERIFICATION_URL[ ]*\}\}"#).expect("static");
|
||||
static ref CODE_PATTERN: regex::Regex =
|
||||
regex::Regex::new(r#"\{\{[ ]*CODE[ ]*\}\}"#).expect("static");
|
||||
};
|
||||
|
||||
if !(URL_PATTERN.is_match(body) || CODE_PATTERN.is_match(body)) {
|
||||
return ierr(format!(
|
||||
"Body needs to contain '{{{{ VERIFICATION_URL }}}}, got: {body}'"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
fn ierr(msg: impl std::string::ToString) -> Result<(), ConfigError> {
|
||||
return Err(ConfigError::Invalid(msg.to_string()));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_env {
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::AppState;
|
||||
use crate::config::proto::Config;
|
||||
use crate::config::proto::{Config, EmailConfig, SmtpEncryption};
|
||||
use crate::constants::AUTH_API_PATH;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
@@ -256,12 +256,34 @@ pub(crate) enum Mailer {
|
||||
}
|
||||
|
||||
impl Mailer {
|
||||
fn new_smtp(host: String, port: u16, user: String, pass: String) -> Result<Mailer, EmailError> {
|
||||
let mailer = AsyncSmtpTransport::<Tokio1Executor>::starttls_relay(&host)?
|
||||
.port(port)
|
||||
.credentials(smtp::authentication::Credentials::new(user, pass))
|
||||
.build();
|
||||
return Ok(Mailer::Smtp(Arc::new(mailer)));
|
||||
fn new_smtp(
|
||||
host: &str,
|
||||
port: u16,
|
||||
user: Option<String>,
|
||||
pass: Option<String>,
|
||||
encryption: SmtpEncryption,
|
||||
) -> Result<Mailer, EmailError> {
|
||||
let transport = match encryption {
|
||||
SmtpEncryption::None => {
|
||||
AsyncSmtpTransport::<Tokio1Executor>::builder_dangerous(host).port(port)
|
||||
}
|
||||
SmtpEncryption::Starttls | SmtpEncryption::Undefined => {
|
||||
AsyncSmtpTransport::<Tokio1Executor>::starttls_relay(host)?
|
||||
.port(port)
|
||||
.credentials(smtp::authentication::Credentials::new(
|
||||
user.ok_or(EmailError::Missing("SMTP username"))?,
|
||||
pass.ok_or(EmailError::Missing("SMTP password"))?,
|
||||
))
|
||||
}
|
||||
SmtpEncryption::Tls => AsyncSmtpTransport::<Tokio1Executor>::relay(host)?
|
||||
.port(port)
|
||||
.credentials(smtp::authentication::Credentials::new(
|
||||
user.ok_or(EmailError::Missing("SMTP username"))?,
|
||||
pass.ok_or(EmailError::Missing("SMTP password"))?,
|
||||
)),
|
||||
};
|
||||
|
||||
return Ok(Mailer::Smtp(Arc::new(transport.build())));
|
||||
}
|
||||
|
||||
fn new_local() -> Mailer {
|
||||
@@ -269,33 +291,32 @@ impl Mailer {
|
||||
}
|
||||
|
||||
pub(crate) fn new_from_config(config: &Config) -> Mailer {
|
||||
let smtp_from_config = || -> Result<Mailer, EmailError> {
|
||||
let email = &config.email;
|
||||
fn smtp_from_config(email: &EmailConfig) -> Result<Mailer, EmailError> {
|
||||
let host = email
|
||||
.smtp_host
|
||||
.to_owned()
|
||||
.as_deref()
|
||||
.ok_or(EmailError::Missing("SMTP host"))?;
|
||||
let port = email
|
||||
.smtp_port
|
||||
.map(|port| port as u16)
|
||||
.and_then(|port| u16::try_from(port).ok())
|
||||
.ok_or(EmailError::Missing("SMTP port"))?;
|
||||
let user = email
|
||||
.smtp_username
|
||||
.to_owned()
|
||||
.ok_or(EmailError::Missing("SMTP username"))?;
|
||||
let pass = email
|
||||
.smtp_password
|
||||
.to_owned()
|
||||
.ok_or(EmailError::Missing("SMTP password"))?;
|
||||
|
||||
Self::new_smtp(host, port, user, pass)
|
||||
};
|
||||
|
||||
if let Ok(mailer) = smtp_from_config() {
|
||||
return mailer;
|
||||
return Mailer::new_smtp(
|
||||
host,
|
||||
port,
|
||||
email.smtp_username.clone(),
|
||||
email.smtp_password.clone(),
|
||||
email.smtp_encryption(),
|
||||
);
|
||||
}
|
||||
|
||||
return Self::new_local();
|
||||
return match smtp_from_config(&config.email) {
|
||||
Ok(mailer) => mailer,
|
||||
Err(err) => {
|
||||
info!("Falling back to local sendmail: {err}");
|
||||
Self::new_local()
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user