@trypattern/sdk 0.1.19 → 0.1.21
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.
|
@@ -5,5 +5,12 @@ export type AuthService = {
|
|
|
5
5
|
exchange: (code: string) => Promise<ExchangeReply | undefined>;
|
|
6
6
|
sendCode: (request: SendCodeRequest) => Promise<SendCodeReply | undefined>;
|
|
7
7
|
verifyCode: (request: VerifyCodeRequest) => Promise<VerifiedIdentity | undefined>;
|
|
8
|
+
noteOwner: (note: OwnerNote) => Promise<boolean>;
|
|
9
|
+
};
|
|
10
|
+
export type OwnerNote = {
|
|
11
|
+
subject: string;
|
|
12
|
+
text: string;
|
|
13
|
+
replyTo?: string;
|
|
8
14
|
};
|
|
9
15
|
export declare function authServiceFor(env: ServerBindings): AuthService | undefined;
|
|
16
|
+
export declare function notifyOwner(env: ServerBindings, note: OwnerNote): Promise<boolean>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
1
2
|
import { exchangeReply, sendCodeReply, verifyCodeReply } from "./signInWire.js";
|
|
2
3
|
export const AUTH_URL = 'https://auth.trypattern.ai';
|
|
3
4
|
function httpAuthService(secret) {
|
|
@@ -18,7 +19,9 @@ function httpAuthService(secret) {
|
|
|
18
19
|
return {
|
|
19
20
|
exchange: (code) => post('/exchange', { code }, exchangeReply),
|
|
20
21
|
sendCode: (request) => post('/email/send', request, sendCodeReply),
|
|
21
|
-
verifyCode: async (request) => (await post('/email/verify', request, verifyCodeReply))?.identity
|
|
22
|
+
verifyCode: async (request) => (await post('/email/verify', request, verifyCodeReply))?.identity,
|
|
23
|
+
noteOwner: async (note) => (await post('/owner-note', note, z.object({ ok: z.literal(true) }))) !==
|
|
24
|
+
undefined
|
|
22
25
|
};
|
|
23
26
|
}
|
|
24
27
|
const devCodes = new Map();
|
|
@@ -41,6 +44,10 @@ const devAuthService = {
|
|
|
41
44
|
return undefined;
|
|
42
45
|
devCodes.delete(challenge);
|
|
43
46
|
return { email: sent.email, name: null, avatarUrl: null, via: 'email' };
|
|
47
|
+
},
|
|
48
|
+
noteOwner: async ({ subject, text }) => {
|
|
49
|
+
console.log(`Email to the owner: ${subject}\n${text}`);
|
|
50
|
+
return true;
|
|
44
51
|
}
|
|
45
52
|
};
|
|
46
53
|
export function authServiceFor(env) {
|
|
@@ -48,3 +55,11 @@ export function authServiceFor(env) {
|
|
|
48
55
|
return httpAuthService(env.AUTH_SECRET);
|
|
49
56
|
return import.meta.env.DEV ? devAuthService : undefined;
|
|
50
57
|
}
|
|
58
|
+
export async function notifyOwner(env, note) {
|
|
59
|
+
try {
|
|
60
|
+
return (await authServiceFor(env)?.noteOwner(note)) ?? false;
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -3,10 +3,11 @@ import type { BookingRequest } from '../bookings.ts';
|
|
|
3
3
|
import type { ServerBlock, ServerEnv } from './env.ts';
|
|
4
4
|
export type BookingsOptions = {
|
|
5
5
|
timezone: string;
|
|
6
|
+
managePath?: string;
|
|
6
7
|
};
|
|
7
8
|
export type BookingEmail = {
|
|
8
9
|
kind: 'requested' | 'accepted' | 'declined';
|
|
9
10
|
request: BookingRequest;
|
|
10
11
|
};
|
|
11
12
|
export declare function sendBookingEmail(c: Context<ServerEnv>, email: BookingEmail): Promise<void>;
|
|
12
|
-
export declare function bookings({ timezone }: BookingsOptions): ServerBlock;
|
|
13
|
+
export declare function bookings({ timezone, managePath }: BookingsOptions): ServerBlock;
|
package/dist/server/bookings.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FULL, RULES_ID, UNAVAILABLE, bookingBlackout, bookingRequests, bookingRules, bookingServices, bookingTables, daysOf, isValidTimezone, rulesOf, rulesProblem, slotFor, takenKey, windowsOn } from "../bookings.js";
|
|
2
2
|
import { refused } from "../services.js";
|
|
3
|
+
import { notifyOwner } from "./authService.js";
|
|
3
4
|
import { serveServices } from "./services.js";
|
|
4
5
|
import { readRow, readRows } from "./tables.js";
|
|
5
6
|
const HOLDING_SQL = "status IN ('requested', 'accepted')";
|
|
@@ -9,6 +10,39 @@ export async function sendBookingEmail(c, email) {
|
|
|
9
10
|
const to = email.kind === 'requested' ? 'the owner' : email.request.email;
|
|
10
11
|
console.log(`Booking email (${email.kind}) to ${to} from ${new URL(c.req.url).host}: ${email.request.name}, ${email.request.date} ${email.request.window}`);
|
|
11
12
|
}
|
|
13
|
+
function clockOf(time) {
|
|
14
|
+
const [hours = 0, minutes = 0] = time.split(':').map(Number);
|
|
15
|
+
const hour = hours % 12 === 0 ? 12 : hours % 12;
|
|
16
|
+
const suffix = hours < 12 ? 'am' : 'pm';
|
|
17
|
+
return minutes === 0
|
|
18
|
+
? `${hour}${suffix}`
|
|
19
|
+
: `${hour}:${String(minutes).padStart(2, '0')}${suffix}`;
|
|
20
|
+
}
|
|
21
|
+
function whenOf(request, label) {
|
|
22
|
+
const day = new Date(`${request.date}T12:00:00Z`).toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', timeZone: 'UTC' });
|
|
23
|
+
const [start = '', end = ''] = request.window.split('-');
|
|
24
|
+
return `${day}, ${label} (${clockOf(start)}–${clockOf(end)})`;
|
|
25
|
+
}
|
|
26
|
+
function ownerNoteOf(request, label, manage) {
|
|
27
|
+
const lines = [
|
|
28
|
+
`${request.name} asked to book ${request.offering} on ${whenOf(request, label)}.`,
|
|
29
|
+
'',
|
|
30
|
+
`Email: ${request.email}`,
|
|
31
|
+
...(request.phone === '' ? [] : [`Phone: ${request.phone}`]),
|
|
32
|
+
...(request.address === '' ? [] : [`Address: ${request.address}`]),
|
|
33
|
+
...(request.notes === '' ? [] : ['', request.notes]),
|
|
34
|
+
'',
|
|
35
|
+
request.status === 'accepted'
|
|
36
|
+
? `It is booked: your booking rules accept requests on their own. See it at ${manage}`
|
|
37
|
+
: `Accept or decline it at ${manage}`,
|
|
38
|
+
`Reply to this email to write to ${request.name}.`
|
|
39
|
+
];
|
|
40
|
+
return {
|
|
41
|
+
subject: `New booking request from ${request.name}`,
|
|
42
|
+
text: lines.join('\n'),
|
|
43
|
+
replyTo: request.email
|
|
44
|
+
};
|
|
45
|
+
}
|
|
12
46
|
function requireAdmin(c) {
|
|
13
47
|
const viewer = c.get('viewer');
|
|
14
48
|
if (viewer === null)
|
|
@@ -65,7 +99,7 @@ async function decide(c, id, status) {
|
|
|
65
99
|
function monthRange(month) {
|
|
66
100
|
return { first: `${month}-01`, last: `${month}-31` };
|
|
67
101
|
}
|
|
68
|
-
export function bookings({ timezone }) {
|
|
102
|
+
export function bookings({ timezone, managePath = '/' }) {
|
|
69
103
|
if (!isValidTimezone(timezone)) {
|
|
70
104
|
throw new Error(`bookings({ timezone: '${timezone}' }) is not a time zone: use an IANA name such as 'America/Toronto'`);
|
|
71
105
|
}
|
|
@@ -97,6 +131,7 @@ export function bookings({ timezone }) {
|
|
|
97
131
|
const saved = await readRequest(c.env.DB, id);
|
|
98
132
|
if (saved === undefined)
|
|
99
133
|
throw new Error('The request was not saved');
|
|
134
|
+
await notifyOwner(c.env, ownerNoteOf(saved, slot.label, new URL(managePath, c.req.url).href));
|
|
100
135
|
await sendBookingEmail(c, {
|
|
101
136
|
kind: status === 'accepted' ? 'accepted' : 'requested',
|
|
102
137
|
request: saved
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { createServer } from './createServer.ts';
|
|
2
2
|
export type { ServerBindings, ServerBlock, ServerEnv, Viewer } from './env.ts';
|
|
3
|
+
export { notifyOwner } from './authService.ts';
|
|
4
|
+
export type { OwnerNote } from './authService.ts';
|
|
3
5
|
export { bookings } from './bookings.ts';
|
|
4
6
|
export type { BookingsOptions } from './bookings.ts';
|
|
5
7
|
export { seedSql, serveTables, sqlTable, sqlTables } from './tables.ts';
|
package/dist/server/index.js
CHANGED