dao deadlock fixes

This commit is contained in:
Ben Allfree 2024-01-12 11:20:34 +00:00
parent 8c31615470
commit c32c3bcb95
23 changed files with 497 additions and 64 deletions

View File

@ -0,0 +1,55 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const collection = new Collection({
"id": "h055gvw3oqi2fs0",
"created": "2024-01-12 11:12:59.598Z",
"updated": "2024-01-12 11:12:59.598Z",
"name": "duplicate_emails",
"type": "view",
"system": false,
"schema": [
{
"system": false,
"id": "9hblghvi",
"name": "domain",
"type": "json",
"required": false,
"presentable": false,
"unique": false,
"options": {
"maxSize": 1
}
},
{
"system": false,
"id": "csleeffq",
"name": "user_count",
"type": "number",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"noDecimal": false
}
}
],
"indexes": [],
"listRule": null,
"viewRule": null,
"createRule": null,
"updateRule": null,
"deleteRule": null,
"options": {
"query": "SELECT (ROW_NUMBER() OVER()) as id, SUBSTR(email, INSTR(email, '@')) AS domain, COUNT(*) AS user_count\nFROM users\nGROUP BY domain\nORDER BY user_count DESC;\n"
}
});
return Dao(db).saveCollection(collection);
}, (db) => {
const dao = new Dao(db);
const collection = dao.findCollectionByNameOrId("h055gvw3oqi2fs0");
return dao.deleteCollection(collection);
})

View File

@ -0,0 +1,92 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("h055gvw3oqi2fs0")
collection.options = {
"query": "SELECT (ROW_NUMBER() OVER()) as id, SUBSTR(email, INSTR(email, '@')) AS domain, COUNT(*) AS user_count\nFROM users\n WHERE email LIKE '%+%'\nGROUP BY domain\nORDER BY user_count DESC;\n"
}
// remove
collection.schema.removeField("9hblghvi")
// remove
collection.schema.removeField("csleeffq")
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "wwltpcoc",
"name": "domain",
"type": "json",
"required": false,
"presentable": false,
"unique": false,
"options": {
"maxSize": 1
}
}))
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "yy0kkdim",
"name": "user_count",
"type": "number",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"noDecimal": false
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("h055gvw3oqi2fs0")
collection.options = {
"query": "SELECT (ROW_NUMBER() OVER()) as id, SUBSTR(email, INSTR(email, '@')) AS domain, COUNT(*) AS user_count\nFROM users\nGROUP BY domain\nORDER BY user_count DESC;\n"
}
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "9hblghvi",
"name": "domain",
"type": "json",
"required": false,
"presentable": false,
"unique": false,
"options": {
"maxSize": 1
}
}))
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "csleeffq",
"name": "user_count",
"type": "number",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"noDecimal": false
}
}))
// remove
collection.schema.removeField("wwltpcoc")
// remove
collection.schema.removeField("yy0kkdim")
return dao.saveCollection(collection)
})

View File

@ -0,0 +1,92 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("h055gvw3oqi2fs0")
collection.options = {
"query": "SELECT (ROW_NUMBER() OVER()) as id, SUBSTR(email, INSTR(email, '@')) AS domain, COUNT(*) AS user_count\nFROM users\n WHERE email LIKE '%+%'\nORDER BY user_count DESC;\n"
}
// remove
collection.schema.removeField("wwltpcoc")
// remove
collection.schema.removeField("yy0kkdim")
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "juo93d3a",
"name": "domain",
"type": "json",
"required": false,
"presentable": false,
"unique": false,
"options": {
"maxSize": 1
}
}))
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "whrph450",
"name": "user_count",
"type": "number",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"noDecimal": false
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("h055gvw3oqi2fs0")
collection.options = {
"query": "SELECT (ROW_NUMBER() OVER()) as id, SUBSTR(email, INSTR(email, '@')) AS domain, COUNT(*) AS user_count\nFROM users\n WHERE email LIKE '%+%'\nGROUP BY domain\nORDER BY user_count DESC;\n"
}
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "wwltpcoc",
"name": "domain",
"type": "json",
"required": false,
"presentable": false,
"unique": false,
"options": {
"maxSize": 1
}
}))
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "yy0kkdim",
"name": "user_count",
"type": "number",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"noDecimal": false
}
}))
// remove
collection.schema.removeField("juo93d3a")
// remove
collection.schema.removeField("whrph450")
return dao.saveCollection(collection)
})

View File

@ -0,0 +1,74 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("h055gvw3oqi2fs0")
collection.options = {
"query": "SELECT (ROW_NUMBER() OVER()) as id, email\nFROM users\n WHERE email LIKE '%+%'\n"
}
// remove
collection.schema.removeField("juo93d3a")
// remove
collection.schema.removeField("whrph450")
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "92iigwzl",
"name": "email",
"type": "email",
"required": false,
"presentable": false,
"unique": false,
"options": {
"exceptDomains": null,
"onlyDomains": null
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("h055gvw3oqi2fs0")
collection.options = {
"query": "SELECT (ROW_NUMBER() OVER()) as id, SUBSTR(email, INSTR(email, '@')) AS domain, COUNT(*) AS user_count\nFROM users\n WHERE email LIKE '%+%'\nORDER BY user_count DESC;\n"
}
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "juo93d3a",
"name": "domain",
"type": "json",
"required": false,
"presentable": false,
"unique": false,
"options": {
"maxSize": 1
}
}))
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "whrph450",
"name": "user_count",
"type": "number",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"noDecimal": false
}
}))
// remove
collection.schema.removeField("92iigwzl")
return dao.saveCollection(collection)
})

View File

@ -0,0 +1,91 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("h055gvw3oqi2fs0")
collection.options = {
"query": "SELECT \n (ROW_NUMBER() OVER()) as id,\n SUBSTR(email, 1, INSTR(email, '@') - 1) AS email_prefix,\n SUBSTR(email, INSTR(email, '@')) AS email_domain,\n COUNT(*) AS count\nFROM \n (SELECT \n CASE \n WHEN INSTR(SUBSTR(email, 1, INSTR(email, '@') - 1), '+') > 0 \n THEN SUBSTR(email, 1, INSTR(SUBSTR(email, 1, INSTR(email, '@') - 1), '+') - 1) || SUBSTR(email, INSTR(email, '@')) \n ELSE email \n END AS email \n FROM users)\nGROUP BY \n email_prefix, email_domain;\n"
}
// remove
collection.schema.removeField("92iigwzl")
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "y2akvv89",
"name": "email_prefix",
"type": "json",
"required": false,
"presentable": false,
"unique": false,
"options": {
"maxSize": 1
}
}))
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "za7mr8gw",
"name": "email_domain",
"type": "json",
"required": false,
"presentable": false,
"unique": false,
"options": {
"maxSize": 1
}
}))
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "zjwougcm",
"name": "count",
"type": "number",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"noDecimal": false
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("h055gvw3oqi2fs0")
collection.options = {
"query": "SELECT (ROW_NUMBER() OVER()) as id, email\nFROM users\n WHERE email LIKE '%+%'\n"
}
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "92iigwzl",
"name": "email",
"type": "email",
"required": false,
"presentable": false,
"unique": false,
"options": {
"exceptDomains": null,
"onlyDomains": null
}
}))
// remove
collection.schema.removeField("y2akvv89")
// remove
collection.schema.removeField("za7mr8gw")
// remove
collection.schema.removeField("zjwougcm")
return dao.saveCollection(collection)
})

View File

@ -7,6 +7,7 @@ routerAdd(
'POST',
'/api/instance',
(c) => {
const dao = $app.dao()
const { audit, mkLog } = /** @type {Lib} */ (require(`${__hooks}/lib.js`))
const log = mkLog(`POST:instance`)
@ -46,7 +47,7 @@ routerAdd(
const { versions } = require(`${__hooks}/versions.js`)
const collection = $app.dao().findCollectionByNameOrId('instances')
const collection = dao.findCollectionByNameOrId('instances')
const record = new Record(collection)
record.set('uid', authRecord.getId())
record.set('subdomain', subdomain)

View File

@ -12,6 +12,7 @@ routerAdd(
'DELETE',
'/api/instance/:id',
(c) => {
const dao = $app.dao()
const { audit, mkLog } = /** @type {Lib} */ (require(`${__hooks}/lib.js`))
const log = mkLog(`DELETE:instance`)
@ -43,7 +44,7 @@ routerAdd(
throw new BadRequestError(`Expected authRecord here`)
}
const record = $app.dao().findRecordById('instances', id)
const record = dao.findRecordById('instances', id)
if (!record) {
throw new BadRequestError(`Instance ${id} not found.`)
}
@ -60,7 +61,7 @@ routerAdd(
const res = $os.removeAll(path)
log(`res`, res)
$app.dao().deleteRecord(record)
dao.deleteRecord(record)
return c.json(200, { status: 'ok' })
},

View File

@ -1,10 +1,11 @@
/** Migrate version numbers */
onAfterBootstrap((e) => {
const dao = $app.dao()
const { audit, mkLog } = /** @type {Lib} */ (require(`${__hooks}/lib.js`))
const log = mkLog(`bootstrap`)
const records = $app.dao().findRecordsByFilter(`instances`, '1=1')
const records = dao.findRecordsByFilter(`instances`, '1=1')
const { versions } = require(`${__hooks}/versions.js`)
const unrecognized = []
records.forEach((record) => {
@ -23,7 +24,7 @@ onAfterBootstrap((e) => {
})()
if (versions.includes(newVersion)) {
record.set(`version`, newVersion)
$app.dao().saveRecord(record)
dao.saveRecord(record)
} else {
unrecognized.push(v)
}

View File

@ -1,6 +1,7 @@
/// <reference path="../types/types.d.ts" />
onModelAfterCreate((e) => {
const dao = e.dao || $app.dao()
const { audit, mkLog } = /** @type {Lib} */ (require(`${__hooks}/lib.js`))
const log = mkLog(`instances:create:discord:notify`)
@ -22,6 +23,9 @@ onModelAfterCreate((e) => {
timeout: 5, // in seconds
})
} catch (e) {
audit(`ERROR`, `Instance creation discord notify failed with ${e}`, { log })
audit(`ERROR`, `Instance creation discord notify failed with ${e}`, {
log,
dao,
})
}
}, 'instances')

View File

@ -1,4 +1,6 @@
onModelAfterUpdate((e) => {
const dao = e.dao || $app.dao()
const newModel = /** @type {models.Record} */ (e.model)
const oldModel = newModel.originalCopy()
@ -18,7 +20,7 @@ onModelAfterUpdate((e) => {
log(`switched`)
const uid = newModel.get(`uid`)
const user = $app.dao().findRecordById('users', uid)
const user = dao.findRecordById('users', uid)
// Bail out if the user has notifications disabled globally or for the instance
const shouldNotify =
@ -33,6 +35,8 @@ onModelAfterUpdate((e) => {
const address = user.getString(`email`)
log({ instanceId, subdomain, address })
enqueueNotification(`email`, `maintenance_mode`, uid, {
log,
dao,
message_template_vars: {
subdomain,
instanceId,

View File

@ -2,5 +2,6 @@
/** Reset instance status to idle on start */
onAfterBootstrap((e) => {
$app.dao().db().newQuery(`update instances set status='idle'`).execute()
const dao = $app.dao()
dao.db().newQuery(`update instances set status='idle'`).execute()
})

View File

@ -14,6 +14,7 @@ routerAdd(
'PUT',
'/api/instance/:id',
(c) => {
const dao = $app.dao()
const { mkLog, audit, removeEmptyKeys } = /** @type {Lib} */ (
require(`${__hooks}/lib.js`)
)
@ -88,7 +89,7 @@ routerAdd(
}),
)
const record = $app.dao().findRecordById('instances', id)
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))

View File

@ -2,6 +2,7 @@
/** Validate instance version */
onModelBeforeCreate((e) => {
const dao = e.dao || $app.dao()
const { versions } = require(`${__hooks}/versions.js`)
const version = e.model.get('version')

View File

@ -2,6 +2,7 @@
/** Validate instance version */
onModelBeforeUpdate((e) => {
const dao = e.dao || $app.dao()
const { versions } = require(`${__hooks}/versions.js`)
const version = e.model.get('version')
@ -22,8 +23,7 @@ onModelBeforeUpdate((e) => {
const inUse = (() => {
try {
$app
.dao()
dao
.db()
.newQuery(
`select id from instances where cname='${cname}' and id <> '${id}'`,

View File

@ -1,6 +1,5 @@
/** @type {Lib['audit']} */
const audit = (event, note, context = {}) => {
const { log = mkLog(`audit`), dao = $app.dao(), extra = {} } = context
const audit = (event, note, { log, dao, extra = {} }) => {
log(`AUDIT:${event}: ${note}`, extra)
dao.saveRecord(
new Record(dao.findCollectionByNameOrId('audit'), {
@ -47,12 +46,12 @@ function interpolateString(template, dict) {
}
/** @type {Lib['enqueueNotification']} */
const enqueueNotification = (channel, template, user_id, context = {}) => {
const {
log = mkLog(`enqueueNotification`),
message_template_vars = {},
dao = $app.dao(),
} = context
const enqueueNotification = (
channel,
template,
user_id,
{ log, dao, message_template_vars = {} },
) => {
log({ channel, template, user_id })
const emailTemplate = dao.findFirstRecordByData(
'message_templates',
@ -75,12 +74,7 @@ const enqueueNotification = (channel, template, user_id, context = {}) => {
}
/** @type {Lib['processNotification']} */
const processNotification = (notificationRec, context) => {
const {
log = mkLog(`processNotification`),
test = false,
dao = $app.dao(),
} = context
const processNotification = (notificationRec, { log, dao, test = false }) => {
log({ notificationRec })
const channel = notificationRec.getString(`channel`)

View File

@ -1,4 +1,5 @@
routerAdd('POST', '/api/ls', (c) => {
const dao = $app.dao()
const { audit, mkLog, enqueueNotification } = /** @type {Lib} */ (
require(`${__hooks}/lib.js`)
)
@ -57,7 +58,7 @@ routerAdd('POST', '/api/ls', (c) => {
const userRec = (() => {
try {
return $app.dao().findFirstRecordByData('users', 'id', user_id)
return dao.findFirstRecordByData('users', 'id', user_id)
} catch (e) {}
})()
if (!userRec) {
@ -96,15 +97,12 @@ routerAdd('POST', '/api/ls', (c) => {
}
applyEditionSpecifics()
const payment = new Record(
$app.dao().findCollectionByNameOrId('payments'),
{
user: user_id,
payment_id: `ls_${order_number}`,
},
)
const payment = new Record(dao.findCollectionByNameOrId('payments'), {
user: user_id,
payment_id: `ls_${order_number}`,
})
$app.dao().runInTransaction((txDao) => {
dao.runInTransaction((txDao) => {
log(`transaction started`)
if (!userRec.getDateTime(`welcome`)) {
enqueueNotification(`email`, `welcome`, user_id, { log, dao: txDao })
@ -138,6 +136,7 @@ routerAdd('POST', '/api/ls', (c) => {
log(`database updated`)
audit(`LS`, `Order ${order_number} ${product_name} processed.`, {
log,
dao,
extra: {
email,
user: user_id,
@ -146,6 +145,7 @@ routerAdd('POST', '/api/ls', (c) => {
} catch (e) {
audit(`LS_ERR`, `${e}`, {
log,
dao,
extra: {
email,
user: user_id,

View File

@ -7,14 +7,21 @@ routerAdd(`GET`, `api/process_single_notification`, (c) => {
const log = mkLog(`process_single_notification`)
log(`start`)
const dao = $app.dao()
try {
const notification = $app
.dao()
.findFirstRecordByData(`notifications`, `delivered`, ``)
const notification = dao.findFirstRecordByData(
`notifications`,
`delivered`,
``,
)
if (!notification) {
return c.json(200, `No notifications to send`)
}
processNotification(notification, { log, test: !!c.queryParam(`test`) })
processNotification(notification, {
log,
dao,
test: !!c.queryParam(`test`),
})
} catch (e) {
c.json(500, `${e}`)
}
@ -22,6 +29,7 @@ routerAdd(`GET`, `api/process_single_notification`, (c) => {
})
onModelAfterCreate((e) => {
const dao = e.dao || $app.dao()
return
const { processNotification, mkLog, audit } = /** @type {Lib} */ (
require(`${__hooks}/lib.js`)
@ -33,18 +41,19 @@ onModelAfterCreate((e) => {
log({ notificationRec })
try {
$app.dao().expandRecord(notificationRec, ['message_template'], null)
dao.expandRecord(notificationRec, ['message_template'], null)
const messageTemplateRec = notificationRec.expandedOne(`message_template`)
if (!messageTemplateRec) {
throw new Error(`Missing message template`)
}
if ([`maintenance-mode`].includes(messageTemplateRec.getString(`slug`))) {
processNotification(notificationRec, { log })
processNotification(notificationRec, { log, dao })
}
} catch (e) {
audit(`ERROR`, `${e}`, {
log,
dao,
extra: {
notification: notificationRec.getId(),
},

View File

@ -102,6 +102,7 @@ routerAdd(
'POST',
'/api/signup',
(c) => {
const dao = $app.dao()
const parsed = (() => {
const rawBody = readerToString(c.request().body)
try {
@ -145,7 +146,7 @@ routerAdd(
const userExists = (() => {
try {
const record = $app.dao().findFirstRecordByData('users', 'email', email)
const record = dao.findFirstRecordByData('users', 'email', email)
return true
} catch {
return false
@ -160,8 +161,8 @@ routerAdd(
)
}
$app.dao().runInTransaction((txDao) => {
const usersCollection = $app.dao().findCollectionByNameOrId('users')
dao.runInTransaction((txDao) => {
const usersCollection = dao.findCollectionByNameOrId('users')
const instanceCollection = $app
.dao()
.findCollectionByNameOrId('instances')

View File

@ -1,6 +1,7 @@
/// <reference path="../types/types.d.ts" />
routerAdd('POST', '/api/sns', (c) => {
const dao = $app.dao()
const { mkLog, audit } = /** @type {Lib} */ (require(`${__hooks}/lib.js`))
const log = mkLog(`sns`)
@ -10,19 +11,18 @@ routerAdd('POST', '/api/sns', (c) => {
email: emailAddress,
})
try {
const user = $app
.dao()
.findFirstRecordByData('users', 'email', emailAddress)
const user = dao.findFirstRecordByData('users', 'email', emailAddress)
log(`user is`, user)
extra.user = user.getId()
user.setVerified(false)
$app.dao().saveRecord(user)
dao.saveRecord(user)
audit('PBOUNCE', `User ${emailAddress} has been disabled`, {
dao,
extra,
log,
})
} catch (e) {
audit('PBOUNCE_ERR', `${e}`, { log, extra })
audit('PBOUNCE_ERR', `${e}`, { dao, log, extra })
log(`After audit`)
}
}
@ -62,6 +62,7 @@ routerAdd('POST', '/api/sns', (c) => {
break
default:
audit('SNS_ERR', `Unrecognized bounce type ${bounceType}`, {
dao,
log,
extra: { raw_payload: raw },
})
@ -82,11 +83,15 @@ routerAdd('POST', '/api/sns', (c) => {
.findFirstRecordByData('users', 'email', emailAddress)
log(`user is`, user)
user.set(`unsubscribe`, true)
$app.dao().saveRecord(user)
dao.saveRecord(user)
audit(
'COMPLAINT',
`User ${emailAddress} has been unsubscribed`,
{ log, extra: { email: emailAddress, user: user.getId() } },
{
log,
dao,
extra: { email: emailAddress, user: user.getId() },
},
)
} catch (e) {
audit(
@ -94,6 +99,7 @@ routerAdd('POST', '/api/sns', (c) => {
`${emailAddress} is not in the system.`,
{
log,
dao,
extra: {
email: emailAddress,
},
@ -109,6 +115,7 @@ routerAdd('POST', '/api/sns', (c) => {
`Unrecognized notification type ${notificationType}`,
{
log,
dao,
extra: { raw_payload: raw },
},
)
@ -117,6 +124,7 @@ routerAdd('POST', '/api/sns', (c) => {
default:
audit(`SNS_ERR`, `Message ${Type} not handled`, {
log,
dao,
extra: { raw_payload: raw },
})
}

View File

@ -1,18 +1,19 @@
/// <reference path="../types/types.d.ts" />
routerAdd('GET', '/api/unsubscribe', (c) => {
const dao = $app.dao()
const { mkLog, audit } = /** @type {Lib} */ (require(`${__hooks}/lib.js`))
const log = mkLog(`unsubscribe`)
const id = c.queryParam('e')
try {
const record = $app.dao().findRecordById('users', id)
const record = dao.findRecordById('users', id)
record.set(`unsubscribe`, true)
$app.dao().saveRecord(record)
dao.saveRecord(record)
const email = record.getString('email')
audit('UNSUBSCRIBE', '', { log, extra: { email, user: id } })
audit('UNSUBSCRIBE', '', { dao, log, extra: { email, user: id } })
$app.newMailClient().send(
new MailerMessage({
@ -26,7 +27,7 @@ routerAdd('GET', '/api/unsubscribe', (c) => {
)
return c.html(200, `<p>${email} has been unsubscribed.`)
} catch (e) {
audit('UNSUBSCRIBE_ERR', `User ${id} not found`, { log })
audit('UNSUBSCRIBE_ERR', `User ${id} not found`, { dao, log })
return c.html(200, `<p>Looks like you're already unsubscribed.`)
}
})

View File

@ -9,6 +9,7 @@ routerAdd(
'GET',
'/api/userToken/:id',
(c) => {
const dao = $app.dao()
const { mkLog } = /** @type {Lib} */ (require(`${__hooks}/lib.js`))
const log = mkLog(`user-token`)
@ -20,7 +21,7 @@ routerAdd(
throw new BadRequestError(`User ID is required.`)
}
const rec = $app.dao().findRecordById('users', id)
const rec = dao.findRecordById('users', id)
const tokenKey = rec.getString('tokenKey')
const passwordHash = rec.getString('passwordHash')
const email = rec.getString(`email`)

View File

@ -1,6 +1,7 @@
/// <reference path="../types/types.d.ts" />
onModelBeforeUpdate((e) => {
const dao = e.dao || $app.dao()
const newModel = /** @type {models.Record} */ (e.model)
const oldModel = newModel.originalCopy()
@ -22,10 +23,10 @@ onModelBeforeUpdate((e) => {
log(`user just became verified`)
const uid = newModel.getId()
enqueueNotification(`email`, `welcome`, uid, { log })
enqueueNotification(`email`, `welcome`, uid, { log, dao })
newModel.set(`welcome`, new DateTime())
} catch (e) {
audit(`ERROR`, `${e}`, { log, extra: { user: newModel.getId() } })
audit(`ERROR`, `${e}`, { log, dao, extra: { user: newModel.getId() } })
throw e
}
}, 'users')

View File

@ -19,7 +19,7 @@ interface Lib {
mkLog: (namespace: string) => Logger
processNotification: (
notificationRec: models.Record,
context: Partial<{ log: Logger; test?: boolean; dao: daos.Dao }>,
context: { log: Logger; test?: boolean; dao: daos.Dao },
) => void
enqueueNotification: (
channel: 'email' | 'lemonbot',
@ -29,27 +29,27 @@ interface Lib {
| 'lemon_order_discord'
| 'welcome',
user_id: string,
context?: Partial<{
message_template_vars: { [_: string]: string }
context: {
message_template_vars?: { [_: string]: string }
dao: daos.Dao
log: Logger
}>,
},
) => void
audit: (
event: AuditEvents,
note: string,
context?: Partial<{
context: {
log: Logger
dao: daos.Dao
extra: Partial<{
extra?: Partial<{
notification: string
email: string
user: string
raw_payload: string
}>
}>,
},
) => void
removeEmptyKeys: <T>(obj: T) => T
}