feat: HA scheduler leadership + templated email log

HA scheduler: the job scheduler and alert evaluator hold session-scoped
PostgreSQL advisory locks on pinned pool connections — replicas scan for
due work exactly once, failover verified live with two replicas. Manual
and hook-triggered runs stay available on any replica.

Email surface: mailer renders registered templates (invitation); every
send is logged to core.email_log with status and error text. New
GET /projects/:id/emails endpoint; Members page shows an Email log card.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomas DvorakandDevin committed 2026-09-24 19:27:06 +02:00
1 parent 45a1621909
commit f747b33b46
24 files changed
+669 -16

No files matched your search

@@ -67,6 +67,8 @@ export type { DeployMarker } from './models/DeployMarker';
export type { Document } from './models/Document';
export type { DocumentID } from './models/DocumentID';
export type { DocumentListResponse } from './models/DocumentListResponse';
export { EmailLogEntry } from './models/EmailLogEntry';
export type { EmailLogListResponse } from './models/EmailLogListResponse';
export type { ErrorEnvelope } from './models/ErrorEnvelope';
export type { EventSeriesBucket } from './models/EventSeriesBucket';
export type { HealthStatus } from './models/HealthStatus';
@@ -207,6 +209,8 @@ export { $DeployMarker } from './schemas/$DeployMarker';
export { $Document } from './schemas/$Document';
export { $DocumentID } from './schemas/$DocumentID';
export { $DocumentListResponse } from './schemas/$DocumentListResponse';
export { $EmailLogEntry } from './schemas/$EmailLogEntry';
export { $EmailLogListResponse } from './schemas/$EmailLogListResponse';
export { $ErrorEnvelope } from './schemas/$ErrorEnvelope';
export { $EventSeriesBucket } from './schemas/$EventSeriesBucket';
export { $HealthStatus } from './schemas/$HealthStatus';
@@ -291,6 +295,7 @@ export { AlertsService } from './services/AlertsService';
export { AutomationService } from './services/AutomationService';
export { CollectionsService } from './services/CollectionsService';
export { DatabasesService } from './services/DatabasesService';
export { EmailService } from './services/EmailService';
export { HealthService } from './services/HealthService';
export { InboundService } from './services/InboundService';
export { InstanceService } from './services/InstanceService';
@@ -0,0 +1,20 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type EmailLogEntry = {
id: string;
template: string;
to_email: string;
subject: string;
status: EmailLogEntry.status;
error?: string;
created_at: string;
};
export namespace EmailLogEntry {
export enum status {
SENT = 'sent',
FAILED = 'failed',
}
}
@@ -0,0 +1,9 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { EmailLogEntry } from './EmailLogEntry';
export type EmailLogListResponse = {
items: Array<EmailLogEntry>;
};
@@ -0,0 +1,37 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export const $EmailLogEntry = {
properties: {
id: {
type: 'string',
isRequired: true,
format: 'uuid',
},
template: {
type: 'string',
isRequired: true,
},
to_email: {
type: 'string',
isRequired: true,
},
subject: {
type: 'string',
isRequired: true,
},
status: {
type: 'Enum',
isRequired: true,
},
error: {
type: 'string',
},
created_at: {
type: 'string',
isRequired: true,
format: 'date-time',
},
},
} as const;
@@ -0,0 +1,15 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export const $EmailLogListResponse = {
properties: {
items: {
type: 'array',
contains: {
type: 'EmailLogEntry',
},
isRequired: true,
},
},
} as const;
@@ -0,0 +1,36 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { EmailLogListResponse } from '../models/EmailLogListResponse';
import type { CancelablePromise } from '../core/CancelablePromise';
import { OpenAPI } from '../core/OpenAPI';
import { request as __request } from '../core/request';
export class EmailService {
/**
* Recent transactional emails sent for this project (invitations etc.) — every send is logged with its outcome.
* @returns EmailLogListResponse Email log entries
* @throws ApiError
*/
public static listEmailLog({
projectId,
limit = 50,
}: {
projectId: string,
limit?: number,
}): CancelablePromise<EmailLogListResponse> {
return __request(OpenAPI, {
method: 'GET',
url: '/projects/{projectID}/emails',
path: {
'projectID': projectId,
},
query: {
'limit': limit,
},
errors: {
401: `Error response`,
},
});
}
}