Skip to main content
Glama
project.ts4.99 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import { allOk, badRequest, forbidden, getReferenceString } from '@medplum/core'; import type { ProjectMembership } from '@medplum/fhirtypes'; import type { Request, Response } from 'express'; import { Router } from 'express'; import { body, validationResult } from 'express-validator'; import { setPassword } from '../auth/setpassword'; import { getAuthenticatedContext } from '../context'; import { invalidRequest, sendOutcome } from '../fhir/outcomes'; import { authenticateRequest } from '../oauth/middleware'; import { getUserByEmailInProject } from '../oauth/utils'; import { createBotHandler, createBotValidator } from './bot'; import { createClientHandler, createClientValidator } from './client'; import { inviteHandler, inviteValidator } from './invite'; import { verifyProjectAdmin } from './utils'; export const projectAdminRouter = Router(); projectAdminRouter.use(authenticateRequest); projectAdminRouter.use(verifyProjectAdmin); // POST to /admin/projects/setpassword // to force set a User password associated to the project by the project admin. projectAdminRouter.post( '/setpassword', [ body('email').isEmail().withMessage('Valid email address is required'), body('password').isLength({ min: 8 }).withMessage('Invalid password, must be at least 8 characters'), ], async (req: Request, res: Response) => { const errors = validationResult(req); if (!errors.isEmpty()) { sendOutcome(res, invalidRequest(errors)); return; } const ctx = getAuthenticatedContext(); const projectId = ctx.project.id; if (!projectId) { sendOutcome(res, badRequest('Project not found')); return; } const user = await getUserByEmailInProject(req.body.email, projectId); if (!user) { sendOutcome(res, badRequest('User not found')); return; } await setPassword(user, req.body.password as string); sendOutcome(res, allOk); } ); projectAdminRouter.post('/:projectId/bot', createBotValidator, createBotHandler); projectAdminRouter.post('/:projectId/client', createClientValidator, createClientHandler); projectAdminRouter.post('/:projectId/invite', inviteValidator, inviteHandler); /** * Handles requests to "/admin/projects/{projectId}" * Returns project metadata and a list of members. */ projectAdminRouter.get('/:projectId', async (req: Request, res: Response) => { const project = getAuthenticatedContext().project; return res.status(200).json({ project: { id: project.id, name: project.name, secret: project.secret, site: project.site, }, }); }); projectAdminRouter.post('/:projectId/secrets', async (req: Request, res: Response) => { const ctx = getAuthenticatedContext(); const result = await ctx.repo.updateResource({ ...ctx.project, secret: req.body, }); res.json(result); }); projectAdminRouter.post('/:projectId/sites', async (req: Request, res: Response) => { const ctx = getAuthenticatedContext(); const result = await ctx.repo.updateResource({ ...ctx.project, site: req.body, }); res.json(result); }); projectAdminRouter.get('/:projectId/members/:membershipId', async (req: Request, res: Response) => { const ctx = getAuthenticatedContext(); const { membershipId } = req.params; const membership = await ctx.repo.readResource<ProjectMembership>('ProjectMembership', membershipId); if (membership.project?.reference !== getReferenceString(ctx.project)) { sendOutcome(res, forbidden); return; } res.json(membership); }); projectAdminRouter.post('/:projectId/members/:membershipId', async (req: Request, res: Response) => { const ctx = getAuthenticatedContext(); const { membershipId } = req.params; const membership = await ctx.repo.readResource<ProjectMembership>('ProjectMembership', membershipId); if (membership.project?.reference !== getReferenceString(ctx.project)) { sendOutcome(res, forbidden); return; } const resource = req.body; if (resource?.resourceType !== 'ProjectMembership' || resource.id !== membershipId) { sendOutcome(res, forbidden); return; } const result = await ctx.repo.updateResource(resource); res.json(result); }); projectAdminRouter.delete('/:projectId/members/:membershipId', async (req: Request, res: Response) => { const ctx = getAuthenticatedContext(); const { membershipId } = req.params; const membership = await ctx.repo.readResource<ProjectMembership>('ProjectMembership', membershipId); if (membership.project?.reference !== getReferenceString(ctx.project)) { sendOutcome(res, forbidden); return; } if (ctx.project.owner?.reference === membership.user?.reference) { sendOutcome(res, badRequest('Cannot delete the owner of the project')); return; } await ctx.repo.deleteResource('ProjectMembership', req.params.membershipId); sendOutcome(res, allOk); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/medplum/medplum'

If you have feedback or need assistance with the MCP directory API, please join our Discord server