Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.git
.github
node_modules
dist
coverage
.env
.env.*
npm-debug.log*
pnpm-debug.log*
13 changes: 9 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
FROM node:22.13.1-alpine

RUN apk add --no-cache bash git
RUN apk update

ARG RESET_DB_ARG=false
ENV RESET_DB=$RESET_DB_ARG
Expand All @@ -12,9 +11,15 @@ ENV SEED_DATA=$SEED_DATA_ARG
ENV PRISMA_CLI_BINARY_TARGETS=linux-musl-openssl-3.0.x

WORKDIR /app
COPY . .
COPY --chown=node:node . .

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ security]
Using COPY --chown=node:node . . is a good practice for setting file ownership, but ensure that all necessary files and directories are accessible to the node user. Verify that there are no permission issues during runtime.

RUN npm install pnpm -g
RUN pnpm install
RUN pnpm install --frozen-lockfile

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ maintainability]
Using --frozen-lockfile with pnpm install ensures that the exact versions in the lockfile are used, which is good for consistency across environments. However, ensure that the lockfile is up-to-date and committed to the repository to avoid potential dependency resolution issues.

RUN pnpm run build
RUN chmod +x appStartUp.sh
CMD ./appStartUp.sh

USER node

HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[❗❗ correctness]
The HEALTHCHECK command uses wget to check the health endpoint. Ensure that wget is installed in the image, or consider using curl which might be more commonly available. Also, verify that the health endpoint is correctly implemented and accessible.

CMD wget -q -O /dev/null "http://127.0.0.1:${PORT:-3000}/v6/projects/health" || exit 1

CMD ["./appStartUp.sh"]
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,11 @@ For the full v5 -> v6 mapping table, see `docs/api-usage-analysis.md`.
| `DELETE` | `/v6/projects/:projectId` | Admin only | Soft-delete project |
| `GET` | `/v6/projects/:projectId/billingAccount` | JWT / M2M | Default billing account (Salesforce) |
| `GET` | `/v6/projects/:projectId/billingAccounts` | JWT / M2M | All billing accounts for project |
| `GET` | `/v6/projects/:projectId/permissions` | JWT / M2M | JWT: caller work-management policy map. M2M: per-member permission matrix with project permissions and template policies |
| `GET` | `/v6/projects/:projectId/permissions` | JWT / M2M | Regular human JWT: caller work-management policy map. M2M, admins, project managers, and project copilots on the project: per-member permission matrix with project permissions and template policies |

Talent Manager note:
- `Talent Manager` and `Topcoder Talent Manager` callers create projects as primary `manager` members.
- Updating `billingAccountId` is restricted to human administrators and project members whose role on that project is `manager` (`Full Access`).

### Members

Expand All @@ -114,7 +118,7 @@ For the full v5 -> v6 mapping table, see `docs/api-usage-analysis.md`.
| --- | --- | --- | --- |
| `GET` | `/v6/projects/:projectId/invites` | JWT / M2M | List invites |
| `GET` | `/v6/projects/:projectId/invites/:inviteId` | JWT / M2M | Get invite |
| `POST` | `/v6/projects/:projectId/invites` | JWT / M2M | Create invite(s) - partial-success response `{ success[], failed[] }` |
| `POST` | `/v6/projects/:projectId/invites` | JWT / M2M | Create invite(s) - returns `201` when any invite is created and includes `{ success[], failed[] }` for rejected targets |
| `PATCH` | `/v6/projects/:projectId/invites/:inviteId` | JWT / M2M | Accept / decline invite |
| `DELETE` | `/v6/projects/:projectId/invites/:inviteId` | JWT / M2M | Delete invite |

Expand Down Expand Up @@ -463,7 +467,7 @@ Open `TODO (quality)` findings from prior phases:
- Elasticsearch removed; all reads use PostgreSQL via Prisma.
- Timeline/milestone CRUD intentionally not migrated (see `docs/timeline-milestone-migration.md`).
- Deprecated endpoints not ported (scope change requests, reports, customer payments, phase members/approvals, estimation items).
- Invite creation uses partial-success semantics: `{ success: Invite[], failed: ErrorInfo[] }`.
- Invite creation returns `201` when any invite is created and still uses `{ success: Invite[], failed: ErrorInfo[] }` for rejected targets.
- Event originator changed from `tc-project-service` to `project-service-v6`.

Full details: `docs/DIFFERENCES_FROM_V5.md` and `docs/MIGRATION_FROM_TC_PROJECT_SERVICE.md`.
Expand Down
2 changes: 1 addition & 1 deletion appStartUp.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ set -eo pipefail
export DATABASE_URL=$(echo -e ${DATABASE_URL})

# Start the app
pnpm start:prod
exec node dist/src/main

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[❗❗ correctness]
Replacing pnpm start:prod with exec node dist/src/main changes how the application is started. Ensure that any scripts or environment setups previously handled by pnpm are now correctly managed, as this could affect application initialization and environment configuration.

2 changes: 1 addition & 1 deletion docs/DIFFERENCES_FROM_V5.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ This document summarizes intentional differences and improvements in `project-se
- Work management permission routes use query-parameter lookup patterns for consistency:
- `/v6/projects/metadata/workManagementPermission?projectTemplateId=:id`
- `/v6/projects/metadata/workManagementPermission?id=:id`
- Invite creation uses partial-success response semantics:
- Invite creation returns `201` when at least one invite is created and keeps partial-success response semantics:
- `{ success: Invite[], failed: ErrorInfo[] }`

## Authorization Improvements
Expand Down
3 changes: 2 additions & 1 deletion docs/MIGRATION_FROM_TC_PROJECT_SERVICE.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ This document maps legacy authorization logic from Express middleware to NestJS
## Consumer Migration Notes

- Continue using `fields` query parameter for optional member/invite enrichment.
- Handle `403` responses from invite create as partial-failure responses with body:
- Invite create now returns `201` when at least one invite is created and still includes:
- `{ success: Invite[], failed: ErrorInfo[] }`
- Compatibility `403` responses remain possible when no invite is created and the response only contains failures.
- If your integration relied on ES stale fields, switch to Member/Identity APIs for user metadata.
11 changes: 11 additions & 0 deletions docs/PERMISSIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ Swagger auth notes:
- `src/shared/enums/projectMemberRole.enum.ts`
- `src/shared/enums/scopes.enum.ts`

## Talent Manager Behavior

- `Talent Manager` and `Topcoder Talent Manager` satisfy `CREATE_PROJECT_AS_MANAGER`, so project creation persists them as the primary `manager` project member.
- That primary `manager` membership then unlocks the standard manager-level project-owner paths, such as edit and delete checks that rely on project-member context.

## Billing Account Editing

- `MANAGE_PROJECT_BILLING_ACCOUNT_ID` is intentionally narrower than general project edit access.
- A caller may set or update `billingAccountId` only when they are a human admin (`Connect Admin`, `administrator`, or `tgadmin`) or they are an active `manager` member on that specific project.
- Global manager, project-manager, task-manager, talent-manager, or M2M-only access is not enough on its own to edit a project's billing account.

## Permission Rule Shape

Supported shapes:
Expand Down
2 changes: 1 addition & 1 deletion docs/api-usage-analysis.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
| GET | `/v5/projects/:projectId/attachments` | **unused** | none | none | Attachment array (read-access filtered) |
| GET | `/v5/projects/:projectId/phases/:phaseId/products` | **unused** | none | none | Phase product array |
| GET | `/v5/projects/:projectId/phases/:phaseId/products/:productId` | **unused** | none | none | Single phase product |
| GET | `/v5/projects/:projectId/permissions` | **unused** | none | none | JWT: policy map `{ [policyName]: true }` for allowed work-management actions. M2M in `/v6`: per-member permission matrix with memberships, project permissions, and template policies |
| GET | `/v5/projects/:projectId/permissions` | **unused** | none | none | JWT: policy map `{ [policyName]: true }` for allowed work-management actions. In `/v6`, M2M/admin/project-manager/project-copilot callers receive a per-member permission matrix with memberships, project permissions, and template policies |
| DELETE | `/v5/projects/:projectId` | **unused** | none | none | `204` |
| GET | `/v5/projects/:projectId/phases/:phaseId` | **unused** | none | none | Phase object (includes members/approvals where present) |
| POST | `/v5/projects/:projectId/phases` | **unused** | none | `{name,status,description?,requirements?,startDate?,endDate?,duration?,budget?,spentBudget?,progress?,details?,order?,productTemplateId?,members?}` | Created phase |
Expand Down
13 changes: 8 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/src/main",
"lint": "eslint \"{src,apps,libs,test,prisma}/**/*.ts\" --fix",
"lint": "eslint \"{src,apps,libs,test,prisma}/**/*.ts\" --fix --no-error-on-unmatched-pattern",
"test": "jest --config ./jest.config.js",
"test:watch": "jest --config ./jest.config.js --watch",
"test:cov": "jest --config ./jest.config.js --coverage",
Expand Down Expand Up @@ -104,14 +104,17 @@
"packageManager": "[email protected]+sha512.41872f037ad22f7348e3b1debbaf7e867cfd448f2726d9cf74c08f19507c31d2c8e7a11525b983febc2df640b5438dee6023ebb1f84ed43cc2d654d2bc326264",
"pnpm": {
"overrides": {
"@hono/node-server": "1.19.10",
"ajv": "8.18.0",
"axios": "1.13.5",
"fast-xml-parser": "5.3.6",
"hono": "4.11.10",
"fast-xml-parser": "5.3.8",
"hono": "4.12.4",
"jws": ">=3.2.3 <4.0.0 || >=4.0.1",
"lodash": "4.17.23",
"minimatch": "10.2.1",
"qs": "6.14.2"
"minimatch": "10.2.3",
"multer": "2.1.1",
"qs": "6.14.2",
"serialize-javascript": "7.0.3"
},
"patchedDependencies": {
"@eslint/[email protected]": "patches/@[email protected]",
Expand Down
Loading
Loading