mirror of
https://github.com/pockethost/pockethost.git
synced 2025-11-23 22:15:49 +00:00
mothership bundle
This commit is contained in:
parent
e6f4618101
commit
8f382dce79
@ -53,11 +53,13 @@
|
||||
"rizzdown",
|
||||
"Rpcs",
|
||||
"semvers",
|
||||
"Squeezy",
|
||||
"sslify",
|
||||
"superadmin",
|
||||
"syslogd",
|
||||
"tailable",
|
||||
"TRYFETCH",
|
||||
"tsup",
|
||||
"Ulimits",
|
||||
"unsub",
|
||||
"Unsub",
|
||||
@ -66,6 +68,7 @@
|
||||
"Unwatching",
|
||||
"unzipper",
|
||||
"upserting",
|
||||
"Virtio"
|
||||
"Virtio",
|
||||
"xsignature"
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,5 +1,11 @@
|
||||
# pockethost
|
||||
|
||||
## 2.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Refactored mothership to TS using JSVM bundling
|
||||
|
||||
## 2.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pockethost",
|
||||
"version": "2.0.1",
|
||||
"version": "2.0.2",
|
||||
"author": {
|
||||
"name": "Ben Allfree",
|
||||
"url": "https://github.com/benallfree"
|
||||
|
||||
@ -94,7 +94,7 @@ export const createSettings = () => ({
|
||||
MOTHERSHIP_ADMIN_PASSWORD: mkString(),
|
||||
PH_MOTHERSHIP_APP_ROOT: mkString(_MOTHERSHIP_APP_ROOT()),
|
||||
MOTHERSHIP_MIGRATIONS_DIR: mkPath(_MOTHERSHIP_APP_ROOT(`pb_migrations`)),
|
||||
MOTHERSHIP_HOOKS_DIR: mkPath(_MOTHERSHIP_APP_ROOT(`pb_hooks`, `src`)),
|
||||
MOTHERSHIP_HOOKS_DIR: mkPath(_MOTHERSHIP_APP_ROOT(`pb_hooks`)),
|
||||
MOTHERSHIP_APP_DIR: mkPath(_MOTHERSHIP_APP_ROOT(`ph_app`), {
|
||||
required: false,
|
||||
}),
|
||||
|
||||
Binary file not shown.
@ -1,6 +1,12 @@
|
||||
{
|
||||
"name": "pockethost-mothership-app",
|
||||
"dependencies": {
|
||||
"pocketpages": "^0.8.0"
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"dev": "tsup --watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@s-libs/micro-dash": "^18.0.0",
|
||||
"tsup": "^8.3.5",
|
||||
"type-fest": "^4.6.0"
|
||||
}
|
||||
}
|
||||
|
||||
3133
packages/pockethost/src/mothership-app/pb_hooks/mothership.js
Normal file
3133
packages/pockethost/src/mothership-app/pb_hooks/mothership.js
Normal file
File diff suppressed because it is too large
Load Diff
122
packages/pockethost/src/mothership-app/pb_hooks/mothership.pb.js
Normal file
122
packages/pockethost/src/mothership-app/pb_hooks/mothership.pb.js
Normal file
@ -0,0 +1,122 @@
|
||||
// src/lib/handlers/instance/hooks.ts
|
||||
routerAdd(
|
||||
"PUT",
|
||||
"/api/instance/:id",
|
||||
(c) => {
|
||||
return require(`${__hooks}/mothership`).HandleInstanceUpdate(c);
|
||||
},
|
||||
$apis.requireRecordAuth()
|
||||
);
|
||||
routerAdd(
|
||||
"POST",
|
||||
"/api/instance",
|
||||
(c) => {
|
||||
return require(`${__hooks}/mothership`).HandleInstanceCreate(c);
|
||||
},
|
||||
$apis.requireRecordAuth()
|
||||
);
|
||||
routerAdd(
|
||||
"DELETE",
|
||||
"/api/instance/:id",
|
||||
(c) => {
|
||||
return require(`${__hooks}/mothership`).HandleInstanceDelete(c);
|
||||
},
|
||||
$apis.requireRecordAuth()
|
||||
);
|
||||
onModelBeforeCreate((e) => {
|
||||
return require(`${__hooks}/mothership`).HandleInstanceVersionValidation(e);
|
||||
}, "instances");
|
||||
onModelAfterUpdate((e) => {
|
||||
}, "instances");
|
||||
onModelAfterCreate((e) => {
|
||||
}, "instances");
|
||||
onAfterBootstrap((e) => {
|
||||
});
|
||||
onAfterBootstrap((e) => {
|
||||
});
|
||||
onAfterBootstrap((e) => {
|
||||
return require(`${__hooks}/mothership`).HandleInstancesResetIdle(e);
|
||||
});
|
||||
onModelBeforeUpdate((e) => {
|
||||
return require(`${__hooks}/mothership`).HandleInstanceBeforeUpdate(e);
|
||||
}, "instances");
|
||||
|
||||
// src/lib/handlers/lemon/hooks.ts
|
||||
routerAdd("POST", "/api/ls", (c) => {
|
||||
return require(`${__hooks}/mothership`).HandleLemonSqueezySale(c);
|
||||
});
|
||||
|
||||
// src/lib/handlers/mail/hooks.ts
|
||||
routerAdd(
|
||||
"POST",
|
||||
"/api/mail",
|
||||
(c) => {
|
||||
return require(`${__hooks}/mothership`).HandleMailSend(c);
|
||||
},
|
||||
$apis.requireAdminAuth()
|
||||
);
|
||||
|
||||
// src/lib/handlers/meta/hooks.ts
|
||||
onAfterBootstrap((e) => {
|
||||
return require(`${__hooks}/mothership`).HandleMetaUpdateAtBoot(e);
|
||||
});
|
||||
|
||||
// src/lib/handlers/mirror/hooks.ts
|
||||
routerAdd(
|
||||
"GET",
|
||||
"/api/mirror",
|
||||
(c) => {
|
||||
return require(`${__hooks}/HandleMirrorData`).HandleMirrorData(c);
|
||||
},
|
||||
$apis.gzip(),
|
||||
$apis.requireAdminAuth()
|
||||
);
|
||||
|
||||
// src/lib/handlers/notify/hooks.ts
|
||||
routerAdd(`GET`, `api/process_single_notification`, (c) => {
|
||||
return require(`${__hooks}/mothership`).HandleProcessSingleNotification(c);
|
||||
});
|
||||
onModelAfterCreate((e) => {
|
||||
return require(`${__hooks}/mothership`).HandleProcessNotification(e);
|
||||
}, `notifications`);
|
||||
onModelBeforeUpdate((e) => {
|
||||
return require(`${__hooks}/mothership`).HandleUserWelcomeMessage(e);
|
||||
}, "users");
|
||||
|
||||
// src/lib/handlers/outpost/hooks.ts
|
||||
routerAdd("GET", "/api/unsubscribe", (c) => {
|
||||
return require(`${__hooks}/mothership`).HandleOutpostUnsubscribe(c);
|
||||
});
|
||||
|
||||
// src/lib/handlers/signup/hooks.ts
|
||||
routerAdd("GET", "/api/signup", (c) => {
|
||||
return require(`${__hooks}/mothership`).HandleSignupCheck(c);
|
||||
});
|
||||
routerAdd("POST", "/api/signup", (c) => {
|
||||
return require(`${__hooks}/mothership`).HandleSignupConfirm(c);
|
||||
});
|
||||
|
||||
// src/lib/handlers/sns/hooks.ts
|
||||
routerAdd("POST", "/api/sns", (c) => {
|
||||
return require(`${__hooks}/mothership`).HandleSesError(c);
|
||||
});
|
||||
|
||||
// src/lib/handlers/stats/hooks.ts
|
||||
routerAdd("GET", "/api/stats", (c) => {
|
||||
return require(`${__hooks}/mothership`).HandleStatsRequest(c);
|
||||
});
|
||||
|
||||
// src/lib/handlers/user/hooks.ts
|
||||
routerAdd(
|
||||
"GET",
|
||||
"/api/userToken/:id",
|
||||
(c) => {
|
||||
return require(`${__hooks}/mothership`).HandleUserTokenRequest(c);
|
||||
},
|
||||
$apis.requireAdminAuth()
|
||||
);
|
||||
|
||||
// src/lib/handlers/versions/hooks.ts
|
||||
routerAdd("GET", "/api/versions", (c) => {
|
||||
return require(`${__hooks}/mothership`).HandleVersionsRequest(c);
|
||||
});
|
||||
@ -1,65 +0,0 @@
|
||||
/*
|
||||
{
|
||||
"subdomain": "foo"
|
||||
}
|
||||
*/
|
||||
routerAdd(
|
||||
'POST',
|
||||
'/api/instance',
|
||||
(c) => {
|
||||
const dao = $app.dao()
|
||||
const { mkLog, versions } = /** @type {Lib} */ (
|
||||
require(`${__hooks}/lib.js`)
|
||||
)
|
||||
|
||||
const log = mkLog(`POST:instance`)
|
||||
const authRecord = /** @type {models.Record} */ (c.get('authRecord')) // empty if not authenticated as regular auth record
|
||||
log(`***authRecord`, JSON.stringify(authRecord))
|
||||
|
||||
if (!authRecord) {
|
||||
throw new Error(`Expected authRecord here`)
|
||||
}
|
||||
|
||||
log(`***TOP OF POST`)
|
||||
let data = /** @type{ {subdomain?: string} } */ (
|
||||
new DynamicModel({
|
||||
subdomain: '',
|
||||
})
|
||||
)
|
||||
|
||||
log(`***before bind`)
|
||||
|
||||
c.bind(data)
|
||||
|
||||
log(`***after bind`)
|
||||
|
||||
// This is necessary for destructuring to work correctly
|
||||
data = JSON.parse(JSON.stringify(data))
|
||||
|
||||
const { subdomain, region } = data
|
||||
|
||||
log(`***vars`, JSON.stringify({ subdomain, region }))
|
||||
|
||||
if (!subdomain) {
|
||||
throw new BadRequestError(
|
||||
`Subdomain is required when creating an instance.`,
|
||||
)
|
||||
}
|
||||
|
||||
const collection = dao.findCollectionByNameOrId('instances')
|
||||
const record = new Record(collection)
|
||||
record.set('uid', authRecord.getId())
|
||||
record.set('region', region || `sfo-1`)
|
||||
record.set('subdomain', subdomain)
|
||||
record.set('status', 'idle')
|
||||
record.set('version', versions[0])
|
||||
record.set('syncAdmin', true)
|
||||
record.set('notifyMaintenanceMode', true)
|
||||
|
||||
const form = new RecordUpsertForm($app, record)
|
||||
form.submit()
|
||||
|
||||
return c.json(200, { instance: record })
|
||||
},
|
||||
$apis.requireRecordAuth(),
|
||||
)
|
||||
@ -1,69 +0,0 @@
|
||||
/*
|
||||
{
|
||||
"id": "kz4ngg77eaw1ho0",
|
||||
"fields": {
|
||||
"maintenance": true
|
||||
"name": '',
|
||||
"version": ''
|
||||
}
|
||||
}
|
||||
*/
|
||||
routerAdd(
|
||||
'DELETE',
|
||||
'/api/instance/:id',
|
||||
(c) => {
|
||||
const dao = $app.dao()
|
||||
const { mkLog } = /** @type {Lib} */ (require(`${__hooks}/lib.js`))
|
||||
|
||||
const log = mkLog(`DELETE:instance`)
|
||||
|
||||
log(`TOP OF DELETE`)
|
||||
let data = new DynamicModel({
|
||||
id: '',
|
||||
})
|
||||
|
||||
c.bind(data)
|
||||
log(`After bind`)
|
||||
|
||||
// This is necessary for destructuring to work correctly
|
||||
data = JSON.parse(JSON.stringify(data))
|
||||
|
||||
const id = c.pathParam('id')
|
||||
|
||||
log(
|
||||
`vars`,
|
||||
JSON.stringify({
|
||||
id,
|
||||
}),
|
||||
)
|
||||
|
||||
const authRecord = /** @type {models.Record} */ (c.get('authRecord')) // empty if not authenticated as regular auth record
|
||||
log(`authRecord`, JSON.stringify(authRecord))
|
||||
|
||||
if (!authRecord) {
|
||||
throw new BadRequestError(`Expected authRecord here`)
|
||||
}
|
||||
|
||||
const record = dao.findRecordById('instances', id)
|
||||
if (!record) {
|
||||
throw new BadRequestError(`Instance ${id} not found.`)
|
||||
}
|
||||
if (record.get('uid') !== authRecord.id) {
|
||||
throw new BadRequestError(`Not authorized`)
|
||||
}
|
||||
|
||||
if (record.getString('status').toLowerCase() !== 'idle') {
|
||||
throw new BadRequestError(`Instance must be shut down first.`)
|
||||
}
|
||||
|
||||
const path = [$os.getenv('DATA_ROOT'), id].join('/')
|
||||
log(`path ${path}`)
|
||||
const res = $os.removeAll(path)
|
||||
log(`res`, res)
|
||||
|
||||
dao.deleteRecord(record)
|
||||
|
||||
return c.json(200, { status: 'ok' })
|
||||
},
|
||||
$apis.requireRecordAuth(),
|
||||
)
|
||||
@ -1,7 +0,0 @@
|
||||
/// <reference path="../types/types.d.ts" />
|
||||
|
||||
/** Reset instance status to idle on start */
|
||||
onAfterBootstrap((e) => {
|
||||
const dao = $app.dao()
|
||||
dao.db().newQuery(`update instances set status='idle'`).execute()
|
||||
})
|
||||
@ -1,121 +0,0 @@
|
||||
/// <reference path="../types/types.d.ts" />
|
||||
|
||||
/*
|
||||
{
|
||||
"id": "kz4ngg77eaw1ho0",
|
||||
"fields": {
|
||||
"maintenance": true
|
||||
"name": '',
|
||||
"version": ''
|
||||
}
|
||||
}
|
||||
*/
|
||||
routerAdd(
|
||||
'PUT',
|
||||
'/api/instance/:id',
|
||||
(c) => {
|
||||
const dao = $app.dao()
|
||||
const { mkLog, removeEmptyKeys } = /** @type {Lib} */ (
|
||||
require(`${__hooks}/lib.js`)
|
||||
)
|
||||
const log = mkLog(`PUT:instance`)
|
||||
|
||||
log(`TOP OF PUT`)
|
||||
|
||||
let data =
|
||||
/**
|
||||
* @type {{
|
||||
* id: string
|
||||
* fields: {
|
||||
* subdomain: string | null
|
||||
* maintenance: boolean | null
|
||||
* version: string | null
|
||||
* secrets: StringKvLookup | null
|
||||
* syncAdmin: boolean | null
|
||||
* dev: boolean | null
|
||||
* cname: string | null
|
||||
* notifyMaintenanceMode: boolean | null
|
||||
* }
|
||||
* }} }
|
||||
*/
|
||||
(
|
||||
new DynamicModel({
|
||||
id: '',
|
||||
fields: {
|
||||
subdomain: null,
|
||||
maintenance: null,
|
||||
version: null,
|
||||
secrets: null,
|
||||
syncAdmin: null,
|
||||
dev: null,
|
||||
cname: null,
|
||||
notifyMaintenanceMode: null,
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
c.bind(data)
|
||||
log(`After bind`)
|
||||
|
||||
// This is necessary for destructuring to work correctly
|
||||
data = JSON.parse(JSON.stringify(data))
|
||||
|
||||
const id = c.pathParam('id')
|
||||
const {
|
||||
fields: {
|
||||
subdomain,
|
||||
maintenance,
|
||||
version,
|
||||
secrets,
|
||||
syncAdmin,
|
||||
dev,
|
||||
cname,
|
||||
notifyMaintenanceMode,
|
||||
},
|
||||
} = data
|
||||
|
||||
log(
|
||||
`vars`,
|
||||
JSON.stringify({
|
||||
id,
|
||||
subdomain,
|
||||
maintenance,
|
||||
version,
|
||||
secrets,
|
||||
syncAdmin,
|
||||
dev,
|
||||
cname,
|
||||
notifyMaintenanceMode,
|
||||
}),
|
||||
)
|
||||
|
||||
const record = dao.findRecordById('instances', id)
|
||||
const authRecord = /** @type {models.Record} */ (c.get('authRecord')) // empty if not authenticated as regular auth record
|
||||
log(`authRecord`, JSON.stringify(authRecord))
|
||||
|
||||
if (!authRecord) {
|
||||
throw new Error(`Expected authRecord here`)
|
||||
}
|
||||
if (record.get('uid') !== authRecord.id) {
|
||||
throw new BadRequestError(`Not authorized`)
|
||||
}
|
||||
|
||||
const sanitized = removeEmptyKeys({
|
||||
subdomain,
|
||||
version,
|
||||
maintenance,
|
||||
secrets,
|
||||
syncAdmin,
|
||||
dev,
|
||||
cname,
|
||||
notifyMaintenanceMode,
|
||||
})
|
||||
|
||||
const form = new RecordUpsertForm($app, record)
|
||||
form.loadData(sanitized)
|
||||
form.submit()
|
||||
|
||||
return c.json(200, { status: 'ok' })
|
||||
},
|
||||
$apis.requireRecordAuth(),
|
||||
)
|
||||
@ -1,183 +0,0 @@
|
||||
/// <reference types="../types/lib.d.ts" />
|
||||
|
||||
/** @type {Lib['mkAudit']} */
|
||||
const mkAudit = (log, dao) => {
|
||||
return (event, note, context) => {
|
||||
log(`top of audit`)
|
||||
log(`AUDIT:${event}: ${note}`, JSON.stringify({ context }, null, 2))
|
||||
dao.saveRecord(
|
||||
new Record(dao.findCollectionByNameOrId('audit'), {
|
||||
event,
|
||||
note,
|
||||
context,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** @type {Lib['mkLog']} */
|
||||
const mkLog =
|
||||
(namespace) =>
|
||||
/**
|
||||
* @param {...any} s
|
||||
* @returns
|
||||
*/
|
||||
(...s) =>
|
||||
console.log(
|
||||
`[${namespace}]`,
|
||||
...s.map((p) => {
|
||||
if (typeof p === 'object') return JSON.stringify(p, null, 2)
|
||||
return p
|
||||
}),
|
||||
)
|
||||
|
||||
/**
|
||||
* @param {...any} args
|
||||
* @returns
|
||||
*/
|
||||
const dbg = (...args) => console.log(args)
|
||||
|
||||
/**
|
||||
* @param {string} template
|
||||
* @param {{ [_: string]: string }} dict
|
||||
* @returns
|
||||
*/
|
||||
function interpolateString(template, dict) {
|
||||
return template.replace(/\{\$(\w+)\}/g, (match, key) => {
|
||||
dbg({ match, key })
|
||||
const lowerKey = key.toLowerCase()
|
||||
return dict.hasOwnProperty(lowerKey) ? dict[lowerKey] || '' : match
|
||||
})
|
||||
}
|
||||
|
||||
/** @type {Lib['mkNotifier']} */
|
||||
const mkNotifier =
|
||||
(log, dao) =>
|
||||
(channel, template, user_id, context = {}) => {
|
||||
log({ channel, template, user_id })
|
||||
const emailTemplate = dao.findFirstRecordByData(
|
||||
'message_templates',
|
||||
`slug`,
|
||||
template,
|
||||
)
|
||||
log(`got email template`, emailTemplate)
|
||||
if (!emailTemplate) throw new Error(`Template ${template} not found`)
|
||||
const emailNotification = new Record(
|
||||
dao.findCollectionByNameOrId('notifications'),
|
||||
{
|
||||
user: user_id,
|
||||
channel,
|
||||
message_template: emailTemplate.getId(),
|
||||
message_template_vars: context,
|
||||
},
|
||||
)
|
||||
log(`built notification record`, emailNotification)
|
||||
dao.saveRecord(emailNotification)
|
||||
}
|
||||
|
||||
/** @type {Lib['mkNotificationProcessor']} */
|
||||
const mkNotificationProcessor =
|
||||
(log, dao, test = false) =>
|
||||
(notificationRec) => {
|
||||
log({ notificationRec })
|
||||
|
||||
const channel = notificationRec.getString(`channel`)
|
||||
dao.expandRecord(notificationRec, ['message_template', 'user'], null)
|
||||
|
||||
const messageTemplateRec = notificationRec.expandedOne('message_template')
|
||||
if (!messageTemplateRec) {
|
||||
throw new Error(`Missing message template`)
|
||||
}
|
||||
const userRec = notificationRec.expandedOne('user')
|
||||
if (!userRec) {
|
||||
throw new Error(`Missing user record`)
|
||||
}
|
||||
const vars = JSON.parse(notificationRec.getString(`message_template_vars`))
|
||||
const to = userRec.email()
|
||||
const subject = interpolateString(
|
||||
messageTemplateRec.getString(`subject`),
|
||||
vars,
|
||||
)
|
||||
const html = interpolateString(
|
||||
messageTemplateRec.getString(`body_html`),
|
||||
vars,
|
||||
)
|
||||
|
||||
log({ channel, messageTemplateRec, userRec, vars, to, subject, html })
|
||||
switch (channel) {
|
||||
case `email`:
|
||||
/** @type {Partial<mailer.Message_In>} */
|
||||
const msgArgs = {
|
||||
from: {
|
||||
address: $app.settings().meta.senderAddress,
|
||||
name: $app.settings().meta.senderName,
|
||||
},
|
||||
to: [{ address: to }],
|
||||
bcc: [process.env.TEST_EMAIL]
|
||||
.filter((e) => !!e)
|
||||
.map((e) => ({ address: e })),
|
||||
subject,
|
||||
html,
|
||||
}
|
||||
if (test) {
|
||||
msgArgs.to = [{ address: `ben@benallfree.com` }]
|
||||
msgArgs.subject = `***TEST ${to} *** ${msgArgs.subject}`
|
||||
}
|
||||
log({ msgArgs })
|
||||
// @ts-ignore
|
||||
const msg = new MailerMessage(msgArgs)
|
||||
$app.newMailClient().send(msg)
|
||||
log(`email sent`)
|
||||
break
|
||||
|
||||
case `lemonbot`:
|
||||
const params = {
|
||||
url: test
|
||||
? process.env.DISCORD_TEST_CHANNEL_URL
|
||||
: process.env.DISCORD_STREAM_CHANNEL_URL,
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
content: subject,
|
||||
}),
|
||||
headers: { 'content-type': 'application/json' },
|
||||
timeout: 5, // in seconds
|
||||
}
|
||||
log(`sending discord message`, params)
|
||||
const res = $http.send(params)
|
||||
log(`discord sent`, res)
|
||||
break
|
||||
default:
|
||||
throw new Error(`Unsupported channel: ${channel}`)
|
||||
}
|
||||
if (!test) {
|
||||
notificationRec.set(`delivered`, new DateTime())
|
||||
dao.saveRecord(notificationRec)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} obj
|
||||
* @returns T
|
||||
*/
|
||||
function removeEmptyKeys(obj) {
|
||||
const sanitized = Object.entries(obj).reduce((acc, [key, value]) => {
|
||||
if (value !== null && value !== undefined) {
|
||||
acc[key] = value
|
||||
}
|
||||
return acc
|
||||
}, /** @type {T} */ ({}))
|
||||
return sanitized
|
||||
}
|
||||
|
||||
/** @type {Lib['versions']} */
|
||||
const versions = require(`${__hooks}/versions.cjs`)
|
||||
|
||||
module.exports = {
|
||||
mkAudit,
|
||||
mkNotificationProcessor,
|
||||
mkLog,
|
||||
mkNotifier,
|
||||
removeEmptyKeys,
|
||||
versions,
|
||||
}
|
||||
@ -1,52 +0,0 @@
|
||||
/// <reference path="../types/types.d.ts" />
|
||||
|
||||
routerAdd(
|
||||
'POST',
|
||||
'/api/mail',
|
||||
(c) => {
|
||||
const { mkLog } = /** @type {Lib} */ (require(`${__hooks}/lib.js`))
|
||||
const log = mkLog(`mail`)
|
||||
|
||||
let data = /** @type {{ to: string; subject: string; body: string }} */ (
|
||||
new DynamicModel({
|
||||
to: '',
|
||||
subject: '',
|
||||
body: '',
|
||||
})
|
||||
)
|
||||
|
||||
log(`before bind`)
|
||||
|
||||
c.bind(data)
|
||||
|
||||
log(`after bind`)
|
||||
|
||||
// This is necessary for destructuring to work correctly
|
||||
data = JSON.parse(JSON.stringify(data))
|
||||
|
||||
log(`bind parsed`, JSON.stringify(data))
|
||||
|
||||
const { to, subject, body } = data
|
||||
|
||||
const email = new MailerMessage({
|
||||
from: {
|
||||
address: $app.settings().meta.senderAddress,
|
||||
name: $app.settings().meta.senderName,
|
||||
},
|
||||
to: [{ address: to }],
|
||||
// bcc: [process.env.TEST_EMAIL]
|
||||
// .filter((e) => !!e)
|
||||
// .map((e) => ({ address: e })),
|
||||
subject: subject,
|
||||
html: body,
|
||||
})
|
||||
|
||||
$app.newMailClient().send(email)
|
||||
|
||||
const msg = `Sent to ${to}`
|
||||
log(msg)
|
||||
|
||||
return c.json(200, { status: 'ok' })
|
||||
},
|
||||
$apis.requireAdminAuth(),
|
||||
)
|
||||
@ -1,22 +0,0 @@
|
||||
/// <reference path="../types/types.d.ts" />
|
||||
|
||||
routerAdd(
|
||||
'GET',
|
||||
'/api/mirror',
|
||||
(c) => {
|
||||
const users = $app
|
||||
.dao()
|
||||
.findRecordsByExpr('verified_users', $dbx.exp('1=1'))
|
||||
|
||||
const instances = $app
|
||||
.dao()
|
||||
.findRecordsByExpr(
|
||||
'instances',
|
||||
$dbx.exp('instances.uid in (select id from verified_users)'),
|
||||
)
|
||||
|
||||
return c.json(200, { users, instances })
|
||||
},
|
||||
$apis.gzip(),
|
||||
$apis.requireAdminAuth(),
|
||||
)
|
||||
@ -1,61 +0,0 @@
|
||||
/// <reference path="../types/types.d.ts" />
|
||||
/// <reference path="../types/lib.d.ts" />
|
||||
|
||||
routerAdd(`GET`, `api/process_single_notification`, (c) => {
|
||||
const { mkLog, mkNotificationProcessor } = /** @type {Lib} */ (
|
||||
require(`${__hooks}/lib.js`)
|
||||
)
|
||||
const log = mkLog(`process_single_notification`)
|
||||
log(`start`)
|
||||
|
||||
const dao = $app.dao()
|
||||
|
||||
const processNotification = mkNotificationProcessor(
|
||||
log,
|
||||
dao,
|
||||
!!c.queryParam(`test`),
|
||||
)
|
||||
|
||||
try {
|
||||
const notification = dao.findFirstRecordByData(
|
||||
`notifications`,
|
||||
`delivered`,
|
||||
``,
|
||||
)
|
||||
if (!notification) {
|
||||
return c.json(200, `No notifications to send`)
|
||||
}
|
||||
processNotification(notification)
|
||||
} catch (e) {
|
||||
c.json(500, `${e}`)
|
||||
}
|
||||
return c.json(200, { status: 'ok' })
|
||||
})
|
||||
|
||||
onModelAfterCreate((e) => {
|
||||
const dao = e.dao || $app.dao()
|
||||
const { mkNotificationProcessor, mkLog, mkAudit } = /** @type {Lib} */ (
|
||||
require(`${__hooks}/lib.js`)
|
||||
)
|
||||
const log = mkLog(`notification:sendImmediately`)
|
||||
const audit = mkAudit(log, dao)
|
||||
const processNotification = mkNotificationProcessor(log, dao, false)
|
||||
|
||||
const notificationRec = /** @type {models.Record} */ (e.model)
|
||||
|
||||
log({ notificationRec })
|
||||
|
||||
try {
|
||||
dao.expandRecord(notificationRec, ['message_template'], null)
|
||||
|
||||
const messageTemplateRec = notificationRec.expandedOne(`message_template`)
|
||||
if (!messageTemplateRec) {
|
||||
throw new Error(`Missing message template`)
|
||||
}
|
||||
processNotification(notificationRec)
|
||||
} catch (e) {
|
||||
audit(`ERROR`, `${e}`, {
|
||||
notification: notificationRec.getId(),
|
||||
})
|
||||
}
|
||||
}, `notifications`)
|
||||
@ -1,10 +0,0 @@
|
||||
// src/pb/pb_hooks/pocodex.pb.ts
|
||||
try {
|
||||
require("pocodex/dist/pb").Init();
|
||||
} catch (e) {
|
||||
console.log(`WARNING: pocodex not loaded: ${e}`);
|
||||
if (e instanceof Error) {
|
||||
console.log(e.stack);
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vc3JjL3BiL3BiX2hvb2tzL3BvY29kZXgucGIudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbInRyeSB7XG4gIHJlcXVpcmUoJ3BvY29kZXgvZGlzdC9wYicpLkluaXQoKVxufSBjYXRjaCAoZSkge1xuICBjb25zb2xlLmxvZyhgV0FSTklORzogcG9jb2RleCBub3QgbG9hZGVkOiAke2V9YClcbiAgaWYgKGUgaW5zdGFuY2VvZiBFcnJvcikge1xuICAgIGNvbnNvbGUubG9nKGUuc3RhY2spXG4gIH1cbn1cbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBQSxJQUFJO0FBQ0YsVUFBUSxpQkFBaUIsRUFBRSxLQUFLO0FBQ2xDLFNBQVMsR0FBRztBQUNWLFVBQVEsSUFBSSxnQ0FBZ0MsQ0FBQyxFQUFFO0FBQy9DLE1BQUksYUFBYSxPQUFPO0FBQ3RCLFlBQVEsSUFBSSxFQUFFLEtBQUs7QUFBQSxFQUNyQjtBQUNGOyIsCiAgIm5hbWVzIjogW10KfQo=
|
||||
@ -1,217 +0,0 @@
|
||||
/// <reference path="../types/types.d.ts" />
|
||||
|
||||
routerAdd(
|
||||
'GET',
|
||||
'/api/signup',
|
||||
(c) => {
|
||||
/**
|
||||
* @param {string} slug
|
||||
* @returns
|
||||
*/
|
||||
const isAvailable = (slug) => {
|
||||
try {
|
||||
const record = $app
|
||||
.dao()
|
||||
.findFirstRecordByData('instances', 'subdomain', slug)
|
||||
return false
|
||||
} catch {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} fieldName
|
||||
* @param {string} slug
|
||||
* @param {string} description
|
||||
* @param {StringKvLookup} [extra]
|
||||
* @returns
|
||||
*/
|
||||
const error = (fieldName, slug, description, extra) =>
|
||||
new ApiError(500, description, {
|
||||
[fieldName]: new ValidationError(slug, description),
|
||||
...extra,
|
||||
})
|
||||
|
||||
const instanceName = (() => {
|
||||
const name = c.queryParam('name').trim()
|
||||
if (name) {
|
||||
if (name.match(/^[a-z][a-z0-9-]{2,39}$/) === null) {
|
||||
throw error(
|
||||
`instanceName`,
|
||||
`invalid`,
|
||||
`Instance name must begin with a letter, be between 3-40 characters, and can only contain a-z, 0-9, and hyphen (-).`,
|
||||
)
|
||||
}
|
||||
if (isAvailable(name)) {
|
||||
return name
|
||||
}
|
||||
throw error(
|
||||
`instanceName`,
|
||||
`exists`,
|
||||
`Instance name ${name} is not available.`,
|
||||
)
|
||||
} else {
|
||||
const random = require(`${__hooks}/random-words.js`)
|
||||
let i = 0
|
||||
while (true) {
|
||||
i++
|
||||
if (i > 100) {
|
||||
return +new Date()
|
||||
}
|
||||
const slug = random.generate(2).join(`-`)
|
||||
if (isAvailable(slug)) return slug
|
||||
}
|
||||
}
|
||||
})()
|
||||
return c.json(200, { instanceName })
|
||||
} /* optional middlewares */,
|
||||
)
|
||||
|
||||
/*
|
||||
// HTTP 200
|
||||
{
|
||||
status: 'ok'
|
||||
}
|
||||
|
||||
// HTTP 500
|
||||
{
|
||||
"code": 500,
|
||||
"message": "That user account already exists. Try a password reset.",
|
||||
"data": {
|
||||
"email": {
|
||||
"code": "exists",
|
||||
"message": "That user account already exists. Try a password reset."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
"code": 500,
|
||||
"message": "Instance name was taken, sorry about that. Try another.",
|
||||
"data": {
|
||||
"instanceName": {
|
||||
"code": "exists",
|
||||
"message": "Instance name was taken, sorry about that. Try another."
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// https://pocketbase.io/docs/js-routing/#sending-request-to-custom-routes-using-the-sdks
|
||||
routerAdd(
|
||||
'POST',
|
||||
'/api/signup',
|
||||
(c) => {
|
||||
const { versions } = /** @type {Lib} */ (require(`${__hooks}/lib.js`))
|
||||
|
||||
const dao = $app.dao()
|
||||
const parsed = (() => {
|
||||
const rawBody = readerToString(c.request().body)
|
||||
try {
|
||||
const parsed = JSON.parse(rawBody)
|
||||
return parsed
|
||||
} catch (e) {
|
||||
throw new BadRequestError(
|
||||
`Error parsing payload. You call this JSON? ${rawBody}`,
|
||||
e,
|
||||
)
|
||||
}
|
||||
})()
|
||||
const email = parsed.email?.trim().toLowerCase()
|
||||
const password = parsed.password?.trim()
|
||||
const desiredInstanceName = parsed.instanceName?.trim()
|
||||
const region = parsed.region?.trim()
|
||||
|
||||
/**
|
||||
* @param {string} fieldName
|
||||
* @param {string} slug
|
||||
* @param {string} description
|
||||
* @param {StringKvLookup} [extra]
|
||||
* @returns
|
||||
*/
|
||||
const error = (fieldName, slug, description, extra) =>
|
||||
new ApiError(500, description, {
|
||||
[fieldName]: new ValidationError(slug, description),
|
||||
...extra,
|
||||
})
|
||||
|
||||
if (!email) {
|
||||
throw error(`email`, 'required', 'Email is required')
|
||||
}
|
||||
|
||||
if (!password) {
|
||||
throw error(`password`, `required`, 'Password is required')
|
||||
}
|
||||
|
||||
if (!desiredInstanceName) {
|
||||
throw error(`instanceName`, `required`, `Instance name is required`)
|
||||
}
|
||||
|
||||
const userExists = (() => {
|
||||
try {
|
||||
const record = dao.findFirstRecordByData('users', 'email', email)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
})()
|
||||
|
||||
if (userExists) {
|
||||
throw error(
|
||||
`email`,
|
||||
`exists`,
|
||||
`That user account already exists. Try a password reset.`,
|
||||
)
|
||||
}
|
||||
|
||||
dao.runInTransaction((txDao) => {
|
||||
const usersCollection = dao.findCollectionByNameOrId('users')
|
||||
const instanceCollection = $app
|
||||
.dao()
|
||||
.findCollectionByNameOrId('instances')
|
||||
|
||||
const user = new Record(usersCollection)
|
||||
try {
|
||||
const username = $app
|
||||
.dao()
|
||||
.suggestUniqueAuthRecordUsername(
|
||||
'users',
|
||||
'user' + $security.randomStringWithAlphabet(5, '123456789'),
|
||||
)
|
||||
user.set('username', username)
|
||||
user.set('email', email)
|
||||
user.set('subscription', 'free')
|
||||
user.set('notifyMaintenanceMode', true)
|
||||
user.setPassword(password)
|
||||
txDao.saveRecord(user)
|
||||
} catch (e) {
|
||||
throw error(`email`, `fail`, `Could not create user: ${e}`)
|
||||
}
|
||||
|
||||
try {
|
||||
const instance = new Record(instanceCollection)
|
||||
instance.set('subdomain', desiredInstanceName)
|
||||
instance.set('region', region || `sfo-1`)
|
||||
instance.set('uid', user.get('id'))
|
||||
instance.set('status', 'idle')
|
||||
instance.set('notifyMaintenanceMode', true)
|
||||
instance.set('syncAdmin', true)
|
||||
instance.set('version', versions[0])
|
||||
txDao.saveRecord(instance)
|
||||
} catch (e) {
|
||||
if (`${e}`.match(/ UNIQUE /)) {
|
||||
throw error(
|
||||
`instanceName`,
|
||||
`exists`,
|
||||
`Instance name was taken, sorry about that. Try another.`,
|
||||
)
|
||||
}
|
||||
throw error(`instanceName`, `fail`, `Could not create instance: ${e}`)
|
||||
}
|
||||
|
||||
$mails.sendRecordVerification($app, user)
|
||||
})
|
||||
|
||||
return c.json(200, { status: 'ok' })
|
||||
} /* optional middlewares */,
|
||||
)
|
||||
@ -1,113 +0,0 @@
|
||||
/// <reference path="../types/types.d.ts" />
|
||||
|
||||
routerAdd('POST', '/api/sns', (c) => {
|
||||
const dao = $app.dao()
|
||||
const { mkLog, mkAudit } = /** @type {Lib} */ (require(`${__hooks}/lib.js`))
|
||||
const log = mkLog(`sns`)
|
||||
const audit = mkAudit(log, dao)
|
||||
|
||||
const processBounce = (emailAddress) => {
|
||||
log(`Processing ${emailAddress}`)
|
||||
const extra = /** @type {{ email: string; user: string }} */ ({
|
||||
email: emailAddress,
|
||||
})
|
||||
try {
|
||||
const user = dao.findFirstRecordByData('users', 'email', emailAddress)
|
||||
log(`user is`, user)
|
||||
extra.user = user.getId()
|
||||
user.setVerified(false)
|
||||
dao.saveRecord(user)
|
||||
audit('PBOUNCE', `User ${emailAddress} has been disabled`, extra)
|
||||
} catch (e) {
|
||||
audit('PBOUNCE_ERR', `${e}`, extra)
|
||||
}
|
||||
}
|
||||
|
||||
;[].forEach(processBounce)
|
||||
|
||||
const raw = readerToString(c.request().body)
|
||||
const data = JSON.parse(raw)
|
||||
log(JSON.stringify(data, null, 2))
|
||||
|
||||
const { Type } = data
|
||||
|
||||
switch (Type) {
|
||||
case `SubscriptionConfirmation`:
|
||||
const url = data.SubscribeURL
|
||||
log(url)
|
||||
$http.send({ url })
|
||||
return c.json(200, { status: 'ok' })
|
||||
|
||||
case `Notification`:
|
||||
const msg = JSON.parse(data.Message)
|
||||
log(msg)
|
||||
const { notificationType } = msg
|
||||
switch (notificationType) {
|
||||
case `Bounce`: {
|
||||
log(`Message is a bounce`)
|
||||
const { bounce } = msg
|
||||
const { bounceType } = bounce
|
||||
switch (bounceType) {
|
||||
case `Permanent`:
|
||||
log(`Message is a permanent bounce`)
|
||||
const { bouncedRecipients } = bounce
|
||||
bouncedRecipients.forEach((recipient) => {
|
||||
const { emailAddress } = recipient
|
||||
processBounce(emailAddress)
|
||||
})
|
||||
break
|
||||
default:
|
||||
audit('SNS_ERR', `Unrecognized bounce type ${bounceType}`, {
|
||||
raw,
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
case `Complaint`:
|
||||
{
|
||||
log(`Message is a Complaint`, msg)
|
||||
const { complaint } = msg
|
||||
const { complainedRecipients } = complaint
|
||||
complainedRecipients.forEach((recipient) => {
|
||||
const { emailAddress } = recipient
|
||||
log(`Processing ${emailAddress}`)
|
||||
try {
|
||||
const user = $app
|
||||
.dao()
|
||||
.findFirstRecordByData('users', 'email', emailAddress)
|
||||
log(`user is`, user)
|
||||
user.set(`unsubscribe`, true)
|
||||
dao.saveRecord(user)
|
||||
audit(
|
||||
'COMPLAINT',
|
||||
`User ${emailAddress} has been unsubscribed`,
|
||||
{ emailAddress, user: user.getId() },
|
||||
)
|
||||
} catch (e) {
|
||||
audit(
|
||||
'COMPLAINT_ERR',
|
||||
`${emailAddress} is not in the system.`,
|
||||
{
|
||||
emailAddress,
|
||||
},
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
break
|
||||
default:
|
||||
audit(
|
||||
'SNS_ERR',
|
||||
`Unrecognized notification type ${notificationType}`,
|
||||
{ raw },
|
||||
)
|
||||
}
|
||||
break
|
||||
default:
|
||||
audit(`SNS_ERR`, `Message ${Type} not handled`, {
|
||||
raw,
|
||||
})
|
||||
}
|
||||
|
||||
return c.json(200, { status: 'ok' })
|
||||
})
|
||||
@ -1,20 +0,0 @@
|
||||
/// <reference path="../types/types.d.ts" />
|
||||
|
||||
routerAdd(
|
||||
'GET',
|
||||
'/api/stats',
|
||||
(c) => {
|
||||
const result = new DynamicModel({
|
||||
total_flounder_subscribers: 0,
|
||||
})
|
||||
|
||||
$app
|
||||
.dao()
|
||||
.db()
|
||||
.select('total_flounder_subscribers')
|
||||
.from('stats')
|
||||
.one(result)
|
||||
|
||||
return c.json(200, result)
|
||||
} /* optional middlewares */,
|
||||
)
|
||||
@ -1,33 +0,0 @@
|
||||
/// <reference path="../types/types.d.ts" />
|
||||
|
||||
/*
|
||||
{
|
||||
"id": "user-id"
|
||||
}
|
||||
*/
|
||||
routerAdd(
|
||||
'GET',
|
||||
'/api/userToken/:id',
|
||||
(c) => {
|
||||
const dao = $app.dao()
|
||||
const { mkLog } = /** @type {Lib} */ (require(`${__hooks}/lib.js`))
|
||||
const log = mkLog(`user-token`)
|
||||
|
||||
const id = c.pathParam('id')
|
||||
|
||||
// log({ id })
|
||||
|
||||
if (!id) {
|
||||
throw new BadRequestError(`User ID is required.`)
|
||||
}
|
||||
|
||||
const rec = dao.findRecordById('users', id)
|
||||
const tokenKey = rec.getString('tokenKey')
|
||||
const passwordHash = rec.getString('passwordHash')
|
||||
const email = rec.getString(`email`)
|
||||
// log({ email, passwordHash, tokenKey })
|
||||
|
||||
return c.json(200, { email, passwordHash, tokenKey })
|
||||
},
|
||||
$apis.requireAdminAuth(),
|
||||
)
|
||||
@ -1,12 +0,0 @@
|
||||
/// <reference path="../types/types.d.ts" />
|
||||
|
||||
/** Return a list of available PocketBase versions */
|
||||
routerAdd(
|
||||
'GET',
|
||||
'/api/versions',
|
||||
(c) => {
|
||||
const { versions } = /** @type {Lib} */ (require(`${__hooks}/lib.js`))
|
||||
|
||||
return c.json(200, { versions })
|
||||
} /* optional middlewares */,
|
||||
)
|
||||
@ -1,167 +0,0 @@
|
||||
module.exports = [
|
||||
"0.22.*",
|
||||
"0.22.27",
|
||||
"0.22.26",
|
||||
"0.22.25",
|
||||
"0.22.24",
|
||||
"0.22.23",
|
||||
"0.22.22",
|
||||
"0.22.21",
|
||||
"0.22.20",
|
||||
"0.22.19",
|
||||
"0.22.18",
|
||||
"0.22.17",
|
||||
"0.22.16",
|
||||
"0.22.15",
|
||||
"0.22.14",
|
||||
"0.22.13",
|
||||
"0.22.12",
|
||||
"0.22.11",
|
||||
"0.22.10",
|
||||
"0.22.9",
|
||||
"0.22.8",
|
||||
"0.22.7",
|
||||
"0.22.6",
|
||||
"0.22.5",
|
||||
"0.22.4",
|
||||
"0.22.3",
|
||||
"0.22.2",
|
||||
"0.22.1",
|
||||
"0.22.0",
|
||||
"0.21.*",
|
||||
"0.21.3",
|
||||
"0.21.2",
|
||||
"0.21.1",
|
||||
"0.21.0",
|
||||
"0.20.*",
|
||||
"0.20.7",
|
||||
"0.20.6",
|
||||
"0.20.5",
|
||||
"0.20.4",
|
||||
"0.20.3",
|
||||
"0.20.2",
|
||||
"0.20.1",
|
||||
"0.20.0",
|
||||
"0.19.*",
|
||||
"0.19.4",
|
||||
"0.19.3",
|
||||
"0.19.2",
|
||||
"0.19.1",
|
||||
"0.19.0",
|
||||
"0.18.*",
|
||||
"0.18.10",
|
||||
"0.18.9",
|
||||
"0.18.8",
|
||||
"0.18.7",
|
||||
"0.18.6",
|
||||
"0.18.5",
|
||||
"0.18.4",
|
||||
"0.18.3",
|
||||
"0.18.2",
|
||||
"0.18.1",
|
||||
"0.18.0",
|
||||
"0.17.*",
|
||||
"0.17.7",
|
||||
"0.17.6",
|
||||
"0.17.5",
|
||||
"0.17.4",
|
||||
"0.17.3",
|
||||
"0.17.2",
|
||||
"0.17.1",
|
||||
"0.17.0",
|
||||
"0.16.*",
|
||||
"0.16.10",
|
||||
"0.16.9",
|
||||
"0.16.8",
|
||||
"0.16.7",
|
||||
"0.16.6",
|
||||
"0.16.5",
|
||||
"0.16.4",
|
||||
"0.16.3",
|
||||
"0.16.2",
|
||||
"0.16.1",
|
||||
"0.16.0",
|
||||
"0.15.*",
|
||||
"0.15.3",
|
||||
"0.15.2",
|
||||
"0.15.1",
|
||||
"0.15.0",
|
||||
"0.14.*",
|
||||
"0.14.5",
|
||||
"0.14.4",
|
||||
"0.14.3",
|
||||
"0.14.2",
|
||||
"0.14.1",
|
||||
"0.14.0",
|
||||
"0.13.*",
|
||||
"0.13.4",
|
||||
"0.13.3",
|
||||
"0.13.2",
|
||||
"0.13.1",
|
||||
"0.13.0",
|
||||
"0.12.*",
|
||||
"0.12.3",
|
||||
"0.12.2",
|
||||
"0.12.1",
|
||||
"0.12.0",
|
||||
"0.11.*",
|
||||
"0.11.4",
|
||||
"0.11.3",
|
||||
"0.11.2",
|
||||
"0.11.1",
|
||||
"0.11.0",
|
||||
"0.10.*",
|
||||
"0.10.4",
|
||||
"0.10.3",
|
||||
"0.10.2",
|
||||
"0.10.1",
|
||||
"0.10.0",
|
||||
"0.9.*",
|
||||
"0.9.2",
|
||||
"0.9.1",
|
||||
"0.9.0",
|
||||
"0.8.*",
|
||||
"0.8.0",
|
||||
"0.7.*",
|
||||
"0.7.10",
|
||||
"0.7.9",
|
||||
"0.7.8",
|
||||
"0.7.7",
|
||||
"0.7.6",
|
||||
"0.7.5",
|
||||
"0.7.4",
|
||||
"0.7.3",
|
||||
"0.7.2",
|
||||
"0.7.1",
|
||||
"0.7.0",
|
||||
"0.6.*",
|
||||
"0.6.0",
|
||||
"0.5.*",
|
||||
"0.5.2",
|
||||
"0.5.1",
|
||||
"0.5.0",
|
||||
"0.4.*",
|
||||
"0.4.2",
|
||||
"0.4.1",
|
||||
"0.4.0",
|
||||
"0.3.*",
|
||||
"0.3.4",
|
||||
"0.3.3",
|
||||
"0.3.2",
|
||||
"0.3.1",
|
||||
"0.3.0",
|
||||
"0.2.*",
|
||||
"0.2.8",
|
||||
"0.2.7",
|
||||
"0.2.6",
|
||||
"0.2.5",
|
||||
"0.2.4",
|
||||
"0.2.3",
|
||||
"0.2.2",
|
||||
"0.2.1",
|
||||
"0.2.0",
|
||||
"0.1.*",
|
||||
"0.1.2",
|
||||
"0.1.1",
|
||||
"0.1.0"
|
||||
]
|
||||
@ -1 +0,0 @@
|
||||
function seedrandom(seed: number): void
|
||||
@ -1,44 +0,0 @@
|
||||
type Logger = (...args: any[]) => void
|
||||
|
||||
type StringKvLookup = { [_: string]: string }
|
||||
|
||||
type AuditEvents =
|
||||
| 'ERROR'
|
||||
| 'NOTIFICATION_ERR'
|
||||
| 'LS'
|
||||
| 'LS_ERR'
|
||||
| 'PBOUNCE'
|
||||
| 'PBOUNCE_ERR'
|
||||
| 'SNS_ERR'
|
||||
| 'COMPLAINT'
|
||||
| 'COMPLAINT_ERR'
|
||||
| 'UNSUBSCRIBE'
|
||||
| 'UNSUBSCRIBE_ERR'
|
||||
|
||||
interface Lib {
|
||||
mkLog: (namespace: string) => Logger
|
||||
mkNotificationProcessor: (
|
||||
log: Logger,
|
||||
dao: daos.Dao,
|
||||
test?: boolean,
|
||||
) => (notificationRec: models.Record) => void
|
||||
mkNotifier: (
|
||||
log: Logger,
|
||||
dao: daos.dao,
|
||||
) => (
|
||||
channel: 'email' | 'lemonbot',
|
||||
template:
|
||||
| 'maintenance-mode'
|
||||
| 'lemon_order_email'
|
||||
| 'lemon_order_discord'
|
||||
| 'welcome',
|
||||
user_id: string,
|
||||
context: { [_: string]: any },
|
||||
) => void
|
||||
mkAudit: (
|
||||
log: Logger,
|
||||
dao: daos.Dao,
|
||||
) => (event: AuditEvents, note: string, context: { [_: string]: any }) => void
|
||||
removeEmptyKeys: <T>(obj: T) => T
|
||||
versions: string[]
|
||||
}
|
||||
167
packages/pockethost/src/mothership-app/pb_hooks/versions.cjs
Normal file
167
packages/pockethost/src/mothership-app/pb_hooks/versions.cjs
Normal file
@ -0,0 +1,167 @@
|
||||
module.exports = [
|
||||
'0.22.*',
|
||||
'0.22.27',
|
||||
'0.22.26',
|
||||
'0.22.25',
|
||||
'0.22.24',
|
||||
'0.22.23',
|
||||
'0.22.22',
|
||||
'0.22.21',
|
||||
'0.22.20',
|
||||
'0.22.19',
|
||||
'0.22.18',
|
||||
'0.22.17',
|
||||
'0.22.16',
|
||||
'0.22.15',
|
||||
'0.22.14',
|
||||
'0.22.13',
|
||||
'0.22.12',
|
||||
'0.22.11',
|
||||
'0.22.10',
|
||||
'0.22.9',
|
||||
'0.22.8',
|
||||
'0.22.7',
|
||||
'0.22.6',
|
||||
'0.22.5',
|
||||
'0.22.4',
|
||||
'0.22.3',
|
||||
'0.22.2',
|
||||
'0.22.1',
|
||||
'0.22.0',
|
||||
'0.21.*',
|
||||
'0.21.3',
|
||||
'0.21.2',
|
||||
'0.21.1',
|
||||
'0.21.0',
|
||||
'0.20.*',
|
||||
'0.20.7',
|
||||
'0.20.6',
|
||||
'0.20.5',
|
||||
'0.20.4',
|
||||
'0.20.3',
|
||||
'0.20.2',
|
||||
'0.20.1',
|
||||
'0.20.0',
|
||||
'0.19.*',
|
||||
'0.19.4',
|
||||
'0.19.3',
|
||||
'0.19.2',
|
||||
'0.19.1',
|
||||
'0.19.0',
|
||||
'0.18.*',
|
||||
'0.18.10',
|
||||
'0.18.9',
|
||||
'0.18.8',
|
||||
'0.18.7',
|
||||
'0.18.6',
|
||||
'0.18.5',
|
||||
'0.18.4',
|
||||
'0.18.3',
|
||||
'0.18.2',
|
||||
'0.18.1',
|
||||
'0.18.0',
|
||||
'0.17.*',
|
||||
'0.17.7',
|
||||
'0.17.6',
|
||||
'0.17.5',
|
||||
'0.17.4',
|
||||
'0.17.3',
|
||||
'0.17.2',
|
||||
'0.17.1',
|
||||
'0.17.0',
|
||||
'0.16.*',
|
||||
'0.16.10',
|
||||
'0.16.9',
|
||||
'0.16.8',
|
||||
'0.16.7',
|
||||
'0.16.6',
|
||||
'0.16.5',
|
||||
'0.16.4',
|
||||
'0.16.3',
|
||||
'0.16.2',
|
||||
'0.16.1',
|
||||
'0.16.0',
|
||||
'0.15.*',
|
||||
'0.15.3',
|
||||
'0.15.2',
|
||||
'0.15.1',
|
||||
'0.15.0',
|
||||
'0.14.*',
|
||||
'0.14.5',
|
||||
'0.14.4',
|
||||
'0.14.3',
|
||||
'0.14.2',
|
||||
'0.14.1',
|
||||
'0.14.0',
|
||||
'0.13.*',
|
||||
'0.13.4',
|
||||
'0.13.3',
|
||||
'0.13.2',
|
||||
'0.13.1',
|
||||
'0.13.0',
|
||||
'0.12.*',
|
||||
'0.12.3',
|
||||
'0.12.2',
|
||||
'0.12.1',
|
||||
'0.12.0',
|
||||
'0.11.*',
|
||||
'0.11.4',
|
||||
'0.11.3',
|
||||
'0.11.2',
|
||||
'0.11.1',
|
||||
'0.11.0',
|
||||
'0.10.*',
|
||||
'0.10.4',
|
||||
'0.10.3',
|
||||
'0.10.2',
|
||||
'0.10.1',
|
||||
'0.10.0',
|
||||
'0.9.*',
|
||||
'0.9.2',
|
||||
'0.9.1',
|
||||
'0.9.0',
|
||||
'0.8.*',
|
||||
'0.8.0',
|
||||
'0.7.*',
|
||||
'0.7.10',
|
||||
'0.7.9',
|
||||
'0.7.8',
|
||||
'0.7.7',
|
||||
'0.7.6',
|
||||
'0.7.5',
|
||||
'0.7.4',
|
||||
'0.7.3',
|
||||
'0.7.2',
|
||||
'0.7.1',
|
||||
'0.7.0',
|
||||
'0.6.*',
|
||||
'0.6.0',
|
||||
'0.5.*',
|
||||
'0.5.2',
|
||||
'0.5.1',
|
||||
'0.5.0',
|
||||
'0.4.*',
|
||||
'0.4.2',
|
||||
'0.4.1',
|
||||
'0.4.0',
|
||||
'0.3.*',
|
||||
'0.3.4',
|
||||
'0.3.3',
|
||||
'0.3.2',
|
||||
'0.3.1',
|
||||
'0.3.0',
|
||||
'0.2.*',
|
||||
'0.2.8',
|
||||
'0.2.7',
|
||||
'0.2.6',
|
||||
'0.2.5',
|
||||
'0.2.4',
|
||||
'0.2.3',
|
||||
'0.2.2',
|
||||
'0.2.1',
|
||||
'0.2.0',
|
||||
'0.1.*',
|
||||
'0.1.2',
|
||||
'0.1.1',
|
||||
'0.1.0',
|
||||
]
|
||||
12
packages/pockethost/src/mothership-app/src/hooks/index.ts
Normal file
12
packages/pockethost/src/mothership-app/src/hooks/index.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import '../lib/handlers/instance/hooks'
|
||||
import '../lib/handlers/lemon/hooks'
|
||||
import '../lib/handlers/mail/hooks'
|
||||
import '../lib/handlers/meta/hooks'
|
||||
import '../lib/handlers/mirror/hooks'
|
||||
import '../lib/handlers/notify/hooks'
|
||||
import '../lib/handlers/outpost/hooks'
|
||||
import '../lib/handlers/signup/hooks'
|
||||
import '../lib/handlers/sns/hooks'
|
||||
import '../lib/handlers/stats/hooks'
|
||||
import '../lib/handlers/user/hooks'
|
||||
import '../lib/handlers/versions/hooks'
|
||||
@ -0,0 +1,11 @@
|
||||
export * from './instance'
|
||||
export * from './lemon'
|
||||
export * from './mail'
|
||||
export * from './meta'
|
||||
export * from './mirror'
|
||||
export * from './notify'
|
||||
export * from './signup'
|
||||
export * from './sns'
|
||||
export * from './stats'
|
||||
export * from './user'
|
||||
export * from './versions'
|
||||
@ -0,0 +1,54 @@
|
||||
import { mkLog } from '$util/Logger'
|
||||
import { versions } from '$util/versions'
|
||||
|
||||
export const HandleInstanceCreate = (c: echo.Context) => {
|
||||
const dao = $app.dao()
|
||||
|
||||
const log = mkLog(`POST:instance`)
|
||||
const authRecord = c.get('authRecord') as models.Record | undefined // empty if not authenticated as regular auth record
|
||||
log(`***authRecord`, JSON.stringify(authRecord))
|
||||
|
||||
if (!authRecord) {
|
||||
throw new Error(`Expected authRecord here`)
|
||||
}
|
||||
|
||||
log(`***TOP OF POST`)
|
||||
let data = new DynamicModel({
|
||||
subdomain: '',
|
||||
region: 'sfo-1',
|
||||
}) as { subdomain?: string; region?: string }
|
||||
|
||||
log(`***before bind`)
|
||||
|
||||
c.bind(data)
|
||||
|
||||
log(`***after bind`)
|
||||
|
||||
// This is necessary for destructuring to work correctly
|
||||
data = JSON.parse(JSON.stringify(data))
|
||||
|
||||
const { subdomain, region } = data
|
||||
|
||||
log(`***vars`, JSON.stringify({ subdomain, region }))
|
||||
|
||||
if (!subdomain) {
|
||||
throw new BadRequestError(
|
||||
`Subdomain is required when creating an instance.`,
|
||||
)
|
||||
}
|
||||
|
||||
const collection = dao.findCollectionByNameOrId('instances')
|
||||
const record = new Record(collection)
|
||||
record.set('uid', authRecord.getId())
|
||||
record.set('region', region || `sfo-1`)
|
||||
record.set('subdomain', subdomain)
|
||||
record.set('status', 'idle')
|
||||
record.set('version', versions[0])
|
||||
record.set('syncAdmin', true)
|
||||
record.set('notifyMaintenanceMode', true)
|
||||
|
||||
const form = new RecordUpsertForm($app, record)
|
||||
form.submit()
|
||||
|
||||
return c.json(200, { instance: record })
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
import { mkLog } from '$util/Logger'
|
||||
|
||||
export const HandleInstanceDelete = (c: echo.Context) => {
|
||||
const dao = $app.dao()
|
||||
|
||||
const log = mkLog(`DELETE:instance`)
|
||||
|
||||
log(`TOP OF DELETE`)
|
||||
let data = new DynamicModel({
|
||||
id: '',
|
||||
})
|
||||
|
||||
c.bind(data)
|
||||
log(`After bind`)
|
||||
|
||||
// This is necessary for destructuring to work correctly
|
||||
data = JSON.parse(JSON.stringify(data))
|
||||
|
||||
const id = c.pathParam('id')
|
||||
|
||||
log(
|
||||
`vars`,
|
||||
JSON.stringify({
|
||||
id,
|
||||
}),
|
||||
)
|
||||
|
||||
const authRecord = c.get('authRecord') as models.Record | undefined // empty if not authenticated as regular auth record
|
||||
log(`authRecord`, JSON.stringify(authRecord))
|
||||
|
||||
if (!authRecord) {
|
||||
throw new BadRequestError(`Expected authRecord here`)
|
||||
}
|
||||
|
||||
const record = dao.findRecordById('instances', id)
|
||||
if (!record) {
|
||||
throw new BadRequestError(`Instance ${id} not found.`)
|
||||
}
|
||||
if (record.get('uid') !== authRecord.id) {
|
||||
throw new BadRequestError(`Not authorized`)
|
||||
}
|
||||
|
||||
if (record.getString('status').toLowerCase() !== 'idle') {
|
||||
throw new BadRequestError(`Instance must be shut down first.`)
|
||||
}
|
||||
|
||||
const path = [$os.getenv('DATA_ROOT'), id].join('/')
|
||||
log(`path ${path}`)
|
||||
const res = $os.removeAll(path)
|
||||
log(`res`, res)
|
||||
|
||||
dao.deleteRecord(record)
|
||||
|
||||
return c.json(200, { status: 'ok' })
|
||||
}
|
||||
@ -0,0 +1,98 @@
|
||||
import { mkLog, StringKvLookup } from '$util/Logger'
|
||||
import { removeEmptyKeys } from '$util/removeEmptyKeys'
|
||||
|
||||
export const HandleInstanceUpdate = (c: echo.Context) => {
|
||||
const dao = $app.dao()
|
||||
const log = mkLog(`PUT:instance`)
|
||||
|
||||
log(`TOP OF PUT`)
|
||||
|
||||
let data = new DynamicModel({
|
||||
id: '',
|
||||
fields: {
|
||||
subdomain: null,
|
||||
maintenance: null,
|
||||
version: null,
|
||||
secrets: null,
|
||||
syncAdmin: null,
|
||||
dev: null,
|
||||
cname: null,
|
||||
notifyMaintenanceMode: null,
|
||||
},
|
||||
}) as {
|
||||
id: string
|
||||
fields: {
|
||||
subdomain: string | null
|
||||
maintenance: boolean | null
|
||||
version: string | null
|
||||
secrets: StringKvLookup | null
|
||||
syncAdmin: boolean | null
|
||||
dev: boolean | null
|
||||
cname: string | null
|
||||
notifyMaintenanceMode: boolean | null
|
||||
}
|
||||
}
|
||||
|
||||
c.bind(data)
|
||||
log(`After bind`)
|
||||
|
||||
// This is necessary for destructuring to work correctly
|
||||
data = JSON.parse(JSON.stringify(data))
|
||||
|
||||
const id = c.pathParam('id')
|
||||
const {
|
||||
fields: {
|
||||
subdomain,
|
||||
maintenance,
|
||||
version,
|
||||
secrets,
|
||||
syncAdmin,
|
||||
dev,
|
||||
cname,
|
||||
notifyMaintenanceMode,
|
||||
},
|
||||
} = data
|
||||
|
||||
log(
|
||||
`vars`,
|
||||
JSON.stringify({
|
||||
id,
|
||||
subdomain,
|
||||
maintenance,
|
||||
version,
|
||||
secrets,
|
||||
syncAdmin,
|
||||
dev,
|
||||
cname,
|
||||
notifyMaintenanceMode,
|
||||
}),
|
||||
)
|
||||
|
||||
const record = dao.findRecordById('instances', id)
|
||||
const authRecord = c.get('authRecord') as models.Record | undefined // empty if not authenticated as regular auth record
|
||||
log(`authRecord`, JSON.stringify(authRecord))
|
||||
|
||||
if (!authRecord) {
|
||||
throw new Error(`Expected authRecord here`)
|
||||
}
|
||||
if (record.get('uid') !== authRecord.id) {
|
||||
throw new BadRequestError(`Not authorized`)
|
||||
}
|
||||
|
||||
const sanitized = removeEmptyKeys({
|
||||
subdomain,
|
||||
version,
|
||||
maintenance,
|
||||
secrets,
|
||||
syncAdmin,
|
||||
dev,
|
||||
cname,
|
||||
notifyMaintenanceMode,
|
||||
})
|
||||
|
||||
const form = new RecordUpsertForm($app, record)
|
||||
form.loadData(sanitized)
|
||||
form.submit()
|
||||
|
||||
return c.json(200, { status: 'ok' })
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
export const HandleInstancesResetIdle = (e: core.BootstrapEvent) => {
|
||||
const dao = $app.dao()
|
||||
dao.db().newQuery(`update instances set status='idle'`).execute()
|
||||
}
|
||||
@ -1,14 +1,13 @@
|
||||
/** Migrate version numbers */
|
||||
onAfterBootstrap((e) => {
|
||||
import { mkLog } from '$util/Logger'
|
||||
import { versions } from '$util/versions'
|
||||
|
||||
export const HandleMigrateInstanceVersions = (e: core.BootstrapEvent) => {
|
||||
const dao = $app.dao()
|
||||
const { audit, mkLog, versions } = /** @type {Lib} */ (
|
||||
require(`${__hooks}/lib.js`)
|
||||
)
|
||||
|
||||
const log = mkLog(`bootstrap`)
|
||||
|
||||
const records = dao.findRecordsByFilter(`instances`, '1=1')
|
||||
const unrecognized = []
|
||||
const records = dao.findRecordsByFilter(`instances`, '1=1').filter((r) => !!r)
|
||||
const unrecognized: string[] = []
|
||||
records.forEach((record) => {
|
||||
const v = record.get('version').trim()
|
||||
if (versions.includes(v)) return
|
||||
@ -31,4 +30,4 @@ onAfterBootstrap((e) => {
|
||||
}
|
||||
})
|
||||
log({ unrecognized })
|
||||
})
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/** Migrate version numbers */
|
||||
onAfterBootstrap((e) => {
|
||||
export const HandleMigrateRegions = (e: core.BootstrapEvent) => {
|
||||
const dao = $app.dao()
|
||||
|
||||
console.log(`***Migrating regions`)
|
||||
@ -7,4 +7,4 @@ onAfterBootstrap((e) => {
|
||||
.db()
|
||||
.newQuery(`update instances set region='sfo-1' where region=''`)
|
||||
.execute()
|
||||
})
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
routerAdd(
|
||||
'PUT',
|
||||
'/api/instance/:id',
|
||||
(c) => {
|
||||
return require(`${__hooks}/mothership`).HandleInstanceUpdate(c)
|
||||
},
|
||||
$apis.requireRecordAuth(),
|
||||
)
|
||||
routerAdd(
|
||||
'POST',
|
||||
'/api/instance',
|
||||
(c) => {
|
||||
return require(`${__hooks}/mothership`).HandleInstanceCreate(c)
|
||||
},
|
||||
$apis.requireRecordAuth(),
|
||||
)
|
||||
routerAdd(
|
||||
'DELETE',
|
||||
'/api/instance/:id',
|
||||
(c) => {
|
||||
return require(`${__hooks}/mothership`).HandleInstanceDelete(c)
|
||||
},
|
||||
$apis.requireRecordAuth(),
|
||||
)
|
||||
/** Validate instance version */
|
||||
onModelBeforeCreate((e) => {
|
||||
return require(`${__hooks}/mothership`).HandleInstanceVersionValidation(e)
|
||||
}, 'instances')
|
||||
|
||||
onModelAfterUpdate((e) => {
|
||||
// return require(`${__hooks}/mothership`).HandleNotifyMaintenanceMode(e)
|
||||
}, 'instances')
|
||||
|
||||
onModelAfterCreate((e) => {
|
||||
// return require(`${__hooks}/mothership`).HandleNotifyDiscordAfterCreate(e)
|
||||
}, 'instances')
|
||||
|
||||
onAfterBootstrap((e) => {
|
||||
// return require(`${__hooks}/mothership`).HandleMigrateInstanceVersions(e)
|
||||
})
|
||||
|
||||
onAfterBootstrap((e) => {
|
||||
// return require(`${__hooks}/mothership`).HandleMigrateRegions(e)
|
||||
})
|
||||
|
||||
/** Reset instance status to idle on start */
|
||||
onAfterBootstrap((e) => {
|
||||
return require(`${__hooks}/mothership`).HandleInstancesResetIdle(e)
|
||||
})
|
||||
|
||||
/** Validate instance version */
|
||||
onModelBeforeUpdate((e) => {
|
||||
return require(`${__hooks}/mothership`).HandleInstanceBeforeUpdate(e)
|
||||
}, 'instances')
|
||||
@ -0,0 +1,10 @@
|
||||
export * from './api/HandleInstanceCreate'
|
||||
export * from './api/HandleInstanceDelete'
|
||||
export * from './api/HandleInstanceUpdate'
|
||||
export * from './bootstrap/HandleInstancesResetIdle'
|
||||
export * from './bootstrap/HandleMigrateInstanceVersions'
|
||||
export * from './bootstrap/HandleMigrateRegions'
|
||||
export * from './model/HandleInstanceBeforeUpdate'
|
||||
export * from './model/HandleInstanceVersionValidation'
|
||||
export * from './model/HandleNotifyDiscordAfterCreate'
|
||||
export * from './model/HandleNotifyMaintenanceMode'
|
||||
@ -1,9 +1,8 @@
|
||||
/// <reference path="../types/types.d.ts" />
|
||||
import { mkLog } from '$util/Logger'
|
||||
import { versions } from '$util/versions'
|
||||
|
||||
/** Validate instance version */
|
||||
onModelBeforeUpdate((e) => {
|
||||
export const HandleInstanceBeforeUpdate = (e: core.ModelEvent) => {
|
||||
const dao = e.dao || $app.dao()
|
||||
const { versions, mkLog } = /** @type {Lib} */ (require(`${__hooks}/lib.js`))
|
||||
|
||||
const log = mkLog(`instances-validate-before-update`)
|
||||
|
||||
@ -42,4 +41,4 @@ onModelBeforeUpdate((e) => {
|
||||
throw new BadRequestError(`Custom domain already in use.`)
|
||||
}
|
||||
}
|
||||
}, 'instances')
|
||||
}
|
||||
@ -1,9 +1,6 @@
|
||||
/// <reference path="../types/types.d.ts" />
|
||||
|
||||
/** Validate instance version */
|
||||
onModelBeforeCreate((e) => {
|
||||
const { versions } = /** @type {Lib} */ (require(`${__hooks}/lib.js`))
|
||||
import { versions } from '$util/versions'
|
||||
|
||||
export const HandleInstanceVersionValidation = (e: core.ModelEvent) => {
|
||||
const dao = e.dao || $app.dao()
|
||||
|
||||
const version = e.model.get('version')
|
||||
@ -14,4 +11,4 @@ onModelBeforeCreate((e) => {
|
||||
)}`,
|
||||
)
|
||||
}
|
||||
}, 'instances')
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
/// <reference path="../types/types.d.ts" />
|
||||
import { mkLog } from '$util/Logger'
|
||||
import { mkAudit } from '$util/mkAudit'
|
||||
|
||||
onModelAfterCreate((e) => {
|
||||
export const HandleNotifyDiscordAfterCreate = (e: core.ModelEvent) => {
|
||||
const dao = e.dao || $app.dao()
|
||||
const { mkAudit, mkLog } = /** @type {Lib} */ (require(`${__hooks}/lib.js`))
|
||||
|
||||
const log = mkLog(`instances:create:discord:notify`)
|
||||
const audit = mkAudit(log, dao)
|
||||
@ -26,4 +26,4 @@ onModelAfterCreate((e) => {
|
||||
} catch (e) {
|
||||
audit(`ERROR`, `Instance creation discord notify failed with ${e}`)
|
||||
}
|
||||
}, 'instances')
|
||||
}
|
||||
@ -1,14 +1,13 @@
|
||||
onModelAfterUpdate((e) => {
|
||||
return
|
||||
import { mkLog } from '$util/Logger'
|
||||
import { mkAudit } from '$util/mkAudit'
|
||||
import { mkNotifier } from '$util/mkNotifier'
|
||||
|
||||
export const HandleNotifyMaintenanceMode = (e: core.ModelEvent) => {
|
||||
const dao = e.dao || $app.dao()
|
||||
|
||||
const newModel = /** @type {models.Record} */ (e.model)
|
||||
const newModel = e.model as models.Record
|
||||
const oldModel = newModel.originalCopy()
|
||||
|
||||
const { mkLog, mkNotifier, mkAudit } = /** @type {Lib} */ (
|
||||
require(`${__hooks}/lib.js`)
|
||||
)
|
||||
|
||||
const log = mkLog(`maintenance-mode`)
|
||||
const audit = mkAudit(log, dao)
|
||||
const notify = mkNotifier(log, dao)
|
||||
@ -44,4 +43,4 @@ onModelAfterUpdate((e) => {
|
||||
} catch (e) {
|
||||
audit(`ERROR`, `Failed to enqueue template with ${e}`)
|
||||
}
|
||||
}, 'instances')
|
||||
}
|
||||
@ -1,16 +1,52 @@
|
||||
routerAdd('POST', '/api/ls', (c) => {
|
||||
import { mkLog } from '$util/Logger'
|
||||
import { mkAudit } from '$util/mkAudit'
|
||||
import { mkNotifier } from '$util/mkNotifier'
|
||||
import { PartialDeep } from 'type-fest'
|
||||
|
||||
type LemonSqueezyDebugContext = PartialDeep<{
|
||||
secret: string
|
||||
raw: string
|
||||
body_hash: string
|
||||
xsignature_header: string
|
||||
data: {
|
||||
data: {
|
||||
attributes: {
|
||||
product_id: string
|
||||
product_name: string
|
||||
first_order_item: {
|
||||
product_id: string
|
||||
product_name: string
|
||||
}
|
||||
}
|
||||
type: string
|
||||
}
|
||||
meta: {
|
||||
event_name: string
|
||||
custom_data: {
|
||||
user_id: string
|
||||
}
|
||||
}
|
||||
}
|
||||
type: string
|
||||
event_name: string
|
||||
user_id: string
|
||||
product_id: number
|
||||
product_name: string
|
||||
}>
|
||||
|
||||
export const HandleLemonSqueezySale = (c: echo.Context) => {
|
||||
const dao = $app.dao()
|
||||
const { mkAudit, mkLog, mkNotifier } = /** @type {Lib} */ (
|
||||
require(`${__hooks}/lib.js`)
|
||||
)
|
||||
|
||||
const log = mkLog(`ls`)
|
||||
const audit = mkAudit(log, dao)
|
||||
const context = {}
|
||||
const context: LemonSqueezyDebugContext = {}
|
||||
|
||||
log(`Top of ls`)
|
||||
try {
|
||||
context.secret = $os.getenv('LS_WEBHOOK_SECRET')
|
||||
context.secret = process.env.LS_WEBHOOK_SECRET
|
||||
if (!context.secret) {
|
||||
throw new Error(`No secret`)
|
||||
}
|
||||
log(`Secret`, context.secret)
|
||||
|
||||
context.raw = readerToString(c.request().body)
|
||||
@ -53,10 +89,11 @@ routerAdd('POST', '/api/ls', (c) => {
|
||||
log(`user ID ok`, context.user_id)
|
||||
}
|
||||
|
||||
context.product_id =
|
||||
context.type === 'orders'
|
||||
? context.data?.data?.attributes.first_order_item?.product_id
|
||||
: context.data?.data?.attributes?.product_id
|
||||
context.product_id = parseInt(
|
||||
(context.type === 'orders'
|
||||
? context.data?.data?.attributes?.first_order_item?.product_id
|
||||
: context.data?.data?.attributes?.product_id) || '0',
|
||||
)
|
||||
if (!context.product_id) {
|
||||
throw new Error(`No product ID`)
|
||||
} else {
|
||||
@ -65,7 +102,7 @@ routerAdd('POST', '/api/ls', (c) => {
|
||||
|
||||
context.product_name =
|
||||
context.type === 'orders'
|
||||
? context.data?.data?.attributes.first_order_item?.product_name
|
||||
? context.data?.data?.attributes?.first_order_item?.product_name
|
||||
: context.data?.data?.attributes?.product_name
|
||||
if (!context.product_name) {
|
||||
throw new Error(`No product name`)
|
||||
@ -77,7 +114,7 @@ routerAdd('POST', '/api/ls', (c) => {
|
||||
try {
|
||||
return dao.findFirstRecordByData('users', 'id', context.user_id)
|
||||
} catch (e) {
|
||||
throw new Error(`User ${user_id} not found`)
|
||||
throw new Error(`User ${context.user_id} not found`)
|
||||
}
|
||||
})()
|
||||
log(`user record ok`, userRec)
|
||||
@ -95,8 +132,9 @@ routerAdd('POST', '/api/ls', (c) => {
|
||||
subscription_expired: () => {
|
||||
signup_canceller()
|
||||
},
|
||||
}
|
||||
const event_handler = event_name_map[context.event_name]
|
||||
} as const
|
||||
const event_handler =
|
||||
event_name_map[context.event_name as keyof typeof event_name_map]
|
||||
if (!event_handler) {
|
||||
log(`unsupported event`, context.event_name)
|
||||
return c.json(200, {
|
||||
@ -149,8 +187,11 @@ routerAdd('POST', '/api/ls', (c) => {
|
||||
userRec.set(`subscription`, `flounder`)
|
||||
userRec.set(`subscription_interval`, `year`)
|
||||
},
|
||||
}
|
||||
const product_handler = product_handler_map[context.product_id]
|
||||
} as const
|
||||
const product_handler =
|
||||
product_handler_map[
|
||||
context.product_id as keyof typeof product_handler_map
|
||||
]
|
||||
if (!product_handler) {
|
||||
throw new Error(`No product handler for ${context.product_id}`)
|
||||
} else {
|
||||
@ -165,7 +206,11 @@ routerAdd('POST', '/api/ls', (c) => {
|
||||
log(`saved user`)
|
||||
|
||||
const notify = mkNotifier(log, txDao)
|
||||
notify(`lemonbot`, `lemon_order_discord`, context.user_id, context)
|
||||
const { user_id } = context
|
||||
if (!user_id) {
|
||||
throw new Error(`User ID expected here`)
|
||||
}
|
||||
notify(`lemonbot`, `lemon_order_discord`, user_id, context)
|
||||
log(`saved discord notice`)
|
||||
})
|
||||
log(`database updated`)
|
||||
@ -190,4 +235,4 @@ routerAdd('POST', '/api/ls', (c) => {
|
||||
audit(`LS_ERR`, `${e}`, context)
|
||||
return c.json(500, { status: `error`, error: e.message })
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
routerAdd('POST', '/api/ls', (c) => {
|
||||
return require(`${__hooks}/mothership`).HandleLemonSqueezySale(c)
|
||||
})
|
||||
@ -0,0 +1 @@
|
||||
export * from './api/HandleLemonSqueezySale'
|
||||
@ -0,0 +1,44 @@
|
||||
import { mkLog } from '$util/Logger'
|
||||
|
||||
export const HandleMailSend = (c: echo.Context) => {
|
||||
const log = mkLog(`mail`)
|
||||
|
||||
let data = new DynamicModel({
|
||||
to: '',
|
||||
subject: '',
|
||||
body: '',
|
||||
}) as { to: string; subject: string; body: string }
|
||||
|
||||
log(`before bind`)
|
||||
|
||||
c.bind(data)
|
||||
|
||||
log(`after bind`)
|
||||
|
||||
// This is necessary for destructuring to work correctly
|
||||
data = JSON.parse(JSON.stringify(data))
|
||||
|
||||
log(`bind parsed`, JSON.stringify(data))
|
||||
|
||||
const { to, subject, body } = data
|
||||
|
||||
const email = new MailerMessage({
|
||||
from: {
|
||||
address: $app.settings().meta.senderAddress,
|
||||
name: $app.settings().meta.senderName,
|
||||
},
|
||||
to: [{ address: to }],
|
||||
bcc: [process.env.TEST_EMAIL]
|
||||
.filter((e): e is string => !!e)
|
||||
.map((e) => ({ address: e })),
|
||||
subject: subject,
|
||||
html: body,
|
||||
})
|
||||
|
||||
$app.newMailClient().send(email)
|
||||
|
||||
const msg = `Sent to ${to}`
|
||||
log(msg)
|
||||
|
||||
return c.json(200, { status: 'ok' })
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
routerAdd(
|
||||
'POST',
|
||||
'/api/mail',
|
||||
(c) => {
|
||||
return require(`${__hooks}/mothership`).HandleMailSend(c)
|
||||
},
|
||||
$apis.requireAdminAuth(),
|
||||
)
|
||||
@ -0,0 +1 @@
|
||||
export * from './api/HandleMailSend'
|
||||
@ -0,0 +1,26 @@
|
||||
import { mkLog } from '$util/Logger'
|
||||
|
||||
export const HandleMetaUpdateAtBoot = (c: core.BootstrapEvent) => {
|
||||
const log = mkLog('HandleMetaUpdateAtBoot')
|
||||
log(`At top of HandleMetaUpdateAtBoot`)
|
||||
log(`***app URL`, process.env.APP_URL)
|
||||
const form = new SettingsUpsertForm($app)
|
||||
form.meta = {
|
||||
...$app.settings().meta,
|
||||
appUrl: process.env.APP_URL || $app.settings().meta.appUrl,
|
||||
verificationTemplate: {
|
||||
...$app.settings().meta.verificationTemplate,
|
||||
actionUrl: `{APP_URL}/login/confirm-account/{TOKEN}`,
|
||||
},
|
||||
resetPasswordTemplate: {
|
||||
...$app.settings().meta.resetPasswordTemplate,
|
||||
actionUrl: `{APP_URL}/login/password-reset/confirm/{TOKEN}`,
|
||||
},
|
||||
confirmEmailChangeTemplate: {
|
||||
...$app.settings().meta.confirmEmailChangeTemplate,
|
||||
actionUrl: `{APP_URL}/login/confirm-email-change/{TOKEN}`,
|
||||
},
|
||||
}
|
||||
log(`Saving form`)
|
||||
form.submit()
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
onAfterBootstrap((e) => {
|
||||
return require(`${__hooks}/mothership`).HandleMetaUpdateAtBoot(e)
|
||||
})
|
||||
@ -0,0 +1 @@
|
||||
export * from './boot/HandleMetaUpdateAtBoot'
|
||||
@ -0,0 +1,12 @@
|
||||
export const HandleMirrorData = (c: echo.Context) => {
|
||||
const users = $app.dao().findRecordsByExpr('verified_users', $dbx.exp('1=1'))
|
||||
|
||||
const instances = $app
|
||||
.dao()
|
||||
.findRecordsByExpr(
|
||||
'instances',
|
||||
$dbx.exp('instances.uid in (select id from verified_users)'),
|
||||
)
|
||||
|
||||
return c.json(200, { users, instances })
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
routerAdd(
|
||||
'GET',
|
||||
'/api/mirror',
|
||||
(c) => {
|
||||
return require(`${__hooks}/HandleMirrorData`).HandleMirrorData(c)
|
||||
},
|
||||
$apis.gzip(),
|
||||
$apis.requireAdminAuth(),
|
||||
)
|
||||
@ -0,0 +1 @@
|
||||
export * from './api/HandleMirrorData'
|
||||
@ -0,0 +1,30 @@
|
||||
import { mkLog } from '$util/Logger'
|
||||
import { mkNotificationProcessor } from '$util/mkNotificationProcessor'
|
||||
|
||||
export const HandleProcessSingleNotification = (c: echo.Context) => {
|
||||
const log = mkLog(`process_single_notification`)
|
||||
log(`start`)
|
||||
|
||||
const dao = $app.dao()
|
||||
|
||||
const processNotification = mkNotificationProcessor(
|
||||
log,
|
||||
dao,
|
||||
!!c.queryParam(`test`),
|
||||
)
|
||||
|
||||
try {
|
||||
const notification = dao.findFirstRecordByData(
|
||||
`notifications`,
|
||||
`delivered`,
|
||||
``,
|
||||
)
|
||||
if (!notification) {
|
||||
return c.json(200, `No notifications to send`)
|
||||
}
|
||||
processNotification(notification)
|
||||
} catch (e) {
|
||||
c.json(500, `${e}`)
|
||||
}
|
||||
return c.json(200, { status: 'ok' })
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
routerAdd(`GET`, `api/process_single_notification`, (c) => {
|
||||
return require(`${__hooks}/mothership`).HandleProcessSingleNotification(c)
|
||||
})
|
||||
|
||||
onModelAfterCreate((e) => {
|
||||
return require(`${__hooks}/mothership`).HandleProcessNotification(e)
|
||||
}, `notifications`)
|
||||
|
||||
onModelBeforeUpdate((e) => {
|
||||
return require(`${__hooks}/mothership`).HandleUserWelcomeMessage(e)
|
||||
}, 'users')
|
||||
@ -0,0 +1,2 @@
|
||||
export * from './api/HandleProcessSingleNotification'
|
||||
export * from './model/HandleUserWelcomeMessage'
|
||||
@ -0,0 +1,29 @@
|
||||
import { mkLog } from '$util/Logger'
|
||||
import { mkAudit } from '$util/mkAudit'
|
||||
import { mkNotificationProcessor } from '$util/mkNotificationProcessor'
|
||||
|
||||
export const HandleProcessNotification = (e: core.ModelEvent) => {
|
||||
const dao = e.dao || $app.dao()
|
||||
|
||||
const log = mkLog(`notification:sendImmediately`)
|
||||
const audit = mkAudit(log, dao)
|
||||
const processNotification = mkNotificationProcessor(log, dao, false)
|
||||
|
||||
const notificationRec = e.model as models.Record
|
||||
|
||||
log({ notificationRec })
|
||||
|
||||
try {
|
||||
dao.expandRecord(notificationRec, ['message_template'])
|
||||
|
||||
const messageTemplateRec = notificationRec.expandedOne(`message_template`)
|
||||
if (!messageTemplateRec) {
|
||||
throw new Error(`Missing message template`)
|
||||
}
|
||||
processNotification(notificationRec)
|
||||
} catch (e) {
|
||||
audit(`ERROR`, `${e}`, {
|
||||
notification: notificationRec.getId(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1,14 +1,12 @@
|
||||
/// <reference path="../types/types.d.ts" />
|
||||
import { mkLog } from '$util/Logger'
|
||||
import { mkAudit } from '$util/mkAudit'
|
||||
import { mkNotifier } from '$util/mkNotifier'
|
||||
|
||||
onModelBeforeUpdate((e) => {
|
||||
export const HandleUserWelcomeMessage = (e: core.ModelEvent) => {
|
||||
const dao = e.dao || $app.dao()
|
||||
const newModel = /** @type {models.Record} */ (e.model)
|
||||
const newModel = /** @type {models.Record} */ e.model
|
||||
const oldModel = newModel.originalCopy()
|
||||
|
||||
const { mkAudit, mkLog, mkNotifier } = /** @type {Lib} */ (
|
||||
require(`${__hooks}/lib.js`)
|
||||
)
|
||||
|
||||
const log = mkLog(`user-welcome-msg`)
|
||||
const notify = mkNotifier(log, dao)
|
||||
const audit = mkAudit(log, dao)
|
||||
@ -31,4 +29,4 @@ onModelBeforeUpdate((e) => {
|
||||
} catch (e) {
|
||||
audit(`ERROR`, `${e}`, { user: newModel.getId() })
|
||||
}
|
||||
}, 'users')
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
/// <reference path="../types/types.d.ts" />
|
||||
import { mkLog } from '$util/Logger'
|
||||
import { mkAudit } from '$util/mkAudit'
|
||||
|
||||
routerAdd('GET', '/api/unsubscribe', (c) => {
|
||||
export const HandleOutpostUnsubscribe = (c: echo.Context) => {
|
||||
const dao = $app.dao()
|
||||
const { mkLog, mkAudit } = /** @type {Lib} */ (require(`${__hooks}/lib.js`))
|
||||
const log = mkLog(`unsubscribe`)
|
||||
const audit = mkAudit(log, dao)
|
||||
|
||||
@ -31,4 +31,4 @@ routerAdd('GET', '/api/unsubscribe', (c) => {
|
||||
audit('UNSUBSCRIBE_ERR', `User ${id} not found`)
|
||||
return c.html(200, `<p>Looks like you're already unsubscribed.`)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
routerAdd('GET', '/api/unsubscribe', (c) => {
|
||||
return require(`${__hooks}/mothership`).HandleOutpostUnsubscribe(c)
|
||||
})
|
||||
@ -0,0 +1 @@
|
||||
export * from './api/HandleOutpostUnsubscribe'
|
||||
@ -0,0 +1,37 @@
|
||||
import { error } from '../error'
|
||||
import { isAvailable } from '../isAvailable'
|
||||
import { generate } from '../random-words'
|
||||
|
||||
export const HandleSignupCheck = (c: echo.Context) => {
|
||||
const instanceName = (() => {
|
||||
const name = c.queryParam('name').trim()
|
||||
if (name) {
|
||||
if (name.match(/^[a-z][a-z0-9-]{2,39}$/) === null) {
|
||||
throw error(
|
||||
`instanceName`,
|
||||
`invalid`,
|
||||
`Instance name must begin with a letter, be between 3-40 characters, and can only contain a-z, 0-9, and hyphen (-).`,
|
||||
)
|
||||
}
|
||||
if (isAvailable(name)) {
|
||||
return name
|
||||
}
|
||||
throw error(
|
||||
`instanceName`,
|
||||
`exists`,
|
||||
`Instance name ${name} is not available.`,
|
||||
)
|
||||
} else {
|
||||
let i = 0
|
||||
while (true) {
|
||||
i++
|
||||
if (i > 100) {
|
||||
return +new Date()
|
||||
}
|
||||
const slug = generate(2).join(`-`)
|
||||
if (isAvailable(slug)) return slug
|
||||
}
|
||||
}
|
||||
})()
|
||||
return c.json(200, { instanceName })
|
||||
}
|
||||
@ -0,0 +1,99 @@
|
||||
import { versions } from '$util/versions'
|
||||
import { error } from '../error'
|
||||
|
||||
export const HandleSignupConfirm = (c: echo.Context) => {
|
||||
const dao = $app.dao()
|
||||
const parsed = (() => {
|
||||
const rawBody = readerToString(c.request().body)
|
||||
try {
|
||||
const parsed = JSON.parse(rawBody)
|
||||
return parsed
|
||||
} catch (e) {
|
||||
throw new BadRequestError(
|
||||
`Error parsing payload. You call this JSON? ${rawBody}`,
|
||||
e,
|
||||
)
|
||||
}
|
||||
})()
|
||||
const email = parsed.email?.trim().toLowerCase()
|
||||
const password = parsed.password?.trim()
|
||||
const desiredInstanceName = parsed.instanceName?.trim()
|
||||
const region = parsed.region?.trim()
|
||||
|
||||
if (!email) {
|
||||
throw error(`email`, 'required', 'Email is required')
|
||||
}
|
||||
|
||||
if (!password) {
|
||||
throw error(`password`, `required`, 'Password is required')
|
||||
}
|
||||
|
||||
if (!desiredInstanceName) {
|
||||
throw error(`instanceName`, `required`, `Instance name is required`)
|
||||
}
|
||||
|
||||
const userExists = (() => {
|
||||
try {
|
||||
const record = dao.findFirstRecordByData('users', 'email', email)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
})()
|
||||
|
||||
if (userExists) {
|
||||
throw error(
|
||||
`email`,
|
||||
`exists`,
|
||||
`That user account already exists. Try a password reset.`,
|
||||
)
|
||||
}
|
||||
|
||||
dao.runInTransaction((txDao) => {
|
||||
const usersCollection = dao.findCollectionByNameOrId('users')
|
||||
const instanceCollection = $app.dao().findCollectionByNameOrId('instances')
|
||||
|
||||
const user = new Record(usersCollection)
|
||||
try {
|
||||
const username = $app
|
||||
.dao()
|
||||
.suggestUniqueAuthRecordUsername(
|
||||
'users',
|
||||
'user' + $security.randomStringWithAlphabet(5, '123456789'),
|
||||
)
|
||||
user.set('username', username)
|
||||
user.set('email', email)
|
||||
user.set('subscription', 'free')
|
||||
user.set('notifyMaintenanceMode', true)
|
||||
user.setPassword(password)
|
||||
txDao.saveRecord(user)
|
||||
} catch (e) {
|
||||
throw error(`email`, `fail`, `Could not create user: ${e}`)
|
||||
}
|
||||
|
||||
try {
|
||||
const instance = new Record(instanceCollection)
|
||||
instance.set('subdomain', desiredInstanceName)
|
||||
instance.set('region', region || `sfo-1`)
|
||||
instance.set('uid', user.get('id'))
|
||||
instance.set('status', 'idle')
|
||||
instance.set('notifyMaintenanceMode', true)
|
||||
instance.set('syncAdmin', true)
|
||||
instance.set('version', versions[0])
|
||||
txDao.saveRecord(instance)
|
||||
} catch (e) {
|
||||
if (`${e}`.match(/ UNIQUE /)) {
|
||||
throw error(
|
||||
`instanceName`,
|
||||
`exists`,
|
||||
`Instance name was taken, sorry about that. Try another.`,
|
||||
)
|
||||
}
|
||||
throw error(`instanceName`, `fail`, `Could not create instance: ${e}`)
|
||||
}
|
||||
|
||||
$mails.sendRecordVerification($app, user)
|
||||
})
|
||||
|
||||
return c.json(200, { status: 'ok' })
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
import { StringKvLookup } from '$util/Logger'
|
||||
|
||||
export const error = (
|
||||
fieldName: string,
|
||||
slug: string,
|
||||
description: string,
|
||||
extra?: StringKvLookup,
|
||||
) =>
|
||||
new ApiError(500, description, {
|
||||
[fieldName]: new ValidationError(slug, description),
|
||||
...extra,
|
||||
})
|
||||
@ -0,0 +1,8 @@
|
||||
routerAdd('GET', '/api/signup', (c) => {
|
||||
return require(`${__hooks}/mothership`).HandleSignupCheck(c)
|
||||
})
|
||||
|
||||
// https://pocketbase.io/docs/js-routing/#sending-request-to-custom-routes-using-the-sdks
|
||||
routerAdd('POST', '/api/signup', (c) => {
|
||||
return require(`${__hooks}/mothership`).HandleSignupConfirm(c)
|
||||
})
|
||||
@ -0,0 +1,2 @@
|
||||
export * from './api/HandleSignupCheck'
|
||||
export * from './api/HandleSignupConfirm'
|
||||
@ -0,0 +1,10 @@
|
||||
export const isAvailable = (slug: string) => {
|
||||
try {
|
||||
const record = $app
|
||||
.dao()
|
||||
.findFirstRecordByData('instances', 'subdomain', slug)
|
||||
return false
|
||||
} catch {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,125 @@
|
||||
import { wordList } from './wordList'
|
||||
|
||||
const shortestWordSize = wordList.reduce((shortestWord, currentWord) =>
|
||||
currentWord.length < shortestWord.length ? currentWord : shortestWord,
|
||||
).length
|
||||
|
||||
const longestWordSize = wordList.reduce((longestWord, currentWord) =>
|
||||
currentWord.length > longestWord.length ? currentWord : longestWord,
|
||||
).length
|
||||
|
||||
export function generate(options: any) {
|
||||
// initalize random number generator for words if options.seed is provided
|
||||
|
||||
const { minLength, maxLength, ...rest } = options || {}
|
||||
|
||||
function word() {
|
||||
let min =
|
||||
typeof minLength !== 'number'
|
||||
? shortestWordSize
|
||||
: limitWordSize(minLength)
|
||||
|
||||
const max =
|
||||
typeof maxLength !== 'number' ? longestWordSize : limitWordSize(maxLength)
|
||||
|
||||
if (min > max) min = max
|
||||
|
||||
let rightSize = false
|
||||
let wordUsed
|
||||
while (!rightSize) {
|
||||
wordUsed = generateRandomWord()
|
||||
rightSize = wordUsed.length <= max && wordUsed.length >= min
|
||||
}
|
||||
return wordUsed
|
||||
}
|
||||
|
||||
function generateRandomWord() {
|
||||
return wordList[randInt(wordList.length)]!
|
||||
}
|
||||
|
||||
// limits the size of words to the minimum and maximum possible
|
||||
function limitWordSize(wordSize: number) {
|
||||
if (wordSize < shortestWordSize) wordSize = shortestWordSize
|
||||
if (wordSize > longestWordSize) wordSize = longestWordSize
|
||||
return wordSize
|
||||
}
|
||||
|
||||
// random int as seeded by options.seed if applicable, or Math.random() otherwise
|
||||
function randInt(lessThan: number) {
|
||||
const r = Math.random()
|
||||
return Math.floor(r * lessThan)
|
||||
}
|
||||
|
||||
// No arguments = generate one word
|
||||
if (options === undefined) {
|
||||
return word()
|
||||
}
|
||||
|
||||
// Just a number = return that many words
|
||||
if (typeof options === 'number') {
|
||||
options = { exactly: options }
|
||||
} else if (Object.keys(rest).length === 0) {
|
||||
return word()
|
||||
}
|
||||
|
||||
// options supported: exactly, min, max, join
|
||||
if (options.exactly) {
|
||||
options.min = options.exactly
|
||||
options.max = options.exactly
|
||||
}
|
||||
|
||||
// not a number = one word par string
|
||||
if (typeof options.wordsPerString !== 'number') {
|
||||
options.wordsPerString = 1
|
||||
}
|
||||
|
||||
//not a function = returns the raw word
|
||||
if (typeof options.formatter !== 'function') {
|
||||
options.formatter = (word: string) => word
|
||||
}
|
||||
|
||||
//not a string = separator is a space
|
||||
if (typeof options.separator !== 'string') {
|
||||
options.separator = ' '
|
||||
}
|
||||
|
||||
const total = options.min + randInt(options.max + 1 - options.min)
|
||||
let results: any = []
|
||||
let token = ''
|
||||
let relativeIndex = 0
|
||||
|
||||
for (let i = 0; i < total * options.wordsPerString; i++) {
|
||||
if (relativeIndex === options.wordsPerString - 1) {
|
||||
token += options.formatter(word(), relativeIndex)
|
||||
} else {
|
||||
token += options.formatter(word(), relativeIndex) + options.separator
|
||||
}
|
||||
relativeIndex++
|
||||
if ((i + 1) % options.wordsPerString === 0) {
|
||||
results.push(token)
|
||||
token = ''
|
||||
relativeIndex = 0
|
||||
}
|
||||
}
|
||||
if (typeof options.join === 'string') {
|
||||
results = results.join(options.join)
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
export function count(options: any) {
|
||||
let { minLength, maxLength } = options || {}
|
||||
|
||||
if (typeof minLength !== 'number') {
|
||||
minLength = shortestWordSize
|
||||
}
|
||||
|
||||
if (typeof maxLength !== 'number') {
|
||||
maxLength = longestWordSize
|
||||
}
|
||||
|
||||
return wordList.filter(
|
||||
(word) => word.length >= minLength && word.length <= maxLength,
|
||||
).length
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
const wordList = [
|
||||
export const wordList = [
|
||||
'ability',
|
||||
'able',
|
||||
'aboard',
|
||||
@ -1952,134 +1952,3 @@ const wordList = [
|
||||
'zoo',
|
||||
'zulu',
|
||||
]
|
||||
|
||||
const shortestWordSize = wordList.reduce((shortestWord, currentWord) =>
|
||||
currentWord.length < shortestWord.length ? currentWord : shortestWord,
|
||||
).length
|
||||
|
||||
const longestWordSize = wordList.reduce((longestWord, currentWord) =>
|
||||
currentWord.length > longestWord.length ? currentWord : longestWord,
|
||||
).length
|
||||
|
||||
function generate(options) {
|
||||
// initalize random number generator for words if options.seed is provided
|
||||
const random = options?.seed ? new seedrandom(options.seed) : null
|
||||
|
||||
const { minLength, maxLength, ...rest } = options || {}
|
||||
|
||||
function word() {
|
||||
let min =
|
||||
typeof minLength !== 'number'
|
||||
? shortestWordSize
|
||||
: limitWordSize(minLength)
|
||||
|
||||
const max =
|
||||
typeof maxLength !== 'number' ? longestWordSize : limitWordSize(maxLength)
|
||||
|
||||
if (min > max) min = max
|
||||
|
||||
let rightSize = false
|
||||
let wordUsed
|
||||
while (!rightSize) {
|
||||
wordUsed = generateRandomWord()
|
||||
rightSize = wordUsed.length <= max && wordUsed.length >= min
|
||||
}
|
||||
return wordUsed
|
||||
}
|
||||
|
||||
function generateRandomWord() {
|
||||
return wordList[randInt(wordList.length)]
|
||||
}
|
||||
|
||||
// limits the size of words to the minimum and maximum possible
|
||||
function limitWordSize(wordSize) {
|
||||
if (wordSize < shortestWordSize) wordSize = shortestWordSize
|
||||
if (wordSize > longestWordSize) wordSize = longestWordSize
|
||||
return wordSize
|
||||
}
|
||||
|
||||
// random int as seeded by options.seed if applicable, or Math.random() otherwise
|
||||
function randInt(lessThan) {
|
||||
const r = random ? random() : Math.random()
|
||||
return Math.floor(r * lessThan)
|
||||
}
|
||||
|
||||
// No arguments = generate one word
|
||||
if (options === undefined) {
|
||||
return word()
|
||||
}
|
||||
|
||||
// Just a number = return that many words
|
||||
if (typeof options === 'number') {
|
||||
options = { exactly: options }
|
||||
} else if (Object.keys(rest).length === 0) {
|
||||
return word()
|
||||
}
|
||||
|
||||
// options supported: exactly, min, max, join
|
||||
if (options.exactly) {
|
||||
options.min = options.exactly
|
||||
options.max = options.exactly
|
||||
}
|
||||
|
||||
// not a number = one word par string
|
||||
if (typeof options.wordsPerString !== 'number') {
|
||||
options.wordsPerString = 1
|
||||
}
|
||||
|
||||
//not a function = returns the raw word
|
||||
if (typeof options.formatter !== 'function') {
|
||||
options.formatter = (word) => word
|
||||
}
|
||||
|
||||
//not a string = separator is a space
|
||||
if (typeof options.separator !== 'string') {
|
||||
options.separator = ' '
|
||||
}
|
||||
|
||||
const total = options.min + randInt(options.max + 1 - options.min)
|
||||
/** @type {string[] | string} */
|
||||
let results = []
|
||||
let token = ''
|
||||
let relativeIndex = 0
|
||||
|
||||
for (let i = 0; i < total * options.wordsPerString; i++) {
|
||||
if (relativeIndex === options.wordsPerString - 1) {
|
||||
token += options.formatter(word(), relativeIndex)
|
||||
} else {
|
||||
token += options.formatter(word(), relativeIndex) + options.separator
|
||||
}
|
||||
relativeIndex++
|
||||
if ((i + 1) % options.wordsPerString === 0) {
|
||||
results.push(token)
|
||||
token = ''
|
||||
relativeIndex = 0
|
||||
}
|
||||
}
|
||||
if (typeof options.join === 'string') {
|
||||
results = results.join(options.join)
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
function count(options) {
|
||||
let { minLength, maxLength } = options || {}
|
||||
|
||||
if (typeof minLength !== 'number') {
|
||||
minLength = shortestWordSize
|
||||
}
|
||||
|
||||
if (typeof maxLength !== 'number') {
|
||||
maxLength = longestWordSize
|
||||
}
|
||||
|
||||
return wordList.filter(
|
||||
(word) => word.length >= minLength && word.length <= maxLength,
|
||||
).length
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
count,
|
||||
generate,
|
||||
}
|
||||
@ -0,0 +1,149 @@
|
||||
import { mkLog } from '$util/Logger'
|
||||
import { mkAudit } from '$util/mkAudit'
|
||||
|
||||
type SnsSubscriptionConfirmationEvent = {
|
||||
Type: 'SubscriptionConfirmation'
|
||||
SubscribeURL: string
|
||||
}
|
||||
|
||||
type SnsNotificationEvent = {
|
||||
Type: 'Notification'
|
||||
Message: string
|
||||
}
|
||||
|
||||
type SnsNotificationBouncePayload = {
|
||||
notificationType: 'Bounce'
|
||||
bounce: {
|
||||
bounceType: 'Permanent'
|
||||
bouncedRecipients: { emailAddress: string }[]
|
||||
}
|
||||
}
|
||||
|
||||
type SnsNotificationComplaintPayload = {
|
||||
notificationType: 'Complaint'
|
||||
complaint: {
|
||||
complainedRecipients: { emailAddress: string }[]
|
||||
}
|
||||
}
|
||||
|
||||
type SnsNotificationPayload =
|
||||
| SnsNotificationBouncePayload
|
||||
| SnsNotificationComplaintPayload
|
||||
|
||||
type SnsEvent = SnsSubscriptionConfirmationEvent | SnsNotificationEvent
|
||||
|
||||
function isSnsSubscriptionConfirmationEvent(
|
||||
event: SnsEvent,
|
||||
): event is SnsSubscriptionConfirmationEvent {
|
||||
return event.Type === 'SubscriptionConfirmation'
|
||||
}
|
||||
|
||||
function isSnsNotificationEvent(
|
||||
event: SnsEvent,
|
||||
): event is SnsNotificationEvent {
|
||||
return event.Type === 'Notification'
|
||||
}
|
||||
|
||||
function isSnsNotificationBouncePayload(
|
||||
payload: SnsNotificationPayload,
|
||||
): payload is SnsNotificationBouncePayload {
|
||||
return payload.notificationType === 'Bounce'
|
||||
}
|
||||
|
||||
function isSnsNotificationComplaintPayload(
|
||||
payload: SnsNotificationPayload,
|
||||
): payload is SnsNotificationComplaintPayload {
|
||||
return payload.notificationType === 'Complaint'
|
||||
}
|
||||
|
||||
export const HandleSesError = (c: echo.Context) => {
|
||||
const dao = $app.dao()
|
||||
const log = mkLog(`sns`)
|
||||
const audit = mkAudit(log, dao)
|
||||
|
||||
const processBounce = (emailAddress: string) => {
|
||||
log(`Processing ${emailAddress}`)
|
||||
const extra = {
|
||||
email: emailAddress,
|
||||
} as { email: string; user: string }
|
||||
try {
|
||||
const user = dao.findFirstRecordByData('users', 'email', emailAddress)
|
||||
log(`user is`, user)
|
||||
extra.user = user.getId()
|
||||
user.setVerified(false)
|
||||
dao.saveRecord(user)
|
||||
audit('PBOUNCE', `User ${emailAddress} has been disabled`, extra)
|
||||
} catch (e) {
|
||||
audit('PBOUNCE_ERR', `${e}`, extra)
|
||||
}
|
||||
}
|
||||
|
||||
const raw = readerToString(c.request().body)
|
||||
const data = JSON.parse(raw) as SnsEvent
|
||||
log(JSON.stringify(data, null, 2))
|
||||
|
||||
if (isSnsSubscriptionConfirmationEvent(data)) {
|
||||
const url = data.SubscribeURL
|
||||
log(url)
|
||||
$http.send({ url })
|
||||
return c.json(200, { status: 'ok' })
|
||||
}
|
||||
|
||||
if (isSnsNotificationEvent(data)) {
|
||||
const msg = JSON.parse(data.Message) as SnsNotificationPayload
|
||||
log(msg)
|
||||
|
||||
if (isSnsNotificationBouncePayload(msg)) {
|
||||
log(`Message is a bounce`)
|
||||
const { bounce } = msg
|
||||
const { bounceType } = bounce
|
||||
switch (bounceType) {
|
||||
case `Permanent`:
|
||||
log(`Message is a permanent bounce`)
|
||||
const { bouncedRecipients } = bounce
|
||||
bouncedRecipients.forEach((recipient) => {
|
||||
const { emailAddress } = recipient
|
||||
processBounce(emailAddress)
|
||||
})
|
||||
break
|
||||
default:
|
||||
audit('SNS_ERR', `Unrecognized bounce type ${bounceType}`, {
|
||||
raw,
|
||||
})
|
||||
}
|
||||
} else if (isSnsNotificationComplaintPayload(msg)) {
|
||||
log(`Message is a Complaint`, msg)
|
||||
const { complaint } = msg
|
||||
const { complainedRecipients } = complaint
|
||||
complainedRecipients.forEach((recipient) => {
|
||||
const { emailAddress } = recipient
|
||||
log(`Processing ${emailAddress}`)
|
||||
try {
|
||||
const user = $app
|
||||
.dao()
|
||||
.findFirstRecordByData('users', 'email', emailAddress)
|
||||
log(`user is`, user)
|
||||
user.set(`unsubscribe`, true)
|
||||
dao.saveRecord(user)
|
||||
audit('COMPLAINT', `User ${emailAddress} has been unsubscribed`, {
|
||||
emailAddress,
|
||||
user: user.getId(),
|
||||
})
|
||||
} catch (e) {
|
||||
audit('COMPLAINT_ERR', `${emailAddress} is not in the system.`, {
|
||||
emailAddress,
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
audit('SNS_ERR', `Unrecognized notification type ${data.Type}`, {
|
||||
raw,
|
||||
})
|
||||
}
|
||||
}
|
||||
audit(`SNS_ERR`, `Message ${data.Type} not handled`, {
|
||||
raw,
|
||||
})
|
||||
|
||||
return c.json(200, { status: 'ok' })
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
routerAdd('POST', '/api/sns', (c) => {
|
||||
return require(`${__hooks}/mothership`).HandleSesError(c)
|
||||
})
|
||||
@ -0,0 +1 @@
|
||||
export * from './api/HandleSesError'
|
||||
@ -0,0 +1,9 @@
|
||||
export const HandleStatsRequest = (c: echo.Context) => {
|
||||
const result = new DynamicModel({
|
||||
total_flounder_subscribers: 0,
|
||||
})
|
||||
|
||||
$app.dao().db().select('total_flounder_subscribers').from('stats').one(result)
|
||||
|
||||
return c.json(200, result)
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
routerAdd('GET', '/api/stats', (c) => {
|
||||
return require(`${__hooks}/mothership`).HandleStatsRequest(c)
|
||||
})
|
||||
@ -0,0 +1 @@
|
||||
export * from './api/HandleStatsRequest'
|
||||
@ -0,0 +1,22 @@
|
||||
import { mkLog } from '$util/Logger'
|
||||
|
||||
export const HandleUserTokenRequest = (c: echo.Context) => {
|
||||
const dao = $app.dao()
|
||||
const log = mkLog(`user-token`)
|
||||
|
||||
const id = c.pathParam('id')
|
||||
|
||||
// log({ id })
|
||||
|
||||
if (!id) {
|
||||
throw new BadRequestError(`User ID is required.`)
|
||||
}
|
||||
|
||||
const rec = dao.findRecordById('users', id)
|
||||
const tokenKey = rec.getString('tokenKey')
|
||||
const passwordHash = rec.getString('passwordHash')
|
||||
const email = rec.getString(`email`)
|
||||
// log({ email, passwordHash, tokenKey })
|
||||
|
||||
return c.json(200, { email, passwordHash, tokenKey })
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
routerAdd(
|
||||
'GET',
|
||||
'/api/userToken/:id',
|
||||
(c) => {
|
||||
return require(`${__hooks}/mothership`).HandleUserTokenRequest(c)
|
||||
},
|
||||
$apis.requireAdminAuth(),
|
||||
)
|
||||
@ -0,0 +1 @@
|
||||
export * from './api/HandleUserTokenRequest'
|
||||
@ -0,0 +1,6 @@
|
||||
import { versions } from '$util/versions'
|
||||
|
||||
/** Return a list of available PocketBase versions */
|
||||
export const HandleVersionsRequest = (c: echo.Context) => {
|
||||
return c.json(200, { versions })
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
/** Return a list of available PocketBase versions */
|
||||
routerAdd('GET', '/api/versions', (c) => {
|
||||
return require(`${__hooks}/mothership`).HandleVersionsRequest(c)
|
||||
})
|
||||
@ -0,0 +1 @@
|
||||
export * from './api/HandleVersionsRequest'
|
||||
1
packages/pockethost/src/mothership-app/src/lib/index.ts
Normal file
1
packages/pockethost/src/mothership-app/src/lib/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './handlers'
|
||||
@ -0,0 +1,24 @@
|
||||
export type Logger = (...args: any[]) => void
|
||||
|
||||
export type StringKvLookup = { [_: string]: string }
|
||||
|
||||
export const mkLog =
|
||||
(namespace: string) =>
|
||||
(...s: any[]) =>
|
||||
console.log(
|
||||
`[${namespace}]`,
|
||||
...s.map((p) => {
|
||||
if (typeof p === 'object') return JSON.stringify(p, null, 2)
|
||||
return p
|
||||
}),
|
||||
)
|
||||
|
||||
export const dbg = (...args: any[]) => console.log(args)
|
||||
|
||||
export const interpolateString = (template: string, dict: StringKvLookup) => {
|
||||
return template.replace(/\{\$(\w+)\}/g, (match, key) => {
|
||||
dbg({ match, key })
|
||||
const lowerKey = key.toLowerCase()
|
||||
return dict.hasOwnProperty(lowerKey) ? dict[lowerKey] || '' : match
|
||||
})
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
import { Logger } from './Logger'
|
||||
|
||||
type AuditEvents =
|
||||
| 'ERROR'
|
||||
| 'NOTIFICATION_ERR'
|
||||
| 'LS'
|
||||
| 'LS_ERR'
|
||||
| 'PBOUNCE'
|
||||
| 'PBOUNCE_ERR'
|
||||
| 'SNS_ERR'
|
||||
| 'COMPLAINT'
|
||||
| 'COMPLAINT_ERR'
|
||||
| 'UNSUBSCRIBE'
|
||||
| 'UNSUBSCRIBE_ERR'
|
||||
|
||||
export const mkAudit = (
|
||||
log: Logger,
|
||||
dao: daos.Dao,
|
||||
): ((
|
||||
event: AuditEvents,
|
||||
note: string,
|
||||
context?: { [_: string]: any },
|
||||
) => void) => {
|
||||
return (event, note, context) => {
|
||||
log(`top of audit`)
|
||||
log(`AUDIT:${event}: ${note}`, JSON.stringify({ context }, null, 2))
|
||||
dao.saveRecord(
|
||||
new Record(dao.findCollectionByNameOrId('audit'), {
|
||||
event,
|
||||
note,
|
||||
context,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
import { Logger, interpolateString } from './Logger'
|
||||
|
||||
export const mkNotificationProcessor =
|
||||
(log: Logger, dao: daos.Dao, test = false) =>
|
||||
(notificationRec: models.Record) => {
|
||||
log({ notificationRec })
|
||||
|
||||
const channel = notificationRec.getString(`channel`)
|
||||
dao.expandRecord(notificationRec, ['message_template', 'user'])
|
||||
|
||||
const messageTemplateRec = notificationRec.expandedOne('message_template')
|
||||
if (!messageTemplateRec) {
|
||||
throw new Error(`Missing message template`)
|
||||
}
|
||||
const userRec = notificationRec.expandedOne('user')
|
||||
if (!userRec) {
|
||||
throw new Error(`Missing user record`)
|
||||
}
|
||||
const vars = JSON.parse(notificationRec.getString(`message_template_vars`))
|
||||
const to = userRec.email()
|
||||
const subject = interpolateString(
|
||||
messageTemplateRec.getString(`subject`),
|
||||
vars,
|
||||
)
|
||||
const html = interpolateString(
|
||||
messageTemplateRec.getString(`body_html`),
|
||||
vars,
|
||||
)
|
||||
|
||||
log({ channel, messageTemplateRec, userRec, vars, to, subject, html })
|
||||
switch (channel) {
|
||||
case `email`:
|
||||
/** @type {Partial<mailer.Message_In>} */
|
||||
const msgArgs = {
|
||||
from: {
|
||||
address: $app.settings().meta.senderAddress,
|
||||
name: $app.settings().meta.senderName,
|
||||
},
|
||||
to: [{ address: to }],
|
||||
bcc: [{ address: `pockethost+notifications@benallfree.com` }],
|
||||
subject,
|
||||
html,
|
||||
}
|
||||
if (test || true) {
|
||||
msgArgs.to = [{ address: `ben@benallfree.com` }]
|
||||
msgArgs.subject = `***TEST ${to} *** ${msgArgs.subject}`
|
||||
}
|
||||
log({ msgArgs })
|
||||
// @ts-ignore
|
||||
const msg = new MailerMessage(msgArgs)
|
||||
$app.newMailClient().send(msg)
|
||||
log(`email sent`)
|
||||
break
|
||||
|
||||
case `lemonbot`:
|
||||
const url = test
|
||||
? process.env.DISCORD_TEST_CHANNEL_URL
|
||||
: process.env.DISCORD_STREAM_CHANNEL_URL
|
||||
if (url) {
|
||||
const params: HttpSendConfig = {
|
||||
url,
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
content: subject,
|
||||
}),
|
||||
headers: { 'content-type': 'application/json' },
|
||||
timeout: 5, // in seconds
|
||||
}
|
||||
log(`sending discord message`, params)
|
||||
const res = $http.send(params)
|
||||
log(`discord sent`, res)
|
||||
}
|
||||
|
||||
break
|
||||
default:
|
||||
throw new Error(`Unsupported channel: ${channel}`)
|
||||
}
|
||||
if (!test) {
|
||||
notificationRec.set(`delivered`, new DateTime())
|
||||
dao.saveRecord(notificationRec)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
import { Logger } from './Logger'
|
||||
|
||||
export const mkNotifier =
|
||||
(log: Logger, dao: daos.Dao) =>
|
||||
<TContext extends Record<string, any>>(
|
||||
channel: 'email' | 'lemonbot',
|
||||
template: string,
|
||||
user_id: string,
|
||||
context: TContext = {} as TContext,
|
||||
) => {
|
||||
log({ channel, template, user_id })
|
||||
const emailTemplate = dao.findFirstRecordByData(
|
||||
'message_templates',
|
||||
`slug`,
|
||||
template,
|
||||
)
|
||||
log(`got email template`, emailTemplate)
|
||||
if (!emailTemplate) throw new Error(`Template ${template} not found`)
|
||||
const emailNotification = new Record(
|
||||
dao.findCollectionByNameOrId('notifications'),
|
||||
{
|
||||
user: user_id,
|
||||
channel,
|
||||
message_template: emailTemplate.getId(),
|
||||
message_template_vars: context,
|
||||
},
|
||||
)
|
||||
log(`built notification record`, emailNotification)
|
||||
dao.saveRecord(emailNotification)
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
import { reduce } from '@s-libs/micro-dash'
|
||||
|
||||
export const removeEmptyKeys = <T extends Record<string, unknown>>(
|
||||
obj: T,
|
||||
): T => {
|
||||
const sanitized = reduce(
|
||||
obj,
|
||||
(acc, value, key) => {
|
||||
if (value !== null && value !== undefined) {
|
||||
acc[key] = value
|
||||
}
|
||||
return acc
|
||||
},
|
||||
{} as T,
|
||||
)
|
||||
return sanitized
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
export const versions = require(`${__hooks}/versions.cjs`)
|
||||
@ -357,7 +357,7 @@ interface MailerMessage extends mailer.Message{} // merge
|
||||
* @group PocketBase
|
||||
*/
|
||||
declare class MailerMessage implements mailer.Message {
|
||||
constructor(message?: Partial<mailer.Message>)
|
||||
constructor(message?: Partial<mailer.Message_Create> )
|
||||
}
|
||||
|
||||
interface Command extends cobra.Command{} // merge
|
||||
@ -994,6 +994,16 @@ interface FormData {
|
||||
set(key:string, value:any): void
|
||||
}
|
||||
|
||||
interface HttpSendConfig {
|
||||
url: string,
|
||||
body?: string|FormData,
|
||||
method?: string, // default to "GET"
|
||||
headers?: { [key:string]: string },
|
||||
timeout?: number, // default to 120
|
||||
|
||||
// deprecated, please use body instead
|
||||
data?: { [key:string]: any },
|
||||
}
|
||||
/**
|
||||
* `$http` defines common methods for working with HTTP requests.
|
||||
*
|
||||
@ -1019,16 +1029,7 @@ declare namespace $http {
|
||||
* console.log(res.json) // the response body as parsed json array or map
|
||||
* ```
|
||||
*/
|
||||
function send(config: {
|
||||
url: string,
|
||||
body?: string|FormData,
|
||||
method?: string, // default to "GET"
|
||||
headers?: { [key:string]: string },
|
||||
timeout?: number, // default to 120
|
||||
|
||||
// deprecated, please use body instead
|
||||
data?: { [key:string]: any },
|
||||
}): {
|
||||
function send(config: HttpSendConfig): {
|
||||
statusCode: number,
|
||||
headers: { [key:string]: Array<string> },
|
||||
cookies: { [key:string]: http.Cookie },
|
||||
@ -13420,7 +13421,7 @@ namespace daos {
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
findRecordsByFilter(collectionNameOrId: string, filter: string, sort: string, limit: number, offset: number, ...params: dbx.Params[]): Array<(models.Record | undefined)>
|
||||
findRecordsByFilter(collectionNameOrId: string, filter: string, sort?: string, limit?: number, offset?: number, ...params: dbx.Params[]): Array<(models.Record | undefined)>
|
||||
}
|
||||
interface Dao {
|
||||
/**
|
||||
@ -13539,7 +13540,7 @@ namespace daos {
|
||||
*
|
||||
* Returns a map with the failed expand parameters and their errors.
|
||||
*/
|
||||
expandRecord(record: models.Record, expands: Array<string>, optFetchFunc: ExpandFetchFunc): _TygojaDict
|
||||
expandRecord(record: models.Record, expands: Array<string>, optFetchFunc?: ExpandFetchFunc): _TygojaDict
|
||||
}
|
||||
interface Dao {
|
||||
/**
|
||||
@ -18530,6 +18531,19 @@ namespace mailer {
|
||||
headers: _TygojaDict
|
||||
attachments: _TygojaDict
|
||||
}
|
||||
|
||||
|
||||
interface Message_Create {
|
||||
from: mail.Address_Create
|
||||
to: Array<mail.Address_Create>
|
||||
bcc: Array<mail.Address_Create>
|
||||
cc: Array<mail.Address_Create>
|
||||
subject: string
|
||||
html: string
|
||||
text: string
|
||||
headers: _TygojaDict
|
||||
attachments: _TygojaDict
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -19621,11 +19635,11 @@ namespace mail {
|
||||
* An address such as "Barry Gibbs <bg@example.com>" is represented
|
||||
* as Address{Name: "Barry Gibbs", Address: "bg@example.com"}.
|
||||
*/
|
||||
interface Address {
|
||||
name: string // Proper name; may be empty.
|
||||
interface Address_Create {
|
||||
name?: string // Proper name; may be empty.
|
||||
address: string // user@domain
|
||||
}
|
||||
interface Address {
|
||||
interface Address extends Address_Create {
|
||||
/**
|
||||
* String formats the address as a valid RFC 5322 address.
|
||||
* If the address's name contains non-ASCII characters
|
||||
16
packages/pockethost/src/mothership-app/tsconfig.json
Normal file
16
packages/pockethost/src/mothership-app/tsconfig.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": false,
|
||||
"strictNullChecks": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitAny": true,
|
||||
"target": "ES6",
|
||||
"module": "CommonJS",
|
||||
"moduleResolution": "node",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"$util/*": ["src/lib/util/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/lib/random-words.js"]
|
||||
}
|
||||
19
packages/pockethost/src/mothership-app/tsup.config.ts
Normal file
19
packages/pockethost/src/mothership-app/tsup.config.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { defineConfig } from 'tsup'
|
||||
|
||||
export default defineConfig({
|
||||
entry: {
|
||||
'mothership.pb': 'src/hooks/index.ts',
|
||||
mothership: 'src/lib/index.ts',
|
||||
},
|
||||
format: ['cjs'],
|
||||
dts: false,
|
||||
clean: false,
|
||||
outDir: 'pb_hooks',
|
||||
shims: true,
|
||||
skipNodeModulesBundle: false,
|
||||
target: 'node20',
|
||||
platform: 'node',
|
||||
minify: false,
|
||||
sourcemap: false,
|
||||
bundle: true,
|
||||
})
|
||||
429
pnpm-lock.yaml
generated
429
pnpm-lock.yaml
generated
@ -129,7 +129,7 @@ importers:
|
||||
version: 4.2.19
|
||||
svelte-check:
|
||||
specifier: ^4.0.4
|
||||
version: 4.0.4(svelte@4.2.19)(typescript@5.6.3)
|
||||
version: 4.0.4(picomatch@4.0.2)(svelte@4.2.19)(typescript@5.6.3)
|
||||
svelte-fa:
|
||||
specifier: ^4.0.3
|
||||
version: 4.0.3(svelte@4.2.19)
|
||||
@ -317,6 +317,18 @@ importers:
|
||||
|
||||
packages/pockethost-instance: {}
|
||||
|
||||
packages/pockethost/src/mothership-app:
|
||||
devDependencies:
|
||||
'@s-libs/micro-dash':
|
||||
specifier: ^18.0.0
|
||||
version: 18.0.0
|
||||
tsup:
|
||||
specifier: ^8.3.5
|
||||
version: 8.3.5(jiti@2.3.3)(postcss@8.4.47)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.5.1)
|
||||
type-fest:
|
||||
specifier: ^4.6.0
|
||||
version: 4.26.1
|
||||
|
||||
packages:
|
||||
|
||||
'@alloc/quick-lru@5.2.0':
|
||||
@ -483,6 +495,12 @@ packages:
|
||||
cpu: [ppc64]
|
||||
os: [aix]
|
||||
|
||||
'@esbuild/aix-ppc64@0.24.0':
|
||||
resolution: {integrity: sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ppc64]
|
||||
os: [aix]
|
||||
|
||||
'@esbuild/android-arm64@0.17.19':
|
||||
resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==}
|
||||
engines: {node: '>=12'}
|
||||
@ -501,6 +519,12 @@ packages:
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-arm64@0.24.0':
|
||||
resolution: {integrity: sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-arm@0.17.19':
|
||||
resolution: {integrity: sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==}
|
||||
engines: {node: '>=12'}
|
||||
@ -519,6 +543,12 @@ packages:
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-arm@0.24.0':
|
||||
resolution: {integrity: sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-x64@0.17.19':
|
||||
resolution: {integrity: sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==}
|
||||
engines: {node: '>=12'}
|
||||
@ -537,6 +567,12 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-x64@0.24.0':
|
||||
resolution: {integrity: sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/darwin-arm64@0.17.19':
|
||||
resolution: {integrity: sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==}
|
||||
engines: {node: '>=12'}
|
||||
@ -555,6 +591,12 @@ packages:
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/darwin-arm64@0.24.0':
|
||||
resolution: {integrity: sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/darwin-x64@0.17.19':
|
||||
resolution: {integrity: sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==}
|
||||
engines: {node: '>=12'}
|
||||
@ -573,6 +615,12 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/darwin-x64@0.24.0':
|
||||
resolution: {integrity: sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/freebsd-arm64@0.17.19':
|
||||
resolution: {integrity: sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==}
|
||||
engines: {node: '>=12'}
|
||||
@ -591,6 +639,12 @@ packages:
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/freebsd-arm64@0.24.0':
|
||||
resolution: {integrity: sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/freebsd-x64@0.17.19':
|
||||
resolution: {integrity: sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==}
|
||||
engines: {node: '>=12'}
|
||||
@ -609,6 +663,12 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/freebsd-x64@0.24.0':
|
||||
resolution: {integrity: sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/linux-arm64@0.17.19':
|
||||
resolution: {integrity: sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==}
|
||||
engines: {node: '>=12'}
|
||||
@ -627,6 +687,12 @@ packages:
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-arm64@0.24.0':
|
||||
resolution: {integrity: sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-arm@0.17.19':
|
||||
resolution: {integrity: sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==}
|
||||
engines: {node: '>=12'}
|
||||
@ -645,6 +711,12 @@ packages:
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-arm@0.24.0':
|
||||
resolution: {integrity: sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ia32@0.17.19':
|
||||
resolution: {integrity: sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==}
|
||||
engines: {node: '>=12'}
|
||||
@ -663,6 +735,12 @@ packages:
|
||||
cpu: [ia32]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ia32@0.24.0':
|
||||
resolution: {integrity: sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ia32]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-loong64@0.17.19':
|
||||
resolution: {integrity: sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==}
|
||||
engines: {node: '>=12'}
|
||||
@ -681,6 +759,12 @@ packages:
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-loong64@0.24.0':
|
||||
resolution: {integrity: sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-mips64el@0.17.19':
|
||||
resolution: {integrity: sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==}
|
||||
engines: {node: '>=12'}
|
||||
@ -699,6 +783,12 @@ packages:
|
||||
cpu: [mips64el]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-mips64el@0.24.0':
|
||||
resolution: {integrity: sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [mips64el]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ppc64@0.17.19':
|
||||
resolution: {integrity: sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==}
|
||||
engines: {node: '>=12'}
|
||||
@ -717,6 +807,12 @@ packages:
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ppc64@0.24.0':
|
||||
resolution: {integrity: sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-riscv64@0.17.19':
|
||||
resolution: {integrity: sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==}
|
||||
engines: {node: '>=12'}
|
||||
@ -735,6 +831,12 @@ packages:
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-riscv64@0.24.0':
|
||||
resolution: {integrity: sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-s390x@0.17.19':
|
||||
resolution: {integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==}
|
||||
engines: {node: '>=12'}
|
||||
@ -753,6 +855,12 @@ packages:
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-s390x@0.24.0':
|
||||
resolution: {integrity: sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-x64@0.17.19':
|
||||
resolution: {integrity: sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==}
|
||||
engines: {node: '>=12'}
|
||||
@ -771,6 +879,12 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-x64@0.24.0':
|
||||
resolution: {integrity: sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/netbsd-x64@0.17.19':
|
||||
resolution: {integrity: sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==}
|
||||
engines: {node: '>=12'}
|
||||
@ -789,12 +903,24 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/netbsd-x64@0.24.0':
|
||||
resolution: {integrity: sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/openbsd-arm64@0.23.1':
|
||||
resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/openbsd-arm64@0.24.0':
|
||||
resolution: {integrity: sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/openbsd-x64@0.17.19':
|
||||
resolution: {integrity: sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==}
|
||||
engines: {node: '>=12'}
|
||||
@ -813,6 +939,12 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/openbsd-x64@0.24.0':
|
||||
resolution: {integrity: sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/sunos-x64@0.17.19':
|
||||
resolution: {integrity: sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==}
|
||||
engines: {node: '>=12'}
|
||||
@ -831,6 +963,12 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [sunos]
|
||||
|
||||
'@esbuild/sunos-x64@0.24.0':
|
||||
resolution: {integrity: sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [sunos]
|
||||
|
||||
'@esbuild/win32-arm64@0.17.19':
|
||||
resolution: {integrity: sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==}
|
||||
engines: {node: '>=12'}
|
||||
@ -849,6 +987,12 @@ packages:
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-arm64@0.24.0':
|
||||
resolution: {integrity: sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-ia32@0.17.19':
|
||||
resolution: {integrity: sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==}
|
||||
engines: {node: '>=12'}
|
||||
@ -867,6 +1011,12 @@ packages:
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-ia32@0.24.0':
|
||||
resolution: {integrity: sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-x64@0.17.19':
|
||||
resolution: {integrity: sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==}
|
||||
engines: {node: '>=12'}
|
||||
@ -885,6 +1035,12 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-x64@0.24.0':
|
||||
resolution: {integrity: sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@fastify/busboy@2.1.1':
|
||||
resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==}
|
||||
engines: {node: '>=14'}
|
||||
@ -1571,6 +1727,12 @@ packages:
|
||||
resolution: {integrity: sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
||||
bundle-require@5.0.0:
|
||||
resolution: {integrity: sha512-GuziW3fSSmopcx4KRymQEJVbZUfqlCqcq7dvs6TYwKRZiegK/2buMxQTPs6MGlNv50wms1699qYO54R8XfRX4w==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
peerDependencies:
|
||||
esbuild: '>=0.18'
|
||||
|
||||
bunyan@1.8.15:
|
||||
resolution: {integrity: sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==}
|
||||
engines: {'0': node >=0.10.0}
|
||||
@ -1580,6 +1742,10 @@ packages:
|
||||
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
cac@6.7.14:
|
||||
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
call-bind@1.0.7:
|
||||
resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@ -1675,6 +1841,10 @@ packages:
|
||||
concat-map@0.0.1:
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
|
||||
consola@3.2.3:
|
||||
resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==}
|
||||
engines: {node: ^14.18.0 || >=16.10.0}
|
||||
|
||||
constantinople@4.0.1:
|
||||
resolution: {integrity: sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==}
|
||||
|
||||
@ -1985,6 +2155,11 @@ packages:
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
esbuild@0.24.0:
|
||||
resolution: {integrity: sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
escalade@3.2.0:
|
||||
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
||||
engines: {node: '>=6'}
|
||||
@ -2085,6 +2260,14 @@ packages:
|
||||
picomatch:
|
||||
optional: true
|
||||
|
||||
fdir@6.4.2:
|
||||
resolution: {integrity: sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==}
|
||||
peerDependencies:
|
||||
picomatch: ^3 || ^4
|
||||
peerDependenciesMeta:
|
||||
picomatch:
|
||||
optional: true
|
||||
|
||||
fecha@4.2.3:
|
||||
resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==}
|
||||
|
||||
@ -2449,6 +2632,10 @@ packages:
|
||||
resolution: {integrity: sha512-EX4oNDwcXSivPrw2qKH2LB5PoFxEvgtv2JgwW0bU858HoLQ+kutSvjLMUqBd0PeJYEinLWhoI9Ol0eYMqj/wNQ==}
|
||||
hasBin: true
|
||||
|
||||
joycon@3.1.1:
|
||||
resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
js-cookie@3.0.5:
|
||||
resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
|
||||
engines: {node: '>=14'}
|
||||
@ -2496,6 +2683,10 @@ packages:
|
||||
lines-and-columns@1.2.4:
|
||||
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
||||
|
||||
load-tsconfig@0.2.5:
|
||||
resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
locate-character@3.0.0:
|
||||
resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==}
|
||||
|
||||
@ -2516,6 +2707,9 @@ packages:
|
||||
lodash.merge@4.6.2:
|
||||
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||
|
||||
lodash.sortby@4.7.0:
|
||||
resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==}
|
||||
|
||||
lodash.startcase@4.4.0:
|
||||
resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==}
|
||||
|
||||
@ -2928,10 +3122,17 @@ packages:
|
||||
picocolors@1.1.0:
|
||||
resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==}
|
||||
|
||||
picocolors@1.1.1:
|
||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||
|
||||
picomatch@2.3.1:
|
||||
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
||||
engines: {node: '>=8.6'}
|
||||
|
||||
picomatch@4.0.2:
|
||||
resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
pify@2.3.0:
|
||||
resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@ -3357,6 +3558,10 @@ packages:
|
||||
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
source-map@0.8.0-beta.0:
|
||||
resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
sourcemap-codec@1.4.8:
|
||||
resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
|
||||
deprecated: Please use @jridgewell/sourcemap-codec instead
|
||||
@ -3560,6 +3765,13 @@ packages:
|
||||
tiny-glob@0.2.9:
|
||||
resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==}
|
||||
|
||||
tinyexec@0.3.1:
|
||||
resolution: {integrity: sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==}
|
||||
|
||||
tinyglobby@0.2.10:
|
||||
resolution: {integrity: sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
tmp@0.0.33:
|
||||
resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==}
|
||||
engines: {node: '>=0.6.0'}
|
||||
@ -3590,6 +3802,13 @@ packages:
|
||||
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
tr46@1.0.1:
|
||||
resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==}
|
||||
|
||||
tree-kill@1.2.2:
|
||||
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
|
||||
hasBin: true
|
||||
|
||||
triple-beam@1.4.1:
|
||||
resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==}
|
||||
engines: {node: '>= 14.0.0'}
|
||||
@ -3603,6 +3822,25 @@ packages:
|
||||
tslib@2.8.1:
|
||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||
|
||||
tsup@8.3.5:
|
||||
resolution: {integrity: sha512-Tunf6r6m6tnZsG9GYWndg0z8dEV7fD733VBFzFJ5Vcm1FtlXB8xBD/rtrBi2a3YKEV7hHtxiZtW5EAVADoe1pA==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@microsoft/api-extractor': ^7.36.0
|
||||
'@swc/core': ^1
|
||||
postcss: ^8.4.12
|
||||
typescript: '>=4.5.0'
|
||||
peerDependenciesMeta:
|
||||
'@microsoft/api-extractor':
|
||||
optional: true
|
||||
'@swc/core':
|
||||
optional: true
|
||||
postcss:
|
||||
optional: true
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
tsx@4.19.2:
|
||||
resolution: {integrity: sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
@ -3760,6 +3998,12 @@ packages:
|
||||
resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
webidl-conversions@4.0.2:
|
||||
resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
|
||||
|
||||
whatwg-url@7.1.0:
|
||||
resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==}
|
||||
|
||||
which-module@2.0.1:
|
||||
resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
|
||||
|
||||
@ -4104,6 +4348,9 @@ snapshots:
|
||||
'@esbuild/aix-ppc64@0.23.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/aix-ppc64@0.24.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm64@0.17.19':
|
||||
optional: true
|
||||
|
||||
@ -4113,6 +4360,9 @@ snapshots:
|
||||
'@esbuild/android-arm64@0.23.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm64@0.24.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm@0.17.19':
|
||||
optional: true
|
||||
|
||||
@ -4122,6 +4372,9 @@ snapshots:
|
||||
'@esbuild/android-arm@0.23.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm@0.24.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-x64@0.17.19':
|
||||
optional: true
|
||||
|
||||
@ -4131,6 +4384,9 @@ snapshots:
|
||||
'@esbuild/android-x64@0.23.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-x64@0.24.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-arm64@0.17.19':
|
||||
optional: true
|
||||
|
||||
@ -4140,6 +4396,9 @@ snapshots:
|
||||
'@esbuild/darwin-arm64@0.23.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-arm64@0.24.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-x64@0.17.19':
|
||||
optional: true
|
||||
|
||||
@ -4149,6 +4408,9 @@ snapshots:
|
||||
'@esbuild/darwin-x64@0.23.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-x64@0.24.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-arm64@0.17.19':
|
||||
optional: true
|
||||
|
||||
@ -4158,6 +4420,9 @@ snapshots:
|
||||
'@esbuild/freebsd-arm64@0.23.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-arm64@0.24.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-x64@0.17.19':
|
||||
optional: true
|
||||
|
||||
@ -4167,6 +4432,9 @@ snapshots:
|
||||
'@esbuild/freebsd-x64@0.23.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-x64@0.24.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm64@0.17.19':
|
||||
optional: true
|
||||
|
||||
@ -4176,6 +4444,9 @@ snapshots:
|
||||
'@esbuild/linux-arm64@0.23.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm64@0.24.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm@0.17.19':
|
||||
optional: true
|
||||
|
||||
@ -4185,6 +4456,9 @@ snapshots:
|
||||
'@esbuild/linux-arm@0.23.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm@0.24.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ia32@0.17.19':
|
||||
optional: true
|
||||
|
||||
@ -4194,6 +4468,9 @@ snapshots:
|
||||
'@esbuild/linux-ia32@0.23.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ia32@0.24.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-loong64@0.17.19':
|
||||
optional: true
|
||||
|
||||
@ -4203,6 +4480,9 @@ snapshots:
|
||||
'@esbuild/linux-loong64@0.23.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-loong64@0.24.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-mips64el@0.17.19':
|
||||
optional: true
|
||||
|
||||
@ -4212,6 +4492,9 @@ snapshots:
|
||||
'@esbuild/linux-mips64el@0.23.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-mips64el@0.24.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ppc64@0.17.19':
|
||||
optional: true
|
||||
|
||||
@ -4221,6 +4504,9 @@ snapshots:
|
||||
'@esbuild/linux-ppc64@0.23.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ppc64@0.24.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-riscv64@0.17.19':
|
||||
optional: true
|
||||
|
||||
@ -4230,6 +4516,9 @@ snapshots:
|
||||
'@esbuild/linux-riscv64@0.23.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-riscv64@0.24.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-s390x@0.17.19':
|
||||
optional: true
|
||||
|
||||
@ -4239,6 +4528,9 @@ snapshots:
|
||||
'@esbuild/linux-s390x@0.23.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-s390x@0.24.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-x64@0.17.19':
|
||||
optional: true
|
||||
|
||||
@ -4248,6 +4540,9 @@ snapshots:
|
||||
'@esbuild/linux-x64@0.23.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-x64@0.24.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-x64@0.17.19':
|
||||
optional: true
|
||||
|
||||
@ -4257,9 +4552,15 @@ snapshots:
|
||||
'@esbuild/netbsd-x64@0.23.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-x64@0.24.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-arm64@0.23.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-arm64@0.24.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-x64@0.17.19':
|
||||
optional: true
|
||||
|
||||
@ -4269,6 +4570,9 @@ snapshots:
|
||||
'@esbuild/openbsd-x64@0.23.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-x64@0.24.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/sunos-x64@0.17.19':
|
||||
optional: true
|
||||
|
||||
@ -4278,6 +4582,9 @@ snapshots:
|
||||
'@esbuild/sunos-x64@0.23.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/sunos-x64@0.24.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-arm64@0.17.19':
|
||||
optional: true
|
||||
|
||||
@ -4287,6 +4594,9 @@ snapshots:
|
||||
'@esbuild/win32-arm64@0.23.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-arm64@0.24.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-ia32@0.17.19':
|
||||
optional: true
|
||||
|
||||
@ -4296,6 +4606,9 @@ snapshots:
|
||||
'@esbuild/win32-ia32@0.23.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-ia32@0.24.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-x64@0.17.19':
|
||||
optional: true
|
||||
|
||||
@ -4305,6 +4618,9 @@ snapshots:
|
||||
'@esbuild/win32-x64@0.23.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-x64@0.24.0':
|
||||
optional: true
|
||||
|
||||
'@fastify/busboy@2.1.1': {}
|
||||
|
||||
'@fortawesome/fontawesome-common-types@6.6.0': {}
|
||||
@ -4984,6 +5300,11 @@ snapshots:
|
||||
buildcheck@0.0.6:
|
||||
optional: true
|
||||
|
||||
bundle-require@5.0.0(esbuild@0.24.0):
|
||||
dependencies:
|
||||
esbuild: 0.24.0
|
||||
load-tsconfig: 0.2.5
|
||||
|
||||
bunyan@1.8.15:
|
||||
optionalDependencies:
|
||||
dtrace-provider: 0.8.8
|
||||
@ -4993,6 +5314,8 @@ snapshots:
|
||||
|
||||
bytes@3.1.2: {}
|
||||
|
||||
cac@6.7.14: {}
|
||||
|
||||
call-bind@1.0.7:
|
||||
dependencies:
|
||||
es-define-property: 1.0.0
|
||||
@ -5101,6 +5424,8 @@ snapshots:
|
||||
|
||||
concat-map@0.0.1: {}
|
||||
|
||||
consola@3.2.3: {}
|
||||
|
||||
constantinople@4.0.1:
|
||||
dependencies:
|
||||
'@babel/parser': 7.26.2
|
||||
@ -5487,6 +5812,33 @@ snapshots:
|
||||
'@esbuild/win32-ia32': 0.23.1
|
||||
'@esbuild/win32-x64': 0.23.1
|
||||
|
||||
esbuild@0.24.0:
|
||||
optionalDependencies:
|
||||
'@esbuild/aix-ppc64': 0.24.0
|
||||
'@esbuild/android-arm': 0.24.0
|
||||
'@esbuild/android-arm64': 0.24.0
|
||||
'@esbuild/android-x64': 0.24.0
|
||||
'@esbuild/darwin-arm64': 0.24.0
|
||||
'@esbuild/darwin-x64': 0.24.0
|
||||
'@esbuild/freebsd-arm64': 0.24.0
|
||||
'@esbuild/freebsd-x64': 0.24.0
|
||||
'@esbuild/linux-arm': 0.24.0
|
||||
'@esbuild/linux-arm64': 0.24.0
|
||||
'@esbuild/linux-ia32': 0.24.0
|
||||
'@esbuild/linux-loong64': 0.24.0
|
||||
'@esbuild/linux-mips64el': 0.24.0
|
||||
'@esbuild/linux-ppc64': 0.24.0
|
||||
'@esbuild/linux-riscv64': 0.24.0
|
||||
'@esbuild/linux-s390x': 0.24.0
|
||||
'@esbuild/linux-x64': 0.24.0
|
||||
'@esbuild/netbsd-x64': 0.24.0
|
||||
'@esbuild/openbsd-arm64': 0.24.0
|
||||
'@esbuild/openbsd-x64': 0.24.0
|
||||
'@esbuild/sunos-x64': 0.24.0
|
||||
'@esbuild/win32-arm64': 0.24.0
|
||||
'@esbuild/win32-ia32': 0.24.0
|
||||
'@esbuild/win32-x64': 0.24.0
|
||||
|
||||
escalade@3.2.0: {}
|
||||
|
||||
escape-html@1.0.3: {}
|
||||
@ -5591,7 +5943,13 @@ snapshots:
|
||||
dependencies:
|
||||
pend: 1.2.0
|
||||
|
||||
fdir@6.4.0: {}
|
||||
fdir@6.4.0(picomatch@4.0.2):
|
||||
optionalDependencies:
|
||||
picomatch: 4.0.2
|
||||
|
||||
fdir@6.4.2(picomatch@4.0.2):
|
||||
optionalDependencies:
|
||||
picomatch: 4.0.2
|
||||
|
||||
fecha@4.2.3: {}
|
||||
|
||||
@ -5988,6 +6346,8 @@ snapshots:
|
||||
jiti@2.3.3:
|
||||
optional: true
|
||||
|
||||
joycon@3.1.1: {}
|
||||
|
||||
js-cookie@3.0.5: {}
|
||||
|
||||
js-stringify@1.0.2:
|
||||
@ -6026,6 +6386,8 @@ snapshots:
|
||||
|
||||
lines-and-columns@1.2.4: {}
|
||||
|
||||
load-tsconfig@0.2.5: {}
|
||||
|
||||
locate-character@3.0.0: {}
|
||||
|
||||
locate-path@5.0.0:
|
||||
@ -6042,6 +6404,8 @@ snapshots:
|
||||
|
||||
lodash.merge@4.6.2: {}
|
||||
|
||||
lodash.sortby@4.7.0: {}
|
||||
|
||||
lodash.startcase@4.4.0: {}
|
||||
|
||||
lodash.truncate@4.4.2: {}
|
||||
@ -6488,8 +6852,12 @@ snapshots:
|
||||
|
||||
picocolors@1.1.0: {}
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
|
||||
picomatch@2.3.1: {}
|
||||
|
||||
picomatch@4.0.2: {}
|
||||
|
||||
pify@2.3.0: {}
|
||||
|
||||
pify@3.0.0: {}
|
||||
@ -6533,7 +6901,6 @@ snapshots:
|
||||
postcss: 8.4.47
|
||||
tsx: 4.19.2
|
||||
yaml: 2.5.1
|
||||
optional: true
|
||||
|
||||
postcss-nested@6.2.0(postcss@8.4.47):
|
||||
dependencies:
|
||||
@ -6991,6 +7358,10 @@ snapshots:
|
||||
|
||||
source-map@0.6.1: {}
|
||||
|
||||
source-map@0.8.0-beta.0:
|
||||
dependencies:
|
||||
whatwg-url: 7.1.0
|
||||
|
||||
sourcemap-codec@1.4.8: {}
|
||||
|
||||
spawndamnit@2.0.0:
|
||||
@ -7078,11 +7449,11 @@ snapshots:
|
||||
|
||||
supports-preserve-symlinks-flag@1.0.0: {}
|
||||
|
||||
svelte-check@4.0.4(svelte@4.2.19)(typescript@5.6.3):
|
||||
svelte-check@4.0.4(picomatch@4.0.2)(svelte@4.2.19)(typescript@5.6.3):
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
chokidar: 4.0.1
|
||||
fdir: 6.4.0
|
||||
fdir: 6.4.0(picomatch@4.0.2)
|
||||
picocolors: 1.1.0
|
||||
sade: 1.8.1
|
||||
svelte: 4.2.19
|
||||
@ -7227,6 +7598,13 @@ snapshots:
|
||||
globalyzer: 0.1.0
|
||||
globrex: 0.1.2
|
||||
|
||||
tinyexec@0.3.1: {}
|
||||
|
||||
tinyglobby@0.2.10:
|
||||
dependencies:
|
||||
fdir: 6.4.2(picomatch@4.0.2)
|
||||
picomatch: 4.0.2
|
||||
|
||||
tmp@0.0.33:
|
||||
dependencies:
|
||||
os-tmpdir: 1.0.2
|
||||
@ -7251,6 +7629,12 @@ snapshots:
|
||||
|
||||
totalist@3.0.1: {}
|
||||
|
||||
tr46@1.0.1:
|
||||
dependencies:
|
||||
punycode: 2.3.1
|
||||
|
||||
tree-kill@1.2.2: {}
|
||||
|
||||
triple-beam@1.4.1: {}
|
||||
|
||||
ts-interface-checker@0.1.13: {}
|
||||
@ -7259,6 +7643,33 @@ snapshots:
|
||||
|
||||
tslib@2.8.1: {}
|
||||
|
||||
tsup@8.3.5(jiti@2.3.3)(postcss@8.4.47)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.5.1):
|
||||
dependencies:
|
||||
bundle-require: 5.0.0(esbuild@0.24.0)
|
||||
cac: 6.7.14
|
||||
chokidar: 4.0.1
|
||||
consola: 3.2.3
|
||||
debug: 4.3.7
|
||||
esbuild: 0.24.0
|
||||
joycon: 3.1.1
|
||||
picocolors: 1.1.1
|
||||
postcss-load-config: 6.0.1(jiti@2.3.3)(postcss@8.4.47)(tsx@4.19.2)(yaml@2.5.1)
|
||||
resolve-from: 5.0.0
|
||||
rollup: 4.24.0
|
||||
source-map: 0.8.0-beta.0
|
||||
sucrase: 3.35.0
|
||||
tinyexec: 0.3.1
|
||||
tinyglobby: 0.2.10
|
||||
tree-kill: 1.2.2
|
||||
optionalDependencies:
|
||||
postcss: 8.4.47
|
||||
typescript: 5.6.3
|
||||
transitivePeerDependencies:
|
||||
- jiti
|
||||
- supports-color
|
||||
- tsx
|
||||
- yaml
|
||||
|
||||
tsx@4.19.2:
|
||||
dependencies:
|
||||
esbuild: 0.23.1
|
||||
@ -7384,6 +7795,14 @@ snapshots:
|
||||
|
||||
web-streams-polyfill@3.2.1: {}
|
||||
|
||||
webidl-conversions@4.0.2: {}
|
||||
|
||||
whatwg-url@7.1.0:
|
||||
dependencies:
|
||||
lodash.sortby: 4.7.0
|
||||
tr46: 1.0.1
|
||||
webidl-conversions: 4.0.2
|
||||
|
||||
which-module@2.0.1: {}
|
||||
|
||||
which@1.3.1:
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
packages:
|
||||
- 'packages/*'
|
||||
- 'packages/pockethost/src/mothership-app'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user