-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #243 from desci-labs/contributor-apis
Contributor APIs
- Loading branch information
Showing
36 changed files
with
3,311 additions
and
79 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
18.16.0 | ||
18.17.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
FROM node:16.20.0-bookworm | ||
FROM node:18.17.0-bookworm | ||
|
||
VOLUME /root/.yarn | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
25 changes: 25 additions & 0 deletions
25
desci-server/prisma/migrations/20240313161411_create_node_contribution_table/migration.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
-- CreateTable | ||
CREATE TABLE "NodeContribution" ( | ||
"id" SERIAL NOT NULL, | ||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
"updatedAt" TIMESTAMP(3) NOT NULL, | ||
"contributorId" TEXT NOT NULL, | ||
"nodeId" INTEGER NOT NULL, | ||
"userId" INTEGER, | ||
"verified" BOOLEAN NOT NULL DEFAULT false, | ||
"email" TEXT NOT NULL, | ||
|
||
CONSTRAINT "NodeContribution_pkey" PRIMARY KEY ("id") | ||
); | ||
|
||
-- CreateIndex | ||
CREATE UNIQUE INDEX "NodeContribution_contributorId_key" ON "NodeContribution"("contributorId"); | ||
|
||
-- CreateIndex | ||
CREATE UNIQUE INDEX "NodeContribution_contributorId_nodeId_userId_key" ON "NodeContribution"("contributorId", "nodeId", "userId"); | ||
|
||
-- AddForeignKey | ||
ALTER TABLE "NodeContribution" ADD CONSTRAINT "NodeContribution_nodeId_fkey" FOREIGN KEY ("nodeId") REFERENCES "Node"("id") ON DELETE RESTRICT ON UPDATE CASCADE; | ||
|
||
-- AddForeignKey | ||
ALTER TABLE "NodeContribution" ADD CONSTRAINT "NodeContribution_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; |
2 changes: 2 additions & 0 deletions
2
desci-server/prisma/migrations/20240315105529_add_orcid_to_contributions/migration.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
-- AlterTable | ||
ALTER TABLE "NodeContribution" ADD COLUMN "orcid" TEXT; |
2 changes: 2 additions & 0 deletions
2
...i-server/prisma/migrations/20240315124454_email_optional_node_contributions/migration.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
-- AlterTable | ||
ALTER TABLE "NodeContribution" ALTER COLUMN "email" DROP NOT NULL; |
3 changes: 3 additions & 0 deletions
3
desci-server/prisma/migrations/20240318164046_soft_delete_contributors/migration.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
-- AlterTable | ||
ALTER TABLE "NodeContribution" ADD COLUMN "deleted" BOOLEAN NOT NULL DEFAULT false, | ||
ADD COLUMN "deletedAt" TIMESTAMP(3); |
8 changes: 8 additions & 0 deletions
8
...server/prisma/migrations/20240318200900_enable_multiple_private_share_codes/migration.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
-- DropIndex | ||
DROP INDEX "PrivateShare_nodeUUID_key"; | ||
|
||
-- AlterTable | ||
ALTER TABLE "PrivateShare" ADD COLUMN "memo" TEXT; | ||
|
||
-- CreateIndex | ||
CREATE INDEX "PrivateShare_nodeUUID_idx" ON "PrivateShare"("nodeUUID"); |
2 changes: 2 additions & 0 deletions
2
...ver/prisma/migrations/20240320193439_add_denied_state_to_node_contributions/migration.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
-- AlterTable | ||
ALTER TABLE "NodeContribution" ADD COLUMN "denied" BOOLEAN NOT NULL DEFAULT false; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
99 changes: 99 additions & 0 deletions
99
desci-server/src/controllers/nodes/contributions/create.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import { Node, User } from '@prisma/client'; | ||
import sgMail from '@sendgrid/mail'; | ||
import { Request, Response } from 'express'; | ||
|
||
import { logger as parentLogger } from '../../../logger.js'; | ||
import { contributorService } from '../../../services/Contributors.js'; | ||
import { ContributorInviteEmailHtml } from '../../../templates/emails/utils/emailRenderer.js'; | ||
|
||
sgMail.setApiKey(process.env.SENDGRID_API_KEY); | ||
|
||
export type AddContributorReqBody = { | ||
contributorId: string; | ||
email?: string; | ||
orcid?: string; | ||
userId?: number; | ||
}; | ||
|
||
export type AddContributorRequest = Request<never, never, AddContributorReqBody> & { | ||
user: User; // added by auth middleware | ||
node: Node; // added by ensureWriteAccess middleware | ||
}; | ||
|
||
export type AddContributorResBody = | ||
| { | ||
ok: boolean; | ||
message: string; | ||
} | ||
| { | ||
error: string; | ||
}; | ||
|
||
export const addContributor = async (req: AddContributorRequest, res: Response<AddContributorResBody>) => { | ||
const node = req.node; | ||
const user = req.user; | ||
|
||
if (!node || !user) | ||
throw Error('Middleware not properly setup for addContributor controller, requires req.node and req.user'); | ||
|
||
const { contributorId, orcid, userId } = req.body; | ||
let { email } = req.body; | ||
if (email) email = email.toLowerCase(); | ||
const logger = parentLogger.child({ | ||
module: 'Contributors::createController', | ||
body: req.body, | ||
uuid: node.uuid, | ||
user: req.user, | ||
nodeId: node.id, | ||
}); | ||
|
||
if (!contributorId) { | ||
return res.status(400).json({ error: 'contributorId required' }); | ||
} | ||
if (!userId && !email && !orcid) { | ||
return res.status(400).json({ error: 'userId, Email or Orcid required' }); | ||
} | ||
// debugger; | ||
// Add contributor to the db | ||
try { | ||
const contributorAdded = await contributorService.addNodeContribution({ | ||
node, | ||
nodeOwner: user, | ||
contributorId, | ||
email, | ||
orcid, | ||
userId, | ||
}); | ||
if (!contributorAdded) throw Error('Failed to add contributor'); | ||
if (user.id !== contributorAdded.userId && contributorAdded.email) { | ||
// Generate a share code for the contributor if it's the node owner themselves | ||
const shareCode = await contributorService.generatePrivShareCodeForContribution(contributorAdded, node); | ||
|
||
// Future: make it count as a friend referral | ||
const emailHtml = ContributorInviteEmailHtml({ | ||
inviter: user.name, | ||
nodeUuid: node.uuid, | ||
privShareCode: shareCode, | ||
contributorId: contributorAdded.contributorId, | ||
newUser: contributorAdded.userId !== undefined, | ||
}); | ||
const emailMsg = { | ||
to: email, | ||
from: 'no-reply@desci.com', | ||
subject: `[nodes.desci.com] ${user.name} has added you as a contributor to their research node.`, | ||
text: `You've been added as a contributor to ${node.title}. Confirm your contribution to ensure you're credited for your work. | ||
Your private share code: ${shareCode}`, | ||
html: emailHtml, | ||
}; | ||
|
||
sgMail.send(emailMsg); | ||
} | ||
logger.info({ contributorAdded }, 'Contributor added successfully'); | ||
return res.status(200).json({ ok: true, message: 'Contributor added successfully' }); | ||
} catch (e) { | ||
logger.error({ e }, 'Failed to add contributor'); | ||
return res.status(500).json({ error: 'Failed to add contributor' }); | ||
} | ||
|
||
return res.status(500).json({ error: 'Something went wrong' }); | ||
}; |
59 changes: 59 additions & 0 deletions
59
desci-server/src/controllers/nodes/contributions/delete.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import { Node, User } from '@prisma/client'; | ||
import { Request, Response } from 'express'; | ||
|
||
import { logger as parentLogger } from '../../../logger.js'; | ||
import { contributorService } from '../../../services/Contributors.js'; | ||
|
||
export type DeleteContributorReqBody = { | ||
contributorId: string; | ||
}; | ||
|
||
export type DeleteContributorRequest = Request<never, never, DeleteContributorReqBody> & { | ||
user: User; // added by auth middleware | ||
node: Node; // added by ensureWriteAccess middleware | ||
}; | ||
|
||
export type DeleteContributorResBody = | ||
| { | ||
ok: boolean; | ||
message: string; | ||
} | ||
| { | ||
error: string; | ||
}; | ||
|
||
export const deleteContributor = async (req: DeleteContributorRequest, res: Response<DeleteContributorResBody>) => { | ||
const node = req.node; | ||
const user = req.user; | ||
|
||
if (!node || !user) | ||
throw Error('Middleware not properly setup for addContributor controller, requires req.node and req.user'); | ||
|
||
const { contributorId } = req.body; | ||
|
||
const logger = parentLogger.child({ | ||
module: 'Contributors::deleteContributorController', | ||
body: req.body, | ||
uuid: node.uuid, | ||
user: (req as any).user, | ||
nodeId: node.id, | ||
}); | ||
|
||
if (!contributorId) { | ||
return res.status(400).json({ error: 'contributorId required' }); | ||
} | ||
|
||
// Remove contributor entry from the db | ||
try { | ||
const contributorRemoved = await contributorService.removeContributor(contributorId, node.id); | ||
if (contributorRemoved) { | ||
logger.info('Contributor deleted successfully'); | ||
return res.status(200).json({ ok: true, message: 'Contributor deleted successfully' }); | ||
} | ||
} catch (e) { | ||
logger.error({ e }, 'Failed to delete contributor'); | ||
return res.status(500).json({ error: 'Failed to delete contributor' }); | ||
} | ||
|
||
return res.status(500).json({ error: 'Something went wrong' }); | ||
}; |
Oops, something went wrong.