addition of new dashboard
This commit is contained in:
433
backend/modules/core/archive.js
Normal file
433
backend/modules/core/archive.js
Normal file
@@ -0,0 +1,433 @@
|
||||
import { getPool } from "../../db/index.js";
|
||||
|
||||
function isUuid(uuid) {
|
||||
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(uuid)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function getAuditErrors(audit) {
|
||||
if (typeof audit !== 'object') {
|
||||
return 'invalid audit object format';
|
||||
}
|
||||
|
||||
if (audit.version != 1) {
|
||||
return 'audit version invalid';
|
||||
}
|
||||
|
||||
if (audit.hashType != 'SHA256') {
|
||||
return 'audit hash type invalid';
|
||||
}
|
||||
|
||||
if (!audit.digest) {
|
||||
return 'audit digest should exist';
|
||||
}
|
||||
|
||||
if (isNaN(audit.created)) {
|
||||
return 'audit created type invalid';
|
||||
}
|
||||
|
||||
if (!audit.eventId && !audit.agreementId) {
|
||||
return 'eventId or agreementId should exist';
|
||||
}
|
||||
|
||||
if (audit.eventId && !isUuid(audit.eventId)) {
|
||||
return 'eventId invalid';
|
||||
}
|
||||
|
||||
if (audit.agreementId && !isUuid(audit.agreementId)) {
|
||||
return 'agreementId invalid';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async function processAudit(data) {
|
||||
const client = await getPool();
|
||||
try {
|
||||
let type = data.audit.eventId ? 'event' : 'agreement';
|
||||
const digest = data.audit.digest;
|
||||
let id = data.audit.eventId ? data.audit.eventId : data.audit.agreementId;
|
||||
await client.query(`BEGIN`);
|
||||
let res = await client.query(`
|
||||
SELECT 1
|
||||
FROM audit a
|
||||
WHERE a.${type}_id = $1
|
||||
AND digest = $2
|
||||
LIMIT 1;
|
||||
`, [
|
||||
id,
|
||||
digest,
|
||||
]);
|
||||
if (res.rows.length > 0) {
|
||||
throw new Error('audit record with id and digest exists');
|
||||
}
|
||||
res = await client.query(`
|
||||
INSERT INTO audit (
|
||||
version,
|
||||
event_id,
|
||||
agreement_id,
|
||||
hash_type,
|
||||
digest,
|
||||
created
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6
|
||||
) RETURNING audit_id;
|
||||
`, [
|
||||
data.audit.version,
|
||||
data.audit.eventId,
|
||||
data.audit.agreementId,
|
||||
data.audit.hashType,
|
||||
data.audit.digest,
|
||||
data.audit.created,
|
||||
]);
|
||||
for (const signature of data.signatures) {
|
||||
await client.query(`
|
||||
INSERT INTO audit_signature (
|
||||
audit_id,
|
||||
version,
|
||||
id,
|
||||
signedOn,
|
||||
type,
|
||||
jws
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6
|
||||
);
|
||||
`, [
|
||||
res.rows[0].audit_id,
|
||||
signature.version,
|
||||
signature.id,
|
||||
signature.signedOn,
|
||||
signature.type,
|
||||
signature.jws,
|
||||
]);
|
||||
}
|
||||
if (data.meta) {
|
||||
for await (const [key, value] of Object.entries(data.meta)) {
|
||||
await client.query(`
|
||||
INSERT INTO audit_meta (
|
||||
audit_id,
|
||||
key,
|
||||
value
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3
|
||||
);
|
||||
`, [
|
||||
res.rows[0].audit_id,
|
||||
key,
|
||||
value,
|
||||
]);
|
||||
}
|
||||
}
|
||||
await client.query('COMMIT');
|
||||
} catch(e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
await client.release();
|
||||
}
|
||||
}
|
||||
|
||||
async function getAuditsByAgreementId(client, id, offset, limit) {
|
||||
let ret = await client.query(`
|
||||
WITH origAudits AS (
|
||||
SELECT
|
||||
audit_id,
|
||||
version,
|
||||
agreement_id,
|
||||
hash_type,
|
||||
digest,
|
||||
created
|
||||
FROM audit
|
||||
WHERE agreement_id = $1
|
||||
ORDER BY created DESC
|
||||
OFFSET $2
|
||||
LIMIT $3
|
||||
),
|
||||
audits AS (
|
||||
SELECT
|
||||
a.*,
|
||||
JSON_AGG(json_build_object(
|
||||
'version', s.version,
|
||||
'id', s.id,
|
||||
'signedOn', s.signedOn,
|
||||
'type', s.type,
|
||||
'jws', s.jws
|
||||
)) AS signatures
|
||||
FROM
|
||||
origAudits a,
|
||||
audit_signature s
|
||||
WHERE s.audit_id = a.audit_id
|
||||
GROUP BY
|
||||
a.audit_id,
|
||||
a.version,
|
||||
a.agreement_id,
|
||||
a.hash_type,
|
||||
a.digest,
|
||||
a.created
|
||||
)
|
||||
SELECT
|
||||
JSON_AGG(json_build_object(
|
||||
'audit', json_build_object(
|
||||
'version', a.version,
|
||||
'agreementId', a.agreement_id,
|
||||
'hashType', a.hash_type,
|
||||
'digest', a.digest,
|
||||
'created', a.created
|
||||
),
|
||||
'signatures', a.signatures
|
||||
)) AS records
|
||||
FROM audits a
|
||||
`, [
|
||||
id,
|
||||
offset,
|
||||
limit,
|
||||
]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
async function getAuditsByEventId(client, id, offset, limit) {
|
||||
let ret = await client.query(`
|
||||
WITH origAudits AS (
|
||||
SELECT
|
||||
audit_id,
|
||||
version,
|
||||
event_id,
|
||||
hash_type,
|
||||
digest,
|
||||
created
|
||||
FROM audit
|
||||
WHERE event_id = $1
|
||||
ORDER BY created DESC
|
||||
OFFSET $2
|
||||
LIMIT $3
|
||||
),
|
||||
audits AS (
|
||||
SELECT
|
||||
a.*,
|
||||
JSON_AGG(json_build_object(
|
||||
'version', s.version,
|
||||
'id', s.id,
|
||||
'signedOn', s.signedOn,
|
||||
'type', s.type,
|
||||
'jws', s.jws
|
||||
)) AS signatures
|
||||
FROM
|
||||
origAudits a,
|
||||
audit_signature s
|
||||
WHERE s.audit_id = a.audit_id
|
||||
GROUP BY
|
||||
a.audit_id,
|
||||
a.version,
|
||||
a.event_id,
|
||||
a.hash_type,
|
||||
a.digest,
|
||||
a.created
|
||||
)
|
||||
SELECT
|
||||
JSON_AGG(json_build_object(
|
||||
'audit', json_build_object(
|
||||
'version', a.version,
|
||||
'eventId', a.event_id,
|
||||
'hashType', a.hash_type,
|
||||
'digest', a.digest,
|
||||
'created', a.created
|
||||
),
|
||||
'signatures', a.signatures
|
||||
)) AS records
|
||||
FROM audits a
|
||||
`, [
|
||||
id,
|
||||
offset,
|
||||
limit,
|
||||
]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
async function getAuditsByMeta(client, meta, offset, limit) {
|
||||
const fields = [offset, limit];
|
||||
let count = fields.length + 1;
|
||||
let whereInVals = ``
|
||||
for await (const [key, value] of Object.entries(meta)) {
|
||||
if (whereInVals != ``)
|
||||
whereInVals = ` AND `
|
||||
whereInVals += `(key = $${count++} AND value = $${count++})`;
|
||||
fields.push(key);
|
||||
fields.push(value);
|
||||
}
|
||||
let ret = await client.query(`
|
||||
WITH origAudits AS (
|
||||
SELECT
|
||||
audit_id,
|
||||
version,
|
||||
event_id,
|
||||
hash_type,
|
||||
digest,
|
||||
created
|
||||
FROM audit
|
||||
WHERE audit_id IN (
|
||||
SELECT audit_id
|
||||
FROM audit_meta
|
||||
WHERE ${whereInVals}
|
||||
)
|
||||
ORDER BY created DESC
|
||||
OFFSET $1
|
||||
LIMIT $2
|
||||
),
|
||||
audits AS (
|
||||
SELECT
|
||||
a.*,
|
||||
JSON_AGG(json_build_object(
|
||||
'version', s.version,
|
||||
'id', s.id,
|
||||
'signedOn', s.signedOn,
|
||||
'type', s.type,
|
||||
'jws', s.jws
|
||||
)) AS signatures
|
||||
FROM
|
||||
origAudits a,
|
||||
audit_signature s
|
||||
WHERE s.audit_id = a.audit_id
|
||||
GROUP BY
|
||||
a.audit_id,
|
||||
a.version,
|
||||
a.event_id,
|
||||
a.hash_type,
|
||||
a.digest,
|
||||
a.created
|
||||
)
|
||||
SELECT
|
||||
JSON_AGG(json_build_object(
|
||||
'audit', json_build_object(
|
||||
'version', a.version,
|
||||
'eventId', a.event_id,
|
||||
'hashType', a.hash_type,
|
||||
'digest', a.digest,
|
||||
'created', a.created
|
||||
),
|
||||
'signatures', a.signatures
|
||||
)) AS records
|
||||
FROM audits a
|
||||
`, fields);
|
||||
return ret;
|
||||
}
|
||||
|
||||
async function getAudits(eventId, agreementId, meta, offset) {
|
||||
let ret;
|
||||
let res;
|
||||
const limit = 100;
|
||||
const client = await getPool();
|
||||
try {
|
||||
if (eventId) {
|
||||
res = await getAuditsByEventId(client, eventId, offset, limit);
|
||||
} else if (agreementId) {
|
||||
res = await getAuditsByAgreementId(client, agreementId, offset, limit);
|
||||
} else {
|
||||
res = await getAuditsByMeta(client, meta, offset, limit);
|
||||
}
|
||||
if (res.rows.length > 0 && res.rows[0].records) {
|
||||
ret = {
|
||||
auditRecords: res.rows[0].records
|
||||
};
|
||||
if (ret.auditRecords.length === limit) {
|
||||
ret.nextPageToken = Buffer.from(`${offset + limit}`).toString("base64");
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
} finally {
|
||||
await client.release();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
async function put(input) {
|
||||
let res = {
|
||||
success: false,
|
||||
error: 'Unknown error',
|
||||
};
|
||||
try {
|
||||
const { audit, meta } = input;
|
||||
const error = getAuditErrors(audit);
|
||||
if (error) {
|
||||
res.error = error;
|
||||
console.log(`${prefix} - Bad Audit: ${error}`);
|
||||
} else {
|
||||
const id = audit.eventId ? audit.eventId : audit.agreementId;
|
||||
if (audit.eventId) {
|
||||
res.message = `event: ${audit.eventId}`;
|
||||
} else {
|
||||
res.message = `agreement: ${audit.agreementId}`;
|
||||
}
|
||||
await processAudit(input);
|
||||
res.success = true;
|
||||
delete res.error;
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.message != 'audit record with id and digest exists')
|
||||
console.error(e);
|
||||
res.error = e.message;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
async function get(input) {
|
||||
let res = {
|
||||
success: false,
|
||||
error: 'Unknown error',
|
||||
};
|
||||
try {
|
||||
let error;
|
||||
let id;
|
||||
let type;
|
||||
if (input.eventId) {
|
||||
id = input.eventId;
|
||||
type = 'event';
|
||||
error = isUuid(input.eventId) ? null : 'eventId invalid';
|
||||
} else if (input.agreementId) {
|
||||
id = input.agreementId;
|
||||
type = 'agreement';
|
||||
error = isUuid(input.agreementId) ? null : 'agreementId invalid';
|
||||
} else if (input.meta) {
|
||||
id = `<meta key/value>`;
|
||||
type = 'meta';
|
||||
} else {
|
||||
error = 'eventId, agreementId, or meta required';
|
||||
}
|
||||
if (error) {
|
||||
res.error = error;
|
||||
res.message = `bad request: ${error}`;
|
||||
} else {
|
||||
res.message = `requested ${type}: ${id}`;
|
||||
const offset = input.pageToken ? parseInt(Buffer.from(`${input.pageToken}`, "base64").toString('ascii')) : 0;
|
||||
let ret = await getAudits(input.eventId, input.agreementId, input.meta, offset);
|
||||
if (ret) {
|
||||
res.data = ret;
|
||||
res.success = true;
|
||||
delete res.error;
|
||||
} else {
|
||||
res.error = "not found"
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.message != 'audit record with id and digest exists')
|
||||
console.error(e);
|
||||
res.error = e.message;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
export const archive = {
|
||||
put,
|
||||
get,
|
||||
}
|
||||
Reference in New Issue
Block a user