Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft node fix #366

Merged
merged 8 commits into from
Jun 19, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "NodeAttestation" ALTER COLUMN "nodeDpid10" DROP NOT NULL;
2 changes: 1 addition & 1 deletion desci-server/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -752,7 +752,7 @@ model NodeAttestation {
community DesciCommunity @relation(fields: [desciCommunityId], references: [id])
claimedById Int
claimedBy User @relation(fields: [claimedById], references: [id])
nodeDpid10 String
nodeDpid10 String?
nodeUuid String
node Node @relation(fields: [nodeUuid], references: [uuid])
nodeVersion Int
Expand Down
2 changes: 1 addition & 1 deletion desci-server/src/controllers/attestations/claims.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export const removeClaim = async (req: RequestWithUser, res: Response, _next: Ne
const node = await prisma.node.findFirst({ where: { uuid: body.nodeUuid } });
if (!node) throw new NotFoundError('Node not found');

const claim = await attestationService.getClaimOnDpid(body.claimId, body.dpid.toString());
const claim = await attestationService.getClaimOnUuid(body.claimId, body.nodeUuid);
if (!claim) throw new NotFoundError();

if (node.ownerId !== req.user.id || claim.claimedById !== req.user.id) throw new AuthFailureError();
Expand Down
10 changes: 5 additions & 5 deletions desci-server/src/controllers/attestations/show.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,21 @@ type ShowNodeAttestationsResponse = {
};

export const showNodeAttestations = async (
req: Request<{ dpid: string }>,
req: Request<{ uuid: string }>,
res: Response<ShowNodeAttestationsResponse>,
) => {
const { dpid } = req.params;
const { uuid } = req.params;

const logger = parentLogger.child({
module: 'ATTESTATIONS::showNodeAttestationsController',
user: (req as any).user,
dpid,
uuid,
});
logger.trace(`showNodeAttestations`);

if (!dpid) throw new BadRequestError('DPID is required');
if (!uuid) throw new BadRequestError('uuid is required');

let attestations = await attestationService.getAllNodeAttestations(dpid);
let attestations = await attestationService.getAllNodeAttestations(uuid);
attestations = attestations.map((att) => ({
...att,
_count: undefined,
Expand Down
136 changes: 54 additions & 82 deletions desci-server/src/controllers/nodes/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
} from '../../services/nodeManager.js';
import { discordNotify } from '../../utils/discordUtils.js';
import { ensureUuidEndsWithDot } from '../../utils.js';

import { getOrCreateDpid, upgradeDpid } from './createDpid.js';

export type PublishReqBody = {
Expand All @@ -40,13 +41,23 @@ export type PublishResBody =
| {
error: string;
};

async function updateAssociatedAttestations(nodeUuid: string, dpid: string) {
const logger = parentLogger.child({
// id: req.id,
module: 'NODE::publishController',
});
logger.info({ nodeUuid, dpid }, `[updateAssociatedAttestations]`);
return await prisma.nodeAttestation.updateMany({
where: {
nodeUuid,
},
data: {
nodeDpid10: dpid,
},
});
}
// call node publish service and add job to queue
export const publish = async (
req: PublishRequest,
res: Response<PublishResBody>,
_next: NextFunction
) => {
export const publish = async (req: PublishRequest, res: Response<PublishResBody>, _next: NextFunction) => {
const { uuid, cid, manifest, transactionId, ceramicStream, commitId, useNewPublish } = req.body;
// debugger;
const email = req.user.email;
Expand Down Expand Up @@ -112,19 +123,8 @@ export const publish = async (
let publishTask: PublishTaskQueue | undefined;

if (useNewPublish) {
logger.info(
{ceramicStream, commitId, uuid, owner: owner.id},
"Triggering new publish flow"
);
await syncPublish(
ceramicStream,
commitId,
node,
owner,
cid,
uuid,
manifest,
);
logger.info({ ceramicStream, commitId, uuid, owner: owner.id }, 'Triggering new publish flow');
await syncPublish(ceramicStream, commitId, node, owner, cid, uuid, manifest);
} else {
publishTask = await prisma.publishTaskQueue.create({
data: {
Expand All @@ -138,7 +138,7 @@ export const publish = async (
status: PublishTaskQueueStatus.WAITING,
},
});
};
}

saveInteraction(
req,
Expand All @@ -156,14 +156,15 @@ export const publish = async (
owner.id,
);

updateAssociatedAttestations(node.uuid, manifest.dpid.id);
return res.send({
ok: true,
taskId: publishTask?.id,
});
} catch (err) {
logger.error({ err }, '[publish::publish] node-publish-err');
return res.status(400).send({ ok: false, error: err.message });
};
}
};

/**
Expand All @@ -176,7 +177,7 @@ export const publish = async (
*
* Semantically, these can both be made fire-and-forget promises if we can
* manage without instantly having the dPID alias available in this function.
*/
*/
const syncPublish = async (
ceramicStream: string,
commitId: string,
Expand All @@ -200,14 +201,12 @@ const syncPublish = async (
nodeId: node.id,
},
orderBy: {
id: "desc",
id: 'desc',
},
});

// Prevent duplicating the NodeVersion entry if the latest version is the same as the one we're trying to publish, as a draft save is triggered before publishing
const latestNodeVersionId = latestNodeVersion?.manifestUrl === cid
? latestNodeVersion.id
: -1;
const latestNodeVersionId = latestNodeVersion?.manifestUrl === cid ? latestNodeVersion.id : -1;

const nodeVersion = await prisma.nodeVersion.upsert({
where: {
Expand All @@ -233,7 +232,7 @@ const syncPublish = async (
`[publish:publish] stream on record does not match passed streamID`,
{ database: node.ceramicStream, ceramicStream },
);
};
}

const legacyDpid = manifest.dpid?.id ? parseInt(manifest.dpid.id) : undefined;
let dpidAlias: number = node.dpidAlias;
Expand All @@ -245,11 +244,8 @@ const syncPublish = async (
// The only reason this isn't just fire-and-forget is that we want the dpid
// for the discord notification, which won't be available otherwise for
// first time publishes.
promises.push(
createOrUpgradeDpidAlias(legacyDpid, ceramicStream, uuid)
.then(dpid => dpidAlias = dpid)
);
};
promises.push(createOrUpgradeDpidAlias(legacyDpid, ceramicStream, uuid).then((dpid) => (dpidAlias = dpid)));
}

promises.push(
handlePublicDataRefs({
Expand All @@ -258,7 +254,7 @@ const syncPublish = async (
manifestCid: cid,
nodeVersionId: nodeVersion.id,
nodeUuid: node.uuid,
})
}),
);

await Promise.all(promises);
Expand All @@ -277,7 +273,7 @@ const syncPublish = async (
/**
* Creates new dPID if legacyDpid is falsy, otherwise tries to upgrade
* the dPID by binding the stream in the alias registry for that dPID.
*/
*/
const createOrUpgradeDpidAlias = async (
legacyDpid: number | undefined,
ceramicStream: string,
Expand All @@ -290,29 +286,21 @@ const createOrUpgradeDpidAlias = async (
} else {
// Will nicely return the existing dpid if this is called multiple times
dpidAlias = await getOrCreateDpid(ceramicStream);
};
}
await setDpidAlias(uuid, dpidAlias);
return dpidAlias;
};

type PublishData = {
nodeId: number,
nodeUuid: string,
userId: number,
manifestCid: string,
nodeVersionId: number,
nodeId: number;
nodeUuid: string;
userId: number;
manifestCid: string;
nodeVersionId: number;
};

const handlePublicDataRefs = async (
params: PublishData,
): Promise<void> => {
const {
nodeId,
nodeUuid,
userId,
manifestCid,
nodeVersionId,
} = params;
const handlePublicDataRefs = async (params: PublishData): Promise<void> => {
const { nodeId, nodeUuid, userId, manifestCid, nodeVersionId } = params;

const logger = parentLogger.child({
module: 'NODE::handlePublicDataRefs',
Expand All @@ -329,47 +317,31 @@ const handlePublicDataRefs = async (
/***
* Traverse the DAG structure to find all relevant CIDs and get relevant info for indexing
*/
cidsRequiredForPublish = await getAllCidsRequiredForPublish(
manifestCid,
nodeUuid,
userId,
nodeId,
nodeVersionId
);
cidsRequiredForPublish = await getAllCidsRequiredForPublish(manifestCid, nodeUuid, userId, nodeId, nodeVersionId);

/**
* Index the DAGs from IPFS in order to avoid recurrent IPFS calls when requesting data in the future
*/
const newPublicDataRefs = await createPublicDataRefs(
cidsRequiredForPublish,
userId,
nodeVersionId,
);
const newPublicDataRefs = await createPublicDataRefs(cidsRequiredForPublish, userId, nodeVersionId);

/**
* Save a success for configurable service quality tracking purposes
*/
await saveInteractionWithoutReq(
ActionType.PUBLISH_NODE_CID_SUCCESS,
{
params,
result: { newPublicDataRefs },
}
);
await saveInteractionWithoutReq(ActionType.PUBLISH_NODE_CID_SUCCESS, {
params,
result: { newPublicDataRefs },
});
} catch (error) {
logger.error({ error }, `[publish::publish] error=${error}`);
/**
* Save a failure for configurable service quality tracking purposes
*/
await saveInteractionWithoutReq(
ActionType.PUBLISH_NODE_CID_FAIL,
{
params,
error
}
);
await saveInteractionWithoutReq(ActionType.PUBLISH_NODE_CID_FAIL, {
params,
error,
});
throw error;
};
}
};

export const publishHandler = async ({
Expand Down Expand Up @@ -462,11 +434,11 @@ export const publishHandler = async ({
logger.trace(`[publish::publish] nodeUuid=${node.uuid}, manifestCid=${cid}, transaction=${transactionId}`);

await handlePublicDataRefs({
nodeId: node.id,
nodeUuid: node.uuid,
userId: owner.id,
manifestCid: cid,
nodeVersionId: nodeVersion.id,
nodeId: node.id,
nodeUuid: node.uuid,
userId: owner.id,
manifestCid: cid,
nodeVersionId: nodeVersion.id,
});

const manifest = await getManifestByCid(cid);
Expand Down
2 changes: 1 addition & 1 deletion desci-server/src/routes/v1/attestations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ router.get(
asyncHander(showCommunityClaims),
);

router.get('/:dpid', [validate(showNodeAttestationsSchema)], asyncHander(showNodeAttestations));
router.get('/:uuid', [validate(showNodeAttestationsSchema)], asyncHander(showNodeAttestations));
router.get('/:claimId/reactions', [validate(getAttestationReactionsSchema)], asyncHander(getAttestationReactions));
router.get(
'/:claimId/verifications',
Expand Down
8 changes: 4 additions & 4 deletions desci-server/src/routes/v1/attestations/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const showCommunityClaimsSchema = z.object({

export const showNodeAttestationsSchema = z.object({
params: z.object({
dpid,
uuid: z.string(),
}),
});

Expand Down Expand Up @@ -149,7 +149,7 @@ export const claimAttestationSchema = z.object({
attestationId: z.coerce.number(),
nodeVersion: z.coerce.number(),
nodeUuid: z.string(),
nodeDpid: z.string(),
nodeDpid: z.string().optional(),
claimerId: z.coerce.number(),
}),
});
Expand All @@ -159,14 +159,14 @@ export const claimEntryAttestationsSchema = z.object({
communityId: z.coerce.number(),
nodeVersion: z.coerce.number(),
nodeUuid: z.string(),
nodeDpid: z.string(),
nodeDpid: z.string().optional(),
claimerId: z.coerce.number(),
}),
});

export const removeClaimSchema = z.object({
body: z.object({
dpid,
dpid: z.coerce.number().optional(),
nodeUuid: z.string(),
claimId: z.coerce.number(),
}),
Expand Down
9 changes: 7 additions & 2 deletions desci-server/src/services/Attestation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
NoAccessError,
VerificationError,
VerificationNotFoundError,
ensureUuidEndsWithDot,
logger,
} from '../internal.js';
import { communityService } from '../internal.js';
Expand Down Expand Up @@ -229,9 +230,9 @@ export class AttestationService {
});
}

async getAllNodeAttestations(dpid: string) {
async getAllNodeAttestations(uuid: string) {
return prisma.nodeAttestation.findMany({
where: { nodeDpid10: dpid, revoked: false },
where: { nodeUuid: ensureUuidEndsWithDot(uuid), revoked: false },
include: {
community: { select: { name: true, description: true, keywords: true, image_url: true } },
attestation: { select: { protected: true, verified_image_url: true } },
Expand Down Expand Up @@ -445,6 +446,10 @@ export class AttestationService {
return prisma.nodeAttestation.findFirst({ where: { id, nodeDpid10 } });
}

async getClaimOnUuid(id: number, nodeUuid: string) {
return prisma.nodeAttestation.findFirst({ where: { id, nodeUuid } });
}

async verifyClaim(nodeAttestationId: number, userId: number) {
assert(nodeAttestationId > 0, 'Error: nodeAttestationId is Zero');
assert(userId > 0, 'Error: userId is Zero');
Expand Down
2 changes: 1 addition & 1 deletion desci-server/src/services/Communities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export class CommunityService {
left outer JOIN "Annotation" ON t1."id" = "Annotation"."nodeAttestationId"
left outer JOIN "NodeAttestationReaction" ON t1."id" = "NodeAttestationReaction"."nodeAttestationId"
left outer JOIN "NodeAttestationVerification" ON t1."id" = "NodeAttestationVerification"."nodeAttestationId"
WHERE t1."revoked" = false AND
WHERE t1."revoked" = false AND t1."nodeDpid10" IS NOT NULL AND
EXISTS
(SELECT *
from "CommunityEntryAttestation" c1
Expand Down
Loading