@protobase/server 0.23.7 → 0.23.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/better-auth/auth-mail/account-exists-email.js +2 -2
- package/dist/better-auth/auth-mail/invitation-email.js +2 -2
- package/dist/better-auth/auth-mail/reset-password-email.js +2 -2
- package/dist/better-auth/auth-mail/send-auth-email.js +1 -1
- package/dist/better-auth/auth-mail/sign-in-code-email.js +2 -2
- package/dist/better-auth/auth-mail/sign-up-code-email.js +2 -2
- package/dist/better-auth/auth-mail/staff-sign-in-email.js +2 -2
- package/dist/better-auth/auth-mail/two-factor-code-email.js +2 -2
- package/dist/check-views.js +3 -3
- package/dist/comments/mention-email.js +2 -2
- package/dist/email.d.ts +3 -0
- package/dist/email.js +2 -0
- package/dist/jobs/options.js +15 -1
- package/dist/jobs/runtime.js +2 -8
- package/dist/mail/define-email.d.ts +7 -1
- package/dist/mail/define-email.js +5 -2
- package/dist/mail/email-layout.d.ts +26 -0
- package/dist/{better-auth/auth-mail/auth-email-layout.js → mail/email-layout.js} +4 -4
- package/dist/mail/mailing-app.d.ts +7 -0
- package/dist/{better-auth/auth-mail → mail}/mailing-app.js +1 -1
- package/dist/mail/smtp-mailer.d.ts +5 -0
- package/dist/mail/smtp-mailer.js +7 -0
- package/dist/view-access.js +10 -0
- package/package.json +6 -5
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { defineEmail } from "../../mail/define-email.js";
|
|
2
|
-
import {
|
|
2
|
+
import { EmailButton, EmailLayout, EmailNote } from "../../mail/email-layout.js";
|
|
3
3
|
import { signInPath } from "@protobase/schema";
|
|
4
4
|
import { Text } from "react-email";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
//#region src/better-auth/auth-mail/account-exists-email.tsx
|
|
7
|
-
const AccountExistsEmail = ({ app }) => /* @__PURE__ */ jsxs(
|
|
7
|
+
const AccountExistsEmail = ({ app }) => /* @__PURE__ */ jsxs(EmailLayout, {
|
|
8
8
|
app,
|
|
9
9
|
preview: `Sign in to ${app.name} with this address instead`,
|
|
10
10
|
heading: "You already have an account",
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { defineEmail } from "../../mail/define-email.js";
|
|
2
|
-
import {
|
|
2
|
+
import { EmailButton, EmailLayout, EmailNote } from "../../mail/email-layout.js";
|
|
3
3
|
import { Text } from "react-email";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region src/better-auth/auth-mail/invitation-email.tsx
|
|
6
6
|
const InvitationEmail = ({ app, inviter, organization, url, expiresAt }) => {
|
|
7
7
|
const target = organization ?? app.name;
|
|
8
|
-
return /* @__PURE__ */ jsxs(
|
|
8
|
+
return /* @__PURE__ */ jsxs(EmailLayout, {
|
|
9
9
|
app,
|
|
10
10
|
preview: `${inviter} invites you to join ${target}`,
|
|
11
11
|
heading: `Join ${target}`,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { defineEmail } from "../../mail/define-email.js";
|
|
2
|
-
import {
|
|
2
|
+
import { EmailButton, EmailLayout, EmailNote } from "../../mail/email-layout.js";
|
|
3
3
|
import { Text } from "react-email";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region src/better-auth/auth-mail/reset-password-email.tsx
|
|
6
|
-
const ResetPasswordEmail = ({ app, url, lifetime }) => /* @__PURE__ */ jsxs(
|
|
6
|
+
const ResetPasswordEmail = ({ app, url, lifetime }) => /* @__PURE__ */ jsxs(EmailLayout, {
|
|
7
7
|
app,
|
|
8
8
|
preview: `Choose a new password for ${app.name}`,
|
|
9
9
|
heading: "Reset your password",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mailingApp } from "
|
|
1
|
+
import { mailingApp } from "../../mail/mailing-app.js";
|
|
2
2
|
//#region src/better-auth/auth-mail/send-auth-email.ts
|
|
3
3
|
/**
|
|
4
4
|
* Renders the auth email `name` for `data`, with the app it comes from on the origin of `requestURL` (see `mailingApp`),
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { defineEmail } from "../../mail/define-email.js";
|
|
2
|
-
import {
|
|
2
|
+
import { EmailCode, EmailLayout, EmailNote } from "../../mail/email-layout.js";
|
|
3
3
|
import { Text } from "react-email";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region src/better-auth/auth-mail/sign-in-code-email.tsx
|
|
6
|
-
const SignInCodeEmail = ({ app, code, lifetime }) => /* @__PURE__ */ jsxs(
|
|
6
|
+
const SignInCodeEmail = ({ app, code, lifetime }) => /* @__PURE__ */ jsxs(EmailLayout, {
|
|
7
7
|
app,
|
|
8
8
|
preview: `Your code to sign in to ${app.name}`,
|
|
9
9
|
heading: "Your sign-in code",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { defineEmail } from "../../mail/define-email.js";
|
|
2
|
-
import {
|
|
2
|
+
import { EmailCode, EmailLayout, EmailNote } from "../../mail/email-layout.js";
|
|
3
3
|
import { Text } from "react-email";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region src/better-auth/auth-mail/sign-up-code-email.tsx
|
|
6
|
-
const SignUpCodeEmail = ({ app, code, lifetime }) => /* @__PURE__ */ jsxs(
|
|
6
|
+
const SignUpCodeEmail = ({ app, code, lifetime }) => /* @__PURE__ */ jsxs(EmailLayout, {
|
|
7
7
|
app,
|
|
8
8
|
preview: `Your code to create an account with ${app.name}`,
|
|
9
9
|
heading: "Create your account",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { defineEmail } from "../../mail/define-email.js";
|
|
2
|
-
import {
|
|
2
|
+
import { EmailLayout, EmailNote } from "../../mail/email-layout.js";
|
|
3
3
|
import { Text } from "react-email";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region src/better-auth/auth-mail/staff-sign-in-email.tsx
|
|
6
|
-
const StaffSignInEmail = ({ app, staff, reason, expiresAt }) => /* @__PURE__ */ jsxs(
|
|
6
|
+
const StaffSignInEmail = ({ app, staff, reason, expiresAt }) => /* @__PURE__ */ jsxs(EmailLayout, {
|
|
7
7
|
app,
|
|
8
8
|
preview: `${staff} signed in as you to help`,
|
|
9
9
|
heading: "Staff signed in as you",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { defineEmail } from "../../mail/define-email.js";
|
|
2
|
-
import {
|
|
2
|
+
import { EmailCode, EmailLayout, EmailNote } from "../../mail/email-layout.js";
|
|
3
3
|
import { Text } from "react-email";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region src/better-auth/auth-mail/two-factor-code-email.tsx
|
|
6
|
-
const TwoFactorCodeEmail = ({ app, code, lifetime }) => /* @__PURE__ */ jsxs(
|
|
6
|
+
const TwoFactorCodeEmail = ({ app, code, lifetime }) => /* @__PURE__ */ jsxs(EmailLayout, {
|
|
7
7
|
app,
|
|
8
8
|
preview: `Your code to finish signing in to ${app.name}`,
|
|
9
9
|
heading: "Finish signing in",
|
package/dist/check-views.js
CHANGED
|
@@ -7,7 +7,7 @@ import { mapDisplayErrors, mapErrors } from "./view-map.js";
|
|
|
7
7
|
import { relatedColumnErrors } from "./view-related-column.js";
|
|
8
8
|
import { editableErrors } from "./view-editable.js";
|
|
9
9
|
import { conditionProblems } from "@protobase/layout";
|
|
10
|
-
import { actionInputErrors, addressErrors, addressPlaceholders, boardErrors,
|
|
10
|
+
import { actionInputErrors, addressErrors, addressPlaceholders, boardErrors, chartFields, datesErrors, displayFields, fieldRefsIn, hrefField, hrefPlaceholders, layoutItems, linkActionError, promptError, promptFields, updateSetErrors, viewOfList } from "@protobase/schema/internal";
|
|
11
11
|
import { isRecordCondition } from "@protobase/schema";
|
|
12
12
|
//#region src/check-views.ts
|
|
13
13
|
const linkFields = (run) => run?.kind !== "link" ? [] : "to" in run ? addressPlaceholders(run.to) : hrefPlaceholders(run.href);
|
|
@@ -147,7 +147,7 @@ const listOfErrors = (model, name, models) => {
|
|
|
147
147
|
* has a section, related section or sidebar whose `when` condition names what the app does not have, a related section or related list column, a cards display's or gallery's photos or a cards display's favorite button, a `listOf`, a "Save search" button or a
|
|
148
148
|
* `searchesOf` that do not fit the resources, cards
|
|
149
149
|
* displays of one list that name different photos, a chart per value that cannot group or add up its fields, a map that cannot show the
|
|
150
|
-
* record's position or a map display that cannot find the records in its view, a board whose columns are no filterable enum, a calendar whose dates are no filterable date or timestamp fields, a link to an address the app does not have, an AI action that cannot write its field, action inputs that do not fit, or a create page that is not one of `pages`.
|
|
150
|
+
* record's position or a map display that cannot find the records in its view, a board whose columns are no filterable enum, a calendar or timeline whose dates are no filterable date or timestamp fields, a link to an address the app does not have, an AI action that cannot write its field, action inputs that do not fit, or a create page that is not one of `pages`.
|
|
151
151
|
*/
|
|
152
152
|
const checkViews = (registry, views, pages = [], fieldTypes = {}) => {
|
|
153
153
|
const models = Object.fromEntries(registry.entries.map((entry) => [entry.name, entry.model]));
|
|
@@ -165,7 +165,7 @@ const checkViews = (registry, views, pages = [], fieldTypes = {}) => {
|
|
|
165
165
|
const searches = [...view.saveSearch ? saveSearchErrors(view.saveSearch, view.resource, models, views).map((error) => `saveSearch: ${error}`) : [], ...view.searchesOf ? searchesOfErrors(view.searchesOf, view.resource, models).map((error) => `searchesOf: ${error}`) : []];
|
|
166
166
|
const maps = [...layoutItems(view.layout).flatMap((item) => item.kind === "map" ? mapErrors(item, model).map((error) => `map "${item.title}": ${error}`) : []), ...(view.list?.displays ?? []).flatMap((display) => display.kind === "map" ? mapDisplayErrors(display, model).map((error) => `display "${display.name}": ${error}`) : [])];
|
|
167
167
|
const boards = (view.list?.displays ?? []).flatMap((display) => display.kind === "board" ? boardErrors(display, model).map((error) => `display "${display.name}": ${error}`) : []);
|
|
168
|
-
const calendars = (view.list?.displays ?? []).flatMap((display) => display.kind === "calendar" ?
|
|
168
|
+
const calendars = (view.list?.displays ?? []).flatMap((display) => display.kind === "calendar" || display.kind === "timeline" ? datesErrors(display, model).map((error) => `display "${display.name}": ${error}`) : []);
|
|
169
169
|
const chart = view.chart ? chartErrors(view.chart, model).map((error) => `chart: ${error}`) : [];
|
|
170
170
|
const history = model.history ? [] : layoutItems(view.layout).flatMap((item) => item.kind === "history" ? [`history "${item.title}" needs .history() on resource "${view.resource}"`] : []);
|
|
171
171
|
const comments = model.comments ? [] : layoutItems(view.layout).flatMap((item) => item.kind === "comments" ? [`comments "${item.title}" needs .comments() on resource "${view.resource}"`] : []);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { defineEmail } from "../mail/define-email.js";
|
|
2
|
-
import {
|
|
2
|
+
import { EmailButton, EmailLayout, EmailNote } from "../mail/email-layout.js";
|
|
3
3
|
import { Text } from "react-email";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region src/comments/mention-email.tsx
|
|
6
|
-
const MentionEmail = ({ app, author, record, body, url }) => /* @__PURE__ */ jsxs(
|
|
6
|
+
const MentionEmail = ({ app, author, record, body, url }) => /* @__PURE__ */ jsxs(EmailLayout, {
|
|
7
7
|
app,
|
|
8
8
|
preview: `${author} mentioned you on ${record}`,
|
|
9
9
|
heading: `${author} mentioned you`,
|
package/dist/email.d.ts
ADDED
package/dist/email.js
ADDED
package/dist/jobs/options.js
CHANGED
|
@@ -21,6 +21,20 @@ const queueOptions = (options) => {
|
|
|
21
21
|
/** The queue settings `queueOptions` sets, as `getQueue` reads them back, so a changed definition is noticed. */
|
|
22
22
|
const queueSettingsDiffer = (wanted, stored) => Object.keys(wanted).some((key) => key !== "policy" && stored[key] !== wanted[key]);
|
|
23
23
|
/**
|
|
24
|
+
* The pg-boss worker options of a job: one job per fetch, `concurrency` at once. A worker fetches again at once after a
|
|
25
|
+
* fetch that found a job while more are ready than its workers take in one poll, by the queue's ready count that the
|
|
26
|
+
* queue maintenance refreshes about every minute, so a backlog drains as fast as its jobs run rather than one job per
|
|
27
|
+
* worker each poll.
|
|
28
|
+
*/
|
|
29
|
+
const workOptions = (concurrency, pollSeconds) => ({
|
|
30
|
+
batchSize: 1,
|
|
31
|
+
localConcurrency: concurrency,
|
|
32
|
+
pollingIntervalSeconds: pollSeconds,
|
|
33
|
+
burstWhenReadyExceeds: concurrency,
|
|
34
|
+
includeMetadata: true,
|
|
35
|
+
perJobResults: true
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
24
38
|
* The pg-boss send options of one enqueue: when, how urgent, and the key a waiting job is found by. An ordered job's
|
|
25
39
|
* group is its key in pg-boss, which keeps one group's jobs in order.
|
|
26
40
|
*/
|
|
@@ -41,4 +55,4 @@ const sendOptions = (options, enqueue = {}) => {
|
|
|
41
55
|
};
|
|
42
56
|
};
|
|
43
57
|
//#endregion
|
|
44
|
-
export { queueOptions, queueSettingsDiffer, sendOptions };
|
|
58
|
+
export { queueOptions, queueSettingsDiffer, sendOptions, workOptions };
|
package/dist/jobs/runtime.js
CHANGED
|
@@ -5,7 +5,7 @@ import { runJob, validatePayload } from "./run-job.js";
|
|
|
5
5
|
import { platformMailer } from "../mail/platform-mailer.js";
|
|
6
6
|
import { runInBackground } from "../background-work.js";
|
|
7
7
|
import { jobsDatabase } from "./database.js";
|
|
8
|
-
import { queueOptions, queueSettingsDiffer, sendOptions } from "./options.js";
|
|
8
|
+
import { queueOptions, queueSettingsDiffer, sendOptions, workOptions } from "./options.js";
|
|
9
9
|
import { createSchedules } from "./schedules.js";
|
|
10
10
|
import { sql } from "kysely";
|
|
11
11
|
import { PgBoss } from "pg-boss";
|
|
@@ -50,13 +50,7 @@ const startJobs = async ({ db, jobs, work = true, pollSeconds = 2, shutdownSecon
|
|
|
50
50
|
const running = workers.get(definition.name);
|
|
51
51
|
if (running?.concurrency === concurrency) return;
|
|
52
52
|
if (running) await boss.offWork(definition.name);
|
|
53
|
-
const id = await boss.work(definition.name, {
|
|
54
|
-
batchSize: 1,
|
|
55
|
-
localConcurrency: concurrency,
|
|
56
|
-
pollingIntervalSeconds: pollSeconds,
|
|
57
|
-
includeMetadata: true,
|
|
58
|
-
perJobResults: true
|
|
59
|
-
}, async ([job]) => runInBackground(async () => {
|
|
53
|
+
const id = await boss.work(definition.name, workOptions(concurrency, pollSeconds), async ([job]) => runInBackground(async () => {
|
|
60
54
|
const current = definitions.get(definition.name) ?? definition;
|
|
61
55
|
if (current.options.schedule !== void 0 && job.singletonKey === "protobase.schedule") await schedules.following(current, job.startAfter);
|
|
62
56
|
return [await runJob(current, job, db, mail)];
|
|
@@ -10,14 +10,20 @@ export type EmailOptions<Data extends object> = {
|
|
|
10
10
|
* It is rendered to the HTML part, and the plain-text part is made from that HTML.
|
|
11
11
|
*/
|
|
12
12
|
component: EmailComponent<Data>;
|
|
13
|
+
/**
|
|
14
|
+
* More header fields, or how to make them from the email's data: such as `List-Unsubscribe` with the recipient's own
|
|
15
|
+
* unsubscribe link, and `List-Unsubscribe-Post: List-Unsubscribe=One-Click`, which mailbox providers expect of bulk mail.
|
|
16
|
+
*/
|
|
17
|
+
headers?: Record<string, string> | ((data: Data) => Record<string, string>);
|
|
13
18
|
};
|
|
14
19
|
/** A function component of the email's data, as JSX writes one; typed here so the server's own types need no React. */
|
|
15
20
|
export type EmailComponent<Data> = (props: Data) => unknown;
|
|
16
|
-
/** An email as `render` gives it: the subject
|
|
21
|
+
/** An email as `render` gives it: the subject, both parts of the body, and its header fields when it has any. */
|
|
17
22
|
export type RenderedEmail = {
|
|
18
23
|
subject: string;
|
|
19
24
|
html: string;
|
|
20
25
|
text: string;
|
|
26
|
+
headers?: Record<string, string>;
|
|
21
27
|
};
|
|
22
28
|
export type EmailTemplate<Data extends object> = {
|
|
23
29
|
readonly name: string;
|
|
@@ -18,10 +18,13 @@ const defineEmail = (options) => {
|
|
|
18
18
|
const render = async (data) => {
|
|
19
19
|
const renderer = await loadRenderer();
|
|
20
20
|
const html = await renderer.render(renderer.createElement(options.component, data));
|
|
21
|
+
const subject = typeof options.subject === "function" ? options.subject(data) : options.subject;
|
|
22
|
+
const headers = typeof options.headers === "function" ? options.headers(data) : options.headers;
|
|
21
23
|
return {
|
|
22
|
-
subject
|
|
24
|
+
subject,
|
|
23
25
|
html,
|
|
24
|
-
text: renderer.toPlainText(html)
|
|
26
|
+
text: renderer.toPlainText(html),
|
|
27
|
+
...headers && { headers }
|
|
25
28
|
};
|
|
26
29
|
};
|
|
27
30
|
const send = async (to, data, mailer = platformMailer()) => {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { MailingApp } from "./mailing-app.js";
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
//#region src/mail/email-layout.d.ts
|
|
4
|
+
export type EmailLayoutProps = {
|
|
5
|
+
app: MailingApp;
|
|
6
|
+
/** The line inboxes show after the subject. */
|
|
7
|
+
preview: string;
|
|
8
|
+
heading: string;
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
};
|
|
11
|
+
/** An email: its heading and body on a card, and a footer naming the app, linked to its home page. */
|
|
12
|
+
export declare const EmailLayout: ({ app, preview, heading, children }: EmailLayoutProps) => import("react").JSX.Element;
|
|
13
|
+
/** A code to type in, on a line of its own in both parts. */
|
|
14
|
+
export declare const EmailCode: ({ code }: {
|
|
15
|
+
code: string;
|
|
16
|
+
}) => import("react").JSX.Element;
|
|
17
|
+
/** A remark below the message, such as what to do when the email was not expected. */
|
|
18
|
+
export declare const EmailNote: ({ children }: {
|
|
19
|
+
children: ReactNode;
|
|
20
|
+
}) => import("react").JSX.Element;
|
|
21
|
+
/** The email's one action, such as signing in: a button, and the label with the link in the plain-text part. */
|
|
22
|
+
export declare const EmailButton: ({ href, children }: {
|
|
23
|
+
href: string;
|
|
24
|
+
children: ReactNode;
|
|
25
|
+
}) => import("react").JSX.Element;
|
|
26
|
+
//#endregion
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Body, Button, Container, Head, Heading, Hr, Html, Link, Preview, Text } from "react-email";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
//#region src/
|
|
3
|
+
//#region src/mail/email-layout.tsx
|
|
4
4
|
const styles = {
|
|
5
5
|
body: {
|
|
6
6
|
backgroundColor: "#f4f4f5",
|
|
@@ -39,8 +39,8 @@ const styles = {
|
|
|
39
39
|
fontWeight: 600
|
|
40
40
|
}
|
|
41
41
|
};
|
|
42
|
-
/** An
|
|
43
|
-
const
|
|
42
|
+
/** An email: its heading and body on a card, and a footer naming the app, linked to its home page. */
|
|
43
|
+
const EmailLayout = ({ app, preview, heading, children }) => /* @__PURE__ */ jsxs(Html, {
|
|
44
44
|
lang: "en",
|
|
45
45
|
children: [
|
|
46
46
|
/* @__PURE__ */ jsx(Head, {}),
|
|
@@ -87,4 +87,4 @@ const EmailButton = ({ href, children }) => /* @__PURE__ */ jsx(Button, {
|
|
|
87
87
|
children
|
|
88
88
|
});
|
|
89
89
|
//#endregion
|
|
90
|
-
export {
|
|
90
|
+
export { EmailButton, EmailCode, EmailLayout, EmailNote };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region src/
|
|
1
|
+
//#region src/mail/mailing-app.ts
|
|
2
2
|
/**
|
|
3
3
|
* The app as an auth email names it: `appName`, and the home page on the origin of `requestURL`, a URL on the origin
|
|
4
4
|
* the request came to (such as Better Auth's `ctx.context.baseURL`), or on `baseURL`'s origin without one.
|
|
@@ -6,6 +6,11 @@ export type MailMessage = {
|
|
|
6
6
|
text: string;
|
|
7
7
|
/** The HTML body, sent beside `text` as the alternative clients show when they can. */
|
|
8
8
|
html?: string;
|
|
9
|
+
/**
|
|
10
|
+
* More header fields by name, such as `List-Unsubscribe` and `List-Unsubscribe-Post`, which mailbox providers expect
|
|
11
|
+
* of bulk mail. A value is one line.
|
|
12
|
+
*/
|
|
13
|
+
headers?: Record<string, string>;
|
|
9
14
|
};
|
|
10
15
|
/** Sends one message; rejects when the server refuses it. */
|
|
11
16
|
export type Mailer = {
|
package/dist/mail/smtp-mailer.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
const smtpMailer = (settings) => {
|
|
11
11
|
let transport;
|
|
12
12
|
return { send: async (message) => {
|
|
13
|
+
checkHeaders(message.headers);
|
|
13
14
|
transport ??= import("nodemailer").then(({ createTransport }) => createTransport({
|
|
14
15
|
url: settings.smtpUrl,
|
|
15
16
|
name: senderDomain(settings.from)
|
|
@@ -26,5 +27,11 @@ const sender = ({ from, name }) => name ? {
|
|
|
26
27
|
} : from;
|
|
27
28
|
/** The domain of a sender address `readMailSettings` accepted, so always a dotted hostname. */
|
|
28
29
|
const senderDomain = (from) => from.slice(from.lastIndexOf("@") + 1);
|
|
30
|
+
const checkHeaders = (headers = {}) => {
|
|
31
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
32
|
+
if (!/^[!-9;-~]+$/.test(name)) throw new Error(`Mail header name "${name}" is not a valid header field name`);
|
|
33
|
+
if (/[\r\n]/.test(value)) throw new Error(`Mail header ${name} has a line break; a header value is one line`);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
29
36
|
//#endregion
|
|
30
37
|
export { smtpMailer };
|
package/dist/view-access.js
CHANGED
|
@@ -120,6 +120,16 @@ const listShape = (resource, model, models, prompted) => shapes.object({
|
|
|
120
120
|
end: shapes.field,
|
|
121
121
|
title: shapes.field
|
|
122
122
|
}, { requires: ["start"] }),
|
|
123
|
+
timeline: shapes.object({
|
|
124
|
+
kind: shapes.data,
|
|
125
|
+
name: shapes.data,
|
|
126
|
+
label: shapes.data,
|
|
127
|
+
icon: shapes.data,
|
|
128
|
+
start: shapes.field,
|
|
129
|
+
end: shapes.field,
|
|
130
|
+
group: shapes.field,
|
|
131
|
+
title: shapes.field
|
|
132
|
+
}, { requires: ["start"] }),
|
|
123
133
|
custom: shapes.object({
|
|
124
134
|
kind: shapes.data,
|
|
125
135
|
name: shapes.data,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@protobase/server",
|
|
3
|
-
"version": "0.23.
|
|
3
|
+
"version": "0.23.9",
|
|
4
4
|
"description": "The Protobase REST API as a Hono app, with Better Auth sign-in.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"node": ">=22.12.0"
|
|
16
16
|
},
|
|
17
17
|
"exports": {
|
|
18
|
-
".": "./dist/index.js"
|
|
18
|
+
".": "./dist/index.js",
|
|
19
|
+
"./email": "./dist/email.js"
|
|
19
20
|
},
|
|
20
21
|
"types": "./dist/index.d.ts",
|
|
21
22
|
"files": [
|
|
@@ -31,9 +32,9 @@
|
|
|
31
32
|
"dependencies": {
|
|
32
33
|
"@better-auth/passkey": "^1.7.7",
|
|
33
34
|
"@better-auth/sso": "^1.7.7",
|
|
34
|
-
"@protobase/layout": "0.23.
|
|
35
|
-
"@protobase/query": "0.23.
|
|
36
|
-
"@protobase/schema": "0.23.
|
|
35
|
+
"@protobase/layout": "0.23.9",
|
|
36
|
+
"@protobase/query": "0.23.9",
|
|
37
|
+
"@protobase/schema": "0.23.9",
|
|
37
38
|
"@react-email/render": "^2.1.0",
|
|
38
39
|
"@scalar/hono-api-reference": "^0.12.10",
|
|
39
40
|
"@sentry/core": "^11.7.0",
|