addition of new dashboard
This commit is contained in:
558
backend/modules/core/data/agreement.js
Normal file
558
backend/modules/core/data/agreement.js
Normal file
@@ -0,0 +1,558 @@
|
||||
import pkg from '@jlinc/core';
|
||||
const { JlincAgreement, 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 getAgreement(client, userId, id, key) {
|
||||
const whereClause = id ? `a.agreement_id_uuid = $1` : `a.agreement_id_uuid = (SELECT agreement_uuid FROM agreement_content WHERE lower(key) = lower($1))`;
|
||||
const whereValue = id ? id : key;
|
||||
const sql = `
|
||||
SELECT
|
||||
JSON_BUILD_OBJECT(
|
||||
'version', a.version,
|
||||
'@context', a.context,
|
||||
'parent', a.parent,
|
||||
'references', (
|
||||
SELECT JSON_AGG(r.uri ORDER BY r.uri)
|
||||
FROM reference r
|
||||
INNER JOIN agreement_reference ar
|
||||
ON r.id = ar.reference_id
|
||||
AND ar.agreement_id = a.id
|
||||
AND (
|
||||
(r.user_id = a.user_id AND ar.user_id = a.user_id)
|
||||
OR
|
||||
(r.user_id IS NULL AND ar.user_id IS NULL AND a.user_id IS NULL)
|
||||
)
|
||||
),
|
||||
'agreementId', a.agreement_id_uuid,
|
||||
'created', a.created,
|
||||
'ids', (
|
||||
SELECT JSON_AGG(ari.required_id)
|
||||
FROM agreement_required_id ari
|
||||
WHERE ari.agreement_id = a.id
|
||||
),
|
||||
'purposes', (
|
||||
SELECT JSON_AGG(p.value ORDER BY p.value)
|
||||
FROM purpose p
|
||||
INNER JOIN agreement_purpose ap
|
||||
ON p.id = ap.purpose_id
|
||||
AND ap.agreement_id = a.id
|
||||
AND (
|
||||
(p.user_id = a.user_id AND ap.user_id = a.user_id)
|
||||
OR
|
||||
(p.user_id IS NULL AND ap.user_id IS NULL AND a.user_id IS NULL)
|
||||
)
|
||||
),
|
||||
'prohibitions', (
|
||||
SELECT JSON_AGG(c.value ORDER BY c.value)
|
||||
FROM prohibition c
|
||||
INNER JOIN agreement_prohibition ac
|
||||
ON c.id = ac.prohibition_id
|
||||
AND ac.agreement_id = a.id
|
||||
AND (
|
||||
(c.user_id = ac.user_id AND ac.user_id = a.user_id)
|
||||
OR
|
||||
(c.user_id IS NULL AND ac.user_id IS NULL AND a.user_id IS NULL)
|
||||
)
|
||||
),
|
||||
'validRoles', (
|
||||
SELECT JSON_AGG(r.value ORDER BY r.value)
|
||||
FROM role r
|
||||
INNER JOIN agreement_role ar
|
||||
ON r.id = ar.role_id
|
||||
AND ar.agreement_id = a.id
|
||||
AND (
|
||||
(r.user_id = ar.user_id AND ar.user_id = a.user_id)
|
||||
OR
|
||||
(r.user_id IS NULL AND ar.user_id IS NULL AND a.user_id IS NULL)
|
||||
)
|
||||
)
|
||||
) AS record
|
||||
FROM agreement a
|
||||
WHERE ${whereClause}
|
||||
AND (
|
||||
(
|
||||
a.user_id = $2
|
||||
OR a.user_id IS NULL
|
||||
)
|
||||
OR a.public IS TRUE
|
||||
)
|
||||
`
|
||||
const res = await client.query(sql, [
|
||||
whereValue,
|
||||
userId,
|
||||
]);
|
||||
if (res.rows.length > 0 && res.rows[0].record) {
|
||||
const ret = res.rows[0].record;
|
||||
if (!ret.ids) ret.ids = [];
|
||||
if (!ret.purposes) ret.purposes = [];
|
||||
if (!ret.prohibitions) ret.prohibitions = [];
|
||||
if (!ret.validRoles) ret.validRoles = [];
|
||||
if (ret.version == 1) {
|
||||
delete ret.references;
|
||||
} else {
|
||||
delete ret["@context"];
|
||||
delete ret.parent;
|
||||
ret.permitted = ret.purposes;
|
||||
delete ret.purposes;
|
||||
ret.prohibited = ret.prohibitions;
|
||||
delete ret.prohibitions;
|
||||
}
|
||||
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,
|
||||
'role', (
|
||||
SELECT r.value
|
||||
FROM role r
|
||||
WHERE r.id = s.role_id
|
||||
AND r.user_id = $2
|
||||
)
|
||||
)
|
||||
) AS records
|
||||
FROM signature s
|
||||
INNER JOIN agreement a ON s.agreement_id = a.id
|
||||
WHERE a.agreement_id_uuid = $1
|
||||
AND a.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 existingAgreement = await getAgreement(client, userId, input.agreementId, input.key)
|
||||
if (!existingAgreement) {
|
||||
response.error = 'agreement not found'
|
||||
} else {
|
||||
const data = {
|
||||
agreement: existingAgreement
|
||||
}
|
||||
if (input.includeSignatures) {
|
||||
data.signatures = await getSignatures(client, userId, input.agreementId)
|
||||
for (let x = 0; x < data.signatures.length; x++) {
|
||||
if (data.signatures[x].role === null) {
|
||||
delete data.signatures[x].role;
|
||||
}
|
||||
}
|
||||
}
|
||||
response = {
|
||||
message: `retrieved: ${input.agreementId}`,
|
||||
data,
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
await client.release();
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
async function save(client, userId, agreement, publicAgreement) {
|
||||
await client.query(`BEGIN`);
|
||||
const isPublic = publicAgreement ? true : false;
|
||||
const res = await client.query(`
|
||||
INSERT INTO agreement (
|
||||
user_id,
|
||||
version,
|
||||
parent,
|
||||
agreement_id_uuid,
|
||||
context,
|
||||
public,
|
||||
created,
|
||||
created_as_ts
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
$7,
|
||||
$8
|
||||
) RETURNING id;
|
||||
`, [
|
||||
userId,
|
||||
agreement.version,
|
||||
agreement.parent,
|
||||
agreement.agreementId,
|
||||
agreement["@context"],
|
||||
isPublic,
|
||||
agreement.created,
|
||||
new Date(agreement.created).toISOString(),
|
||||
]);
|
||||
for (const id of agreement.ids) {
|
||||
await client.query(`
|
||||
INSERT INTO agreement_required_id (
|
||||
user_id,
|
||||
agreement_id,
|
||||
required_id
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3
|
||||
);
|
||||
`, [
|
||||
userId,
|
||||
res.rows[0].id,
|
||||
id,
|
||||
]);
|
||||
}
|
||||
if (agreement.references) {
|
||||
for (const reference of agreement.references) {
|
||||
await client.query(`
|
||||
INSERT INTO reference (
|
||||
user_id,
|
||||
uri
|
||||
) VALUES (
|
||||
$1,
|
||||
$2
|
||||
) ON CONFLICT DO NOTHING;
|
||||
`, [
|
||||
userId,
|
||||
reference,
|
||||
]);
|
||||
await client.query(`
|
||||
INSERT INTO agreement_reference (
|
||||
user_id,
|
||||
agreement_id,
|
||||
reference_id
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
(
|
||||
SELECT id
|
||||
FROM reference
|
||||
WHERE uri = $3
|
||||
AND user_id ${userId ? `= $1` : `IS NULL`}
|
||||
)
|
||||
);
|
||||
`, [
|
||||
userId,
|
||||
res.rows[0].id,
|
||||
reference,
|
||||
]);
|
||||
}
|
||||
}
|
||||
const purposes = agreement.version == 1 ? agreement.purposes : agreement.permitted;
|
||||
for (const purpose of purposes) {
|
||||
await client.query(`
|
||||
INSERT INTO purpose (
|
||||
user_id,
|
||||
value
|
||||
) VALUES (
|
||||
$1,
|
||||
$2
|
||||
) ON CONFLICT DO NOTHING;
|
||||
`, [
|
||||
userId,
|
||||
purpose,
|
||||
]);
|
||||
await client.query(`
|
||||
INSERT INTO agreement_purpose (
|
||||
user_id,
|
||||
agreement_id,
|
||||
purpose_id
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
(
|
||||
SELECT id
|
||||
FROM purpose
|
||||
WHERE value = $3
|
||||
AND user_id ${userId ? `= $1` : `IS NULL`}
|
||||
)
|
||||
);
|
||||
`, [
|
||||
userId,
|
||||
res.rows[0].id,
|
||||
purpose,
|
||||
]);
|
||||
}
|
||||
const prohibitions = agreement.version == 1 ? agreement.prohibitions : agreement.prohibited;
|
||||
for (const prohibition of prohibitions) {
|
||||
await client.query(`
|
||||
INSERT INTO prohibition (
|
||||
user_id,
|
||||
value
|
||||
) VALUES (
|
||||
$1,
|
||||
$2
|
||||
) ON CONFLICT DO NOTHING;
|
||||
`, [
|
||||
userId,
|
||||
prohibition,
|
||||
]);
|
||||
await client.query(`
|
||||
INSERT INTO agreement_prohibition (
|
||||
user_id,
|
||||
agreement_id,
|
||||
prohibition_id
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
(
|
||||
SELECT id
|
||||
FROM prohibition
|
||||
WHERE value = $3
|
||||
AND user_id ${userId ? `= $1` : `IS NULL`}
|
||||
)
|
||||
);
|
||||
`, [
|
||||
userId,
|
||||
res.rows[0].id,
|
||||
prohibition,
|
||||
]);
|
||||
}
|
||||
for (const role of agreement.validRoles) {
|
||||
await client.query(`
|
||||
INSERT INTO role (
|
||||
user_id,
|
||||
value
|
||||
) VALUES (
|
||||
$1,
|
||||
$2
|
||||
) ON CONFLICT DO NOTHING;
|
||||
`, [
|
||||
userId,
|
||||
role,
|
||||
]);
|
||||
await client.query(`
|
||||
INSERT INTO agreement_role (
|
||||
user_id,
|
||||
agreement_id,
|
||||
role_id
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
(
|
||||
SELECT id
|
||||
FROM role
|
||||
WHERE value = $3
|
||||
AND user_id ${userId ? `= $1` : `IS NULL`}
|
||||
)
|
||||
);
|
||||
`, [
|
||||
userId,
|
||||
res.rows[0].id,
|
||||
role,
|
||||
]);
|
||||
}
|
||||
await client.query('COMMIT');
|
||||
}
|
||||
|
||||
async function saveSignatures(client, userId, agreementId, 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
|
||||
),
|
||||
(
|
||||
SELECT id
|
||||
FROM agreement
|
||||
WHERE agreement_id_uuid = $8
|
||||
AND user_id = $1
|
||||
),
|
||||
$9,
|
||||
$10
|
||||
) ON CONFLICT DO NOTHING;
|
||||
`, [
|
||||
userId,
|
||||
signature.version,
|
||||
signature.id,
|
||||
signature.signedOn,
|
||||
signature.type,
|
||||
signature.jws,
|
||||
signature.role,
|
||||
agreementId,
|
||||
null,
|
||||
new Date(signature.signedOn).toISOString(),
|
||||
]);
|
||||
}
|
||||
await client.query('COMMIT');
|
||||
}
|
||||
|
||||
async function create(input, userId, _client, uuid) {
|
||||
let response = {
|
||||
success: false,
|
||||
error: 'Unknown error',
|
||||
};
|
||||
const client = _client || await getPool();
|
||||
try {
|
||||
input.didDocs = [];
|
||||
for (const shortName of input.shortNames) {
|
||||
input.didDocs.push((await entity.getEntity(client, userId, shortName)).didDoc)
|
||||
}
|
||||
delete input.shortNames;
|
||||
if (input.caveats) {
|
||||
input.prohibitions = input.caveats;
|
||||
delete input.caveats;
|
||||
}
|
||||
const agreement = await JlincAgreement.create(input);
|
||||
if (uuid) {
|
||||
agreement.agreementId = uuid;
|
||||
}
|
||||
await save(client, userId, agreement, input.public);
|
||||
for (const uri of (agreement.references || [])) {
|
||||
await cacheAgreementMarkdown({ userId, agreementUuid: agreement.agreementId, uri }, client);
|
||||
}
|
||||
response = {
|
||||
message: `created and saved: ${agreement.agreementId}`,
|
||||
data: agreement,
|
||||
}
|
||||
} catch(e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
if (!_client)
|
||||
await client.release();
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
async function process(input, userId, _client, _agreement) {
|
||||
let response = {
|
||||
success: false,
|
||||
error: 'Unknown error',
|
||||
};
|
||||
const client = _client || await getPool();
|
||||
try {
|
||||
const existingAgreement = _agreement || await getAgreement(client, userId, input.agreementId)
|
||||
if (!existingAgreement) {
|
||||
throw new Error('agreement does not exist')
|
||||
}
|
||||
const inputEntity = 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 = {
|
||||
agreement: existingAgreement,
|
||||
didDoc,
|
||||
signingKey,
|
||||
signingPublicKey,
|
||||
role: input.role,
|
||||
}
|
||||
const agreementData = await JlincAgreement.sign(signingInput);
|
||||
await saveSignatures(client, userId, existingAgreement.agreementId, agreementData.signatures);
|
||||
const audit = await JlincAudit.create(agreementData);
|
||||
const auditInput = {
|
||||
audit,
|
||||
didDoc,
|
||||
signingKey,
|
||||
signingPublicKey,
|
||||
}
|
||||
const auditData = await JlincAudit.sign(auditInput);
|
||||
response = {
|
||||
message: `signed and saved: ${agreementData?.agreement?.agreementId}`,
|
||||
data: {
|
||||
auditData,
|
||||
},
|
||||
}
|
||||
if (input.archive) {
|
||||
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 created = (await create(input.data, userId, client)).data;
|
||||
const processed = (await process(
|
||||
{
|
||||
agreementId: created.agreementId,
|
||||
shortName: input.shortName,
|
||||
role: input.role,
|
||||
archive: input.archive,
|
||||
},
|
||||
userId,
|
||||
client,
|
||||
created,
|
||||
)).data;
|
||||
response = {
|
||||
message: `created and processed: ${created.agreementId}`,
|
||||
data: {
|
||||
created,
|
||||
processed,
|
||||
},
|
||||
}
|
||||
} catch(e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
await client.release();
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
export const agreement = {
|
||||
getAgreement,
|
||||
getSignatures,
|
||||
get,
|
||||
create,
|
||||
process,
|
||||
produce,
|
||||
}
|
||||
Reference in New Issue
Block a user