addition of new dashboard

This commit is contained in:
2026-08-20 15:08:32 +00:00
parent 0622018d95
commit bf59249ed7
161 changed files with 13170 additions and 119 deletions

View 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,
}