Files
2026-08-20 15:08:32 +00:00

173 lines
5.1 KiB
JavaScript

import { getPool } from "../../../db/index.js";
import { createHash } from "crypto";
import { getConfig } from "../../../common/config.js";
import axios from "axios";
import sodium from "sodium-native";
async function getEntity(client, userId, shortName) {
const sql = `
SELECT
JSON_BUILD_OBJECT(
'fedidUrl', e.fedid_url,
'shortName', e.short_name,
'didId', e.did_id,
'controlPrivateKeyB64U', e.control_private_key_b64u,
'recoveryPrivateKeyB64U', e.recovery_private_key_b64u,
'didDoc', e.did_doc
) AS record
FROM entity e
WHERE LOWER(e.short_name) = LOWER($1)
AND e.user_id = $2;
`
const res = await client.query(sql, [
shortName,
userId,
]);
if (res.rows.length > 0 && res.rows[0].record) {
return res.rows[0].record;
}
return null;
}
async function get(input, userId) {
let response = {
success: false,
error: 'Unknown error',
};
const client = await getPool();
try {
const existingEntity = await getEntity(client, userId, input.shortName)
if (!existingEntity) {
response.error = 'entity not found'
} else {
const data = {
didDoc: existingEntity.didDoc,
}
response = {
message: `retrieved: ${input.shortName}`,
data,
}
}
} catch(e) {
console.error(e)
} finally {
await client.release();
}
return response;
}
async function getDomains(input, userId) {
let response = {
success: false,
error: 'Unknown error',
};
try {
const config = getConfig();
const fedidUrl = input.fedidUrl ?? config.defaultFedidUrl;
const data = (await axios.get(
`${fedidUrl}/api/v2/domains`,
)).data.data.domains;
response = {
message: `retrieved domains`,
data,
}
} catch(e) {
console.error(e)
}
return response;
}
async function save(client, userId, entity) {
const res = await client.query(`
INSERT INTO entity (
user_id,
fedid_url,
short_name,
did_id,
control_private_key_b64u,
recovery_private_key_b64u,
did_doc
) VALUES (
$1,
$2,
$3,
$4,
$5,
$6,
$7
) RETURNING id;
`, [
userId,
entity.fedidUrl,
entity.shortName,
entity.didDoc.id,
entity.controlPrivateKeyB64U,
entity.recoveryPrivateKeyB64U,
entity.didDoc,
]);
}
async function create(input, userId) {
let response = {
success: false,
error: 'Unknown error',
};
const client = await getPool();
try {
const config = getConfig();
const fedidUrl = input.fedidUrl ?? config.defaultFedidUrl;
const domains = (await axios.get(
`${fedidUrl}/api/v2/domains`,
)).data.data.domains;
const shortName = input.shortName.split('@')
const domain = shortName[1]
if (!domains.includes(domain)) {
throw new Error('domain is not available');
}
const entity = {
shortName: input.shortName,
fedidUrl,
}
// Generate a control key
entity.controlPublicKey = Buffer.alloc(sodium.crypto_sign_PUBLICKEYBYTES);
entity.controlPrivateKey = Buffer.alloc(sodium.crypto_sign_SECRETKEYBYTES);
sodium.crypto_sign_keypair(entity.controlPublicKey, entity.controlPrivateKey);
entity.controlPublicKeyB64U = entity.controlPublicKey.toString("base64url");
entity.controlPrivateKeyB64U = entity.controlPrivateKey.toString("base64url");
// Generate a recovery key
entity.recoveryPublicKey = Buffer.alloc(sodium.crypto_sign_PUBLICKEYBYTES);
entity.recoveryPrivateKey = Buffer.alloc(sodium.crypto_sign_SECRETKEYBYTES);
entity.recoveryPrivateKeyB64U = entity.recoveryPrivateKey.toString("base64url");
sodium.crypto_sign_keypair(entity.recoveryPublicKey, entity.recoveryPrivateKey);
entity.recoveryHash = createHash("sha256").update(entity.recoveryPublicKey).digest("hex").slice(0, 48);
entity.didDoc = (await axios.post(
`${fedidUrl}/api/v2/did/create`,
{
shortName: entity.shortName,
control: entity.controlPublicKeyB64U,
recoveryHash: entity.recoveryHash,
},
)).data.data.didDoc;
await save(client, userId, entity);
response = {
message: `created and saved: ${entity.didDoc.id}`,
data: {
didDoc: entity.didDoc,
},
}
} catch(e) {
console.error(e);
response.error = e.message;
} finally {
await client.release();
}
return response;
}
export const entity = {
getEntity,
getDomains,
get,
create,
}