addition of new dashboard
This commit is contained in:
413
backend/modules/core/data/event.js
Normal file
413
backend/modules/core/data/event.js
Normal file
@@ -0,0 +1,413 @@
|
||||
import pkg from '@jlinc/core';
|
||||
const { JlincEvent, JlincAudit } = pkg;
|
||||
import { getPool } from "../../../db/index.js";
|
||||
import { entity } from "./entity.js";
|
||||
import { putQueue } from "../../../common/queue.js";
|
||||
import { cacheAgreementMarkdown } from "../../../util/cacheAgreement.js";
|
||||
|
||||
async function getEvent(client, userId, id, includeData, meta) {
|
||||
const dataSql = includeData
|
||||
? `
|
||||
'data', (
|
||||
SELECT ed.data
|
||||
FROM event_data ed
|
||||
WHERE ed.event_id = e.id
|
||||
),
|
||||
`
|
||||
: ``;
|
||||
let fields = [userId];
|
||||
let count = fields.length + 1;
|
||||
let whereAnd = ``
|
||||
if (id) {
|
||||
whereAnd += ` AND e.event_id_uuid = $${count++}`;
|
||||
fields.push(id);
|
||||
}
|
||||
if (meta) {
|
||||
let whereInVals = ``
|
||||
for await (const [key, value] of Object.entries(meta)) {
|
||||
if (whereInVals != ``)
|
||||
whereInVals = ` AND `
|
||||
whereInVals += `(em.key = $${count++} AND em.value = $${count++})`;
|
||||
fields.push(key);
|
||||
fields.push(value);
|
||||
}
|
||||
whereAnd += `
|
||||
AND e.id IN (
|
||||
SELECT em.event_id
|
||||
FROM event_meta em
|
||||
WHERE em.user_id = $1
|
||||
AND ${whereInVals}
|
||||
)
|
||||
`
|
||||
}
|
||||
const sql = `
|
||||
SELECT
|
||||
JSON_BUILD_OBJECT(
|
||||
'version', e.version,
|
||||
'eventId', e.event_id_uuid,
|
||||
'type', (
|
||||
SELECT et.value
|
||||
FROM event_type et
|
||||
WHERE et.id = e.event_type_id
|
||||
),
|
||||
'senderId', e.sender_id,
|
||||
'recipientId', e.recipient_id,
|
||||
'created', e.created,
|
||||
'agreementId', (
|
||||
SELECT a.agreement_id_uuid
|
||||
FROM agreement a
|
||||
WHERE a.id = e.agreement_id
|
||||
AND a.user_id = $1
|
||||
),
|
||||
${dataSql}
|
||||
'created', e.created
|
||||
) AS record
|
||||
FROM event e
|
||||
WHERE e.user_id = $1
|
||||
${whereAnd};
|
||||
`
|
||||
const res = await client.query(sql, fields);
|
||||
if (res.rows.length > 0 && res.rows[0].record) {
|
||||
const ret = res.rows[0].record;
|
||||
if (ret.data) {
|
||||
try {
|
||||
ret.data = JSON.parse(ret.data);
|
||||
} catch(e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function getSignatures(client, userId, id) {
|
||||
const sql = `
|
||||
SELECT
|
||||
JSON_AGG(
|
||||
JSON_BUILD_OBJECT(
|
||||
'version', s.version,
|
||||
'id', s.signer_id,
|
||||
'signedOn', s.signed_on,
|
||||
'type', s.type,
|
||||
'jws', s.jws
|
||||
)
|
||||
) AS records
|
||||
FROM signature s
|
||||
INNER JOIN event e ON s.event_id = e.id
|
||||
WHERE e.event_id_uuid = $1
|
||||
AND e.user_id = $2;
|
||||
`
|
||||
const res = await client.query(sql, [
|
||||
id,
|
||||
userId,
|
||||
]);
|
||||
if (res.rows.length > 0 && res.rows[0].records) {
|
||||
return res.rows[0].records;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function get(input, userId) {
|
||||
let response = {
|
||||
success: false,
|
||||
error: 'Unknown error',
|
||||
};
|
||||
const client = await getPool();
|
||||
try {
|
||||
const existingEvent = await getEvent(client, userId, input.eventId, true, input.meta)
|
||||
if (!existingEvent) {
|
||||
response.error = 'event not found'
|
||||
} else {
|
||||
const data = {
|
||||
event: existingEvent
|
||||
}
|
||||
if (input.includeSignatures) {
|
||||
data.signatures = await getSignatures(client, userId, input.eventId)
|
||||
}
|
||||
response = {
|
||||
message: `retrieved: ${existingEvent.eventId}`,
|
||||
data,
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
await client.release();
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
async function save(client, userId, event, meta) {
|
||||
await client.query(`BEGIN`);
|
||||
const res = await client.query(`
|
||||
INSERT INTO event (
|
||||
user_id,
|
||||
version,
|
||||
event_id_uuid,
|
||||
event_type_id,
|
||||
agreement_id,
|
||||
sender_id,
|
||||
recipient_id,
|
||||
created,
|
||||
created_as_ts
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
(
|
||||
SELECT id
|
||||
FROM event_type
|
||||
WHERE value = $4
|
||||
),
|
||||
(
|
||||
SELECT id
|
||||
FROM agreement
|
||||
WHERE agreement_id_uuid = $5
|
||||
),
|
||||
$6,
|
||||
$7,
|
||||
$8,
|
||||
$9
|
||||
) RETURNING id;
|
||||
`, [
|
||||
userId,
|
||||
event.version,
|
||||
event.eventId,
|
||||
event.type,
|
||||
event.agreementId,
|
||||
event.senderId,
|
||||
event.recipientId,
|
||||
event.created,
|
||||
new Date(event.created).toISOString(),
|
||||
]);
|
||||
await client.query(`
|
||||
INSERT INTO event_data (
|
||||
user_id,
|
||||
event_id,
|
||||
data
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3
|
||||
);
|
||||
`, [
|
||||
userId,
|
||||
res.rows[0].id,
|
||||
event.data,
|
||||
]);
|
||||
if (meta) {
|
||||
for await (const [key, value] of Object.entries(meta)) {
|
||||
await client.query(`
|
||||
INSERT INTO event_meta (
|
||||
user_id,
|
||||
event_id,
|
||||
key,
|
||||
value
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4
|
||||
);
|
||||
`, [
|
||||
userId,
|
||||
res.rows[0].id,
|
||||
key,
|
||||
value,
|
||||
]);
|
||||
}
|
||||
}
|
||||
await client.query('COMMIT');
|
||||
}
|
||||
|
||||
async function saveSignatures(client, userId, eventId, signatures) {
|
||||
await client.query(`BEGIN`);
|
||||
for (const signature of signatures) {
|
||||
await client.query(`
|
||||
INSERT INTO signature (
|
||||
user_id,
|
||||
version,
|
||||
signer_id,
|
||||
signed_on,
|
||||
type,
|
||||
jws,
|
||||
role_id,
|
||||
agreement_id,
|
||||
event_id,
|
||||
signed_on_as_ts
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
(
|
||||
SELECT id
|
||||
FROM role
|
||||
WHERE value = $7
|
||||
AND user_id = $1
|
||||
),
|
||||
$8,
|
||||
(
|
||||
SELECT id
|
||||
FROM event
|
||||
WHERE event_id_uuid = $9
|
||||
AND user_id = $1
|
||||
),
|
||||
$10
|
||||
) ON CONFLICT DO NOTHING;
|
||||
`, [
|
||||
userId,
|
||||
signature.version,
|
||||
signature.id,
|
||||
signature.signedOn,
|
||||
signature.type,
|
||||
signature.jws,
|
||||
signature.role,
|
||||
null,
|
||||
eventId,
|
||||
new Date(signature.signedOn).toISOString(),
|
||||
]);
|
||||
}
|
||||
await client.query('COMMIT');
|
||||
}
|
||||
|
||||
async function create(input, userId, _client, _sender) {
|
||||
let response = {
|
||||
success: false,
|
||||
error: 'Unknown error',
|
||||
};
|
||||
const client = _client || await getPool();
|
||||
try {
|
||||
input.senderId = _sender ? _sender.didDoc.id : (await entity.getEntity(client, userId, input.senderShortName)).didDoc.id
|
||||
input.recipientId = (await entity.getEntity(client, userId, input.recipientShortName)).didDoc.id
|
||||
delete input.senderShortName
|
||||
delete input.recipientShortName
|
||||
const event = await JlincEvent.create(input);
|
||||
await save(client, userId, event, input.meta);
|
||||
// Pull down any referenced agreement content now (request refs are URIs,
|
||||
// permission refs are { reference, permitted }), so a get never has to.
|
||||
for (const ref of (event.references || [])) {
|
||||
const uri = typeof ref === "string" ? ref : ref?.reference;
|
||||
await cacheAgreementMarkdown({ userId, agreementUuid: event.agreementId, uri }, client);
|
||||
}
|
||||
response = {
|
||||
message: `created and saved: ${event.eventId}`,
|
||||
data: event,
|
||||
}
|
||||
} catch(e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
if (!_client)
|
||||
await client.release();
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
async function process(input, userId, _client, _event, _sender, meta) {
|
||||
let response = {
|
||||
success: false,
|
||||
error: 'Unknown error',
|
||||
};
|
||||
const client = _client || await getPool();
|
||||
try {
|
||||
const existingEvent = _event || await getEvent(client, userId, input.eventId, true)
|
||||
if (!existingEvent) {
|
||||
throw new Error('event does not exist')
|
||||
}
|
||||
const inputEntity = _sender || input.shortName ? await entity.getEntity(client, userId, input.shortName) : null;
|
||||
const didDoc = inputEntity ? inputEntity.didDoc : input.didDoc;
|
||||
const signingKey = inputEntity ? inputEntity.controlPrivateKeyB64U : input.signingKey;
|
||||
const signingPublicKey = inputEntity ? inputEntity.didDoc.verificationMethod[0].key : input.signingPublicKey;
|
||||
const signingInput = {
|
||||
event: existingEvent,
|
||||
didDoc,
|
||||
signingKey,
|
||||
signingPublicKey,
|
||||
}
|
||||
const eventData = await JlincEvent.sign(signingInput);
|
||||
await saveSignatures(client, userId, existingEvent.eventId, eventData.signatures);
|
||||
const audit = await JlincAudit.create(eventData);
|
||||
const auditInput = {
|
||||
audit,
|
||||
didDoc,
|
||||
signingKey,
|
||||
signingPublicKey,
|
||||
}
|
||||
const auditData = await JlincAudit.sign(auditInput);
|
||||
response = {
|
||||
message: `signed and saved: ${eventData?.event?.eventId}`,
|
||||
data: {
|
||||
auditData,
|
||||
},
|
||||
}
|
||||
if (input.archive) {
|
||||
if (meta) {
|
||||
response.data.auditData.meta = meta;
|
||||
}
|
||||
await putQueue(
|
||||
client,
|
||||
'audit',
|
||||
`${input.archive.url}/api/v1/audit/put`,
|
||||
{
|
||||
'Authorization': `Bearer ${input.archive.key}`,
|
||||
},
|
||||
response.data.auditData,
|
||||
)
|
||||
}
|
||||
} catch(e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
if (!_client)
|
||||
await client.release();
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
async function produce(input, userId) {
|
||||
let response = {
|
||||
success: false,
|
||||
error: 'Unknown error',
|
||||
};
|
||||
const client = await getPool();
|
||||
try {
|
||||
const shortName = input.senderShortName;
|
||||
const sender = (await entity.getEntity(client, userId, input.senderShortName))
|
||||
const created = (await create(input, userId, client, sender)).data;
|
||||
const processed = (await process(
|
||||
{
|
||||
eventId: created.eventId,
|
||||
shortName,
|
||||
archive: input.archive,
|
||||
},
|
||||
userId,
|
||||
client,
|
||||
created,
|
||||
sender,
|
||||
input.meta,
|
||||
)).data;
|
||||
response = {
|
||||
message: `created and processed: ${created.eventId}`,
|
||||
data: {
|
||||
created,
|
||||
processed,
|
||||
},
|
||||
}
|
||||
} catch(e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
await client.release();
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
export const event = {
|
||||
getEvent,
|
||||
getSignatures,
|
||||
get,
|
||||
create,
|
||||
process,
|
||||
produce,
|
||||
}
|
||||
Reference in New Issue
Block a user