[READ-ONLY] One Calendar is a privacy-first calendar web app built with Next.js. It has modern security features, including e2ee, password-protected sharing, and self-destructing share links 馃搮
calendar.xyehr.cn
nextjs
2.4 kB
95 lines
1import {
2 auditEnricher,
3 auditOnly,
4 signed,
5 type AuditActor,
6 type RequestLogger,
7} from 'evlog'
8import { createAuthMiddleware } from 'evlog/better-auth'
9import { createEvlog } from 'evlog/next'
10import { auth } from '@/lib/auth'
11
12const identify = createAuthMiddleware(auth)
13
14const mainDrain = async (ctx: unknown) => {
15 console.log(JSON.stringify(ctx))
16}
17
18const auditDrain = auditOnly(
19 signed(
20 async (ctx: unknown) => {
21 console.log(JSON.stringify(ctx))
22 },
23 {
24 strategy: 'hash-chain',
25 },
26 ),
27 { await: true },
28)
29
30export const anonymousAuditActor = {
31 type: 'system',
32 id: 'anonymous',
33} satisfies AuditActor
34
35function actorFromEvent(event: Record<string, unknown>): AuditActor | null {
36 const user = event.user
37 if (user && typeof user === 'object') {
38 const candidate = user as Record<string, unknown>
39 if (typeof candidate.id === 'string') {
40 return {
41 type: 'user',
42 id: candidate.id,
43 ...(typeof candidate.email === 'string'
44 ? { email: candidate.email }
45 : {}),
46 ...(typeof candidate.name === 'string'
47 ? { displayName: candidate.name }
48 : {}),
49 }
50 }
51 }
52
53 if (typeof event.userId === 'string') {
54 return { type: 'user', id: event.userId }
55 }
56
57 return null
58}
59
60export function getAuditActor(
61 logger: Pick<RequestLogger, 'getContext'>,
62 fallback: AuditActor = anonymousAuditActor,
63) {
64 return actorFromEvent(logger.getContext()) ?? fallback
65}
66
67const auditEnrich = auditEnricher({
68 bridge: {
69 getSession: ({ event }) => actorFromEvent(event as Record<string, unknown>),
70 },
71})
72
73const evlog = createEvlog({
74 service: 'one-calendar',
75 drain: async (ctx) => {
76 const results = await Promise.allSettled([mainDrain(ctx), auditDrain(ctx)])
77 const rejected = results.find((r) => r.status === 'rejected')
78 if (rejected?.status === 'rejected') throw rejected.reason
79 },
80 enrich: auditEnrich,
81})
82
83const withEvlogBase = evlog.withEvlog
84
85export const withEvlog: typeof evlog.withEvlog = (handler) =>
86 withEvlogBase(async (...args: Parameters<typeof handler>) => {
87 const request = args[0]
88 if (request instanceof Request) {
89 const log = evlog.useLogger()
90 await identify(log, request.headers, new URL(request.url).pathname)
91 }
92 return handler(...args)
93 })
94
95export const { useLogger, log, createError, createEvlogError } = evlog