fix tests isues
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
# npm audit report
|
||||
|
||||
postcss <8.5.10
|
||||
Severity: moderate
|
||||
PostCSS has XSS via Unescaped </style> in its CSS Stringify Output - https://github.com/advisories/GHSA-qx2v-qp2m-jg93
|
||||
fix available via `npm audit fix --force`
|
||||
Will install next@9.3.3, which is a breaking change
|
||||
node_modules/next/node_modules/postcss
|
||||
next 9.3.4-canary.0 - 16.3.0-canary.5
|
||||
Depends on vulnerable versions of postcss
|
||||
node_modules/next
|
||||
|
||||
uuid <11.1.1
|
||||
Severity: moderate
|
||||
uuid: Missing buffer bounds check in v3/v5/v6 when buf is provided - https://github.com/advisories/GHSA-w5hq-g745-h8pq
|
||||
fix available via `npm audit fix --force`
|
||||
Will install node-cron@4.2.1, which is a breaking change
|
||||
node_modules/gaxios/node_modules/uuid
|
||||
node_modules/google-gax/node_modules/uuid
|
||||
node_modules/node-cron/node_modules/uuid
|
||||
node_modules/teeny-request/node_modules/uuid
|
||||
node_modules/uuid
|
||||
firebase-admin 7.0.0 - 8.2.0 || >=10.2.0
|
||||
Depends on vulnerable versions of @google-cloud/firestore
|
||||
Depends on vulnerable versions of @google-cloud/storage
|
||||
Depends on vulnerable versions of uuid
|
||||
node_modules/firebase-admin
|
||||
gaxios 6.4.0 - 6.7.1
|
||||
Depends on vulnerable versions of uuid
|
||||
node_modules/gaxios
|
||||
google-gax 4.0.5-experimental - 4.6.1
|
||||
Depends on vulnerable versions of retry-request
|
||||
Depends on vulnerable versions of uuid
|
||||
node_modules/google-gax
|
||||
@google-cloud/firestore 7.5.0-pre.0 || 7.6.0 - 7.11.6
|
||||
Depends on vulnerable versions of google-gax
|
||||
node_modules/@google-cloud/firestore
|
||||
node-cron 3.0.2 - 3.0.3
|
||||
Depends on vulnerable versions of uuid
|
||||
node_modules/node-cron
|
||||
teeny-request 3.9.1 - 9.0.0
|
||||
Depends on vulnerable versions of uuid
|
||||
node_modules/teeny-request
|
||||
@google-cloud/storage 2.2.0 - 2.5.0 || >=5.19.0
|
||||
Depends on vulnerable versions of retry-request
|
||||
Depends on vulnerable versions of teeny-request
|
||||
node_modules/@google-cloud/storage
|
||||
retry-request 7.0.0 - 7.0.2
|
||||
Depends on vulnerable versions of teeny-request
|
||||
node_modules/retry-request
|
||||
|
||||
11 moderate severity vulnerabilities
|
||||
|
||||
To address issues that do not require attention, run:
|
||||
npm audit fix
|
||||
|
||||
To address all issues (including breaking changes), run:
|
||||
npm audit fix --force
|
||||
@@ -0,0 +1,47 @@
|
||||
docker compose -f docker-compose.dev.yml up -d postgres
|
||||
docker compose -f docker-compose.dev.yml up -d redis
|
||||
docker compose -p rentaldrivego-prod --env-file .env.docker.production -f docker-compose.production.yml up --build -d --no-deps marketplace
|
||||
docker compose -p rentaldrivego-prod --env-file .env.docker.production -f docker-compose.production.yml up --build -d --no-deps admin
|
||||
docker compose -f docker-compose.dev.yml --profile full up --build
|
||||
docker compose -f docker-compose.dev.yml --profile dashboard up --build
|
||||
docker compose -f docker-compose.dev.yml --profile marketplace up --build
|
||||
docker compose -f docker-compose.dev.yml --profile api up --build
|
||||
|
||||
|
||||
Default dev admin:
|
||||
|
||||
Email: platform.ops@rentaldrivego.com
|
||||
Password: PlatformOps123!
|
||||
|
||||
|
||||
|
||||
What this means:
|
||||
|
||||
docker compose -f docker-compose.dev.yml --profile full up --build will no longer reseed or redeploy the DB on every fix.
|
||||
Your data stays as long as you do not run docker compose ... down -v or manually delete the Docker volumes.
|
||||
If you later change the schema and want to apply it without wiping data, run:
|
||||
docker compose -f docker-compose.dev.yml run --rm migrate sh -c "npm run db:deploy"
|
||||
Production was already using a persistent named Postgres volume, so the main issue was the dev bootstrap behavior. Restart the dev stack once for this change to take effect.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Done. The DOCKER.md production section now covers the full deployment flow from a fresh server. The short version:
|
||||
|
||||
|
||||
# Once per server
|
||||
docker network create traefik-proxy
|
||||
|
||||
# Clone & configure
|
||||
git clone <repo> rentaldrivego && cd rentaldrivego
|
||||
cp .env.docker.production.example .env.docker.production
|
||||
# → fill in POSTGRES_PASSWORD, JWT_SECRET, ACME_EMAIL, RESEND_API_KEY
|
||||
|
||||
# Deploy
|
||||
docker compose -f traefik.yaml up -d
|
||||
docker compose -p rentaldrivego-prod --env-file .env.docker.production -f docker-compose.production.yml up --build -d
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
Deploy dev
|
||||
|
||||
Option A — Docker (recommended, full stack)
|
||||
|
||||
# First time or after dependency changes:
|
||||
docker compose -f docker-compose.dev.yml build
|
||||
|
||||
# Start everything (api + all frontend apps + postgres + redis):
|
||||
docker compose -f docker-compose.dev.yml --profile full up
|
||||
|
||||
# Or start only what you need (mix and match profiles):
|
||||
docker compose -f docker-compose.dev.yml --profile api up # api only
|
||||
docker compose -f docker-compose.dev.yml --profile api --profile dashboard up
|
||||
|
||||
First run seeds the database automatically; subsequent runs skip seeding.
|
||||
|
||||
Option B — Local (no Docker for apps)
|
||||
|
||||
# Start infra only:
|
||||
docker compose -f docker-compose.dev.yml up postgres redis
|
||||
|
||||
# Then in a separate terminal:
|
||||
npm run dev # starts all apps via turbo
|
||||
|
||||
Port map (after port reassignment)
|
||||
|
||||
┌─────────────┬───────────────────────────────────────────────┐
|
||||
│ App │ URL │
|
||||
├─────────────┼───────────────────────────────────────────────┤
|
||||
│ public-site │ http://localhost:3000 │
|
||||
├─────────────┼───────────────────────────────────────────────┤
|
||||
│ marketplace │ http://localhost:3001/marketplace │
|
||||
├─────────────┼───────────────────────────────────────────────┤
|
||||
│ dashboard │ http://localhost:3002/dashboard │
|
||||
├─────────────┼───────────────────────────────────────────────┤
|
||||
│ admin │ http://localhost:3003/admin │
|
||||
├─────────────┼───────────────────────────────────────────────┤
|
||||
│ api │ http://localhost:4000 │
|
||||
├─────────────┼───────────────────────────────────────────────┤
|
||||
│ pgAdmin │ http://localhost:5050 (needs --profile tools) │
|
||||
└─────────────┴───────────────────────────────────────────────┘
|
||||
|
||||
▎ Note: In Docker dev each app is directly accessible on its own port. The /marketplace, /dashboard, /admin proxy
|
||||
▎ through port 3000 (public-site) only works when running locally (not in separate containers), since localhost inside
|
||||
▎ a container refers to that container only.
|
||||
@@ -0,0 +1,12 @@
|
||||
apps/api/src/index.ts -> apps/api/src/index.ts differ
|
||||
apps/api/src/middleware/requireApiKey.test.ts -> apps/api/src/middleware/requireApiKey.test.ts differ
|
||||
apps/api/src/middleware/requireApiKey.ts -> apps/api/src/middleware/requireApiKey.ts differ
|
||||
apps/api/src/middleware/requireCompanyAuth.test.ts -> apps/api/src/middleware/requireCompanyAuth.test.ts differ
|
||||
apps/api/src/middleware/requireRenterAuth.test.ts -> apps/api/src/middleware/requireRenterAuth.test.ts differ
|
||||
apps/api/src/modules/auth/auth.employee.service.ts -> apps/api/src/modules/auth/auth.employee.service.ts differ
|
||||
apps/api/src/security/tokens.ts -> apps/api/src/security/tokens.ts differ
|
||||
apps/api/src/tests/helpers/fixtures.ts -> apps/api/src/tests/helpers/fixtures.ts differ
|
||||
packages/database/prisma/migrations: 20260609233000_drop_legacy_company_api_key
|
||||
packages/database/prisma/schema.prisma -> packages/database/prisma/schema.prisma differ
|
||||
packages/database/src/index.d.ts -> packages/database/src/index.d.ts differ
|
||||
packages/database/src/index.ts -> packages/database/src/index.ts differ
|
||||
@@ -0,0 +1,342 @@
|
||||
diff -ru '--exclude=node_modules' car_project_original/apps/api/src/index.ts car_project_work/apps/api/src/index.ts
|
||||
--- car_project_original/apps/api/src/index.ts 2026-06-09 19:40:36.000000000 +0000
|
||||
+++ car_project_work/apps/api/src/index.ts 2026-06-09 23:18:30.521017321 +0000
|
||||
@@ -1,11 +1,11 @@
|
||||
import http from 'http'
|
||||
import { Server as SocketIOServer } from 'socket.io'
|
||||
import cron from 'node-cron'
|
||||
-import jwt from 'jsonwebtoken'
|
||||
import { redis } from './lib/redis'
|
||||
import { prisma } from './lib/prisma'
|
||||
import { assertStorageConfiguration } from './lib/storage'
|
||||
import { createApp, corsOrigins } from './app'
|
||||
+import { verifyAnyActorToken } from './security/tokens'
|
||||
import { sendNotification } from './services/notificationService'
|
||||
import {
|
||||
runTrialExpirationJob,
|
||||
@@ -29,7 +29,7 @@
|
||||
const token = socket.handshake.auth?.token as string | undefined
|
||||
if (!token) return next() // unauthenticated connections allowed; they just don't join rooms
|
||||
try {
|
||||
- const payload = jwt.verify(token, process.env.JWT_SECRET!) as { sub: string; type: string }
|
||||
+ const payload = verifyAnyActorToken(token)
|
||||
;(socket as any).authenticatedUserId = payload.sub
|
||||
next()
|
||||
} catch {
|
||||
diff -ru '--exclude=node_modules' car_project_original/apps/api/src/middleware/requireApiKey.test.ts car_project_work/apps/api/src/middleware/requireApiKey.test.ts
|
||||
--- car_project_original/apps/api/src/middleware/requireApiKey.test.ts 2026-06-09 19:40:36.000000000 +0000
|
||||
+++ car_project_work/apps/api/src/middleware/requireApiKey.test.ts 2026-06-09 23:18:42.795559504 +0000
|
||||
@@ -1,10 +1,12 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { NextFunction, Request, Response } from 'express'
|
||||
+import { generateCompanyApiKey } from '../security/apiKeys'
|
||||
|
||||
vi.mock('../lib/prisma', () => ({
|
||||
prisma: {
|
||||
- company: {
|
||||
+ companyApiKey: {
|
||||
findUnique: vi.fn(),
|
||||
+ update: vi.fn(),
|
||||
},
|
||||
},
|
||||
}))
|
||||
@@ -42,19 +44,18 @@
|
||||
message: 'API key required in x-api-key header',
|
||||
statusCode: 401,
|
||||
})
|
||||
- expect(prisma.company.findUnique).not.toHaveBeenCalled()
|
||||
+ expect((prisma as any).companyApiKey.findUnique).not.toHaveBeenCalled()
|
||||
expect(next).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
- it('rejects unknown API keys', async () => {
|
||||
- vi.mocked(prisma.company.findUnique).mockResolvedValue(null)
|
||||
+ it('rejects malformed API keys before database lookup', async () => {
|
||||
const req = { headers: { 'x-api-key': 'bad-key' } } as unknown as Request
|
||||
const res = createResponseStub()
|
||||
const next = vi.fn() as NextFunction
|
||||
|
||||
await requireApiKey(req, res, next)
|
||||
|
||||
- expect(prisma.company.findUnique).toHaveBeenCalledWith({ where: { apiKey: 'bad-key' } })
|
||||
+ expect((prisma as any).companyApiKey.findUnique).not.toHaveBeenCalled()
|
||||
expect(res.status).toHaveBeenCalledWith(401)
|
||||
expect(res.json).toHaveBeenCalledWith({
|
||||
error: 'invalid_api_key',
|
||||
@@ -64,15 +65,91 @@
|
||||
expect(next).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
- it('attaches company context and calls next for valid API keys', async () => {
|
||||
- const company = { id: 'company_1', apiKey: 'valid-key', name: 'Atlas Cars' }
|
||||
- vi.mocked(prisma.company.findUnique).mockResolvedValue(company as any)
|
||||
- const req = { headers: { 'x-api-key': 'valid-key' } } as unknown as Request
|
||||
+ it('rejects unknown API key prefixes', async () => {
|
||||
+ const generated = generateCompanyApiKey()
|
||||
+ vi.mocked((prisma as any).companyApiKey.findUnique).mockResolvedValue(null)
|
||||
+ const req = { headers: { 'x-api-key': generated.rawKey } } as unknown as Request
|
||||
const res = createResponseStub()
|
||||
const next = vi.fn() as NextFunction
|
||||
|
||||
await requireApiKey(req, res, next)
|
||||
|
||||
+ expect((prisma as any).companyApiKey.findUnique).toHaveBeenCalledWith({
|
||||
+ where: { prefix: generated.prefix },
|
||||
+ include: { company: true },
|
||||
+ })
|
||||
+ expect(res.status).toHaveBeenCalledWith(401)
|
||||
+ expect(res.json).toHaveBeenCalledWith({
|
||||
+ error: 'invalid_api_key',
|
||||
+ message: 'Invalid API key',
|
||||
+ statusCode: 401,
|
||||
+ })
|
||||
+ expect(next).not.toHaveBeenCalled()
|
||||
+ })
|
||||
+
|
||||
+ it('rejects revoked API keys', async () => {
|
||||
+ const generated = generateCompanyApiKey()
|
||||
+ vi.mocked((prisma as any).companyApiKey.findUnique).mockResolvedValue({
|
||||
+ id: 'key_1',
|
||||
+ companyId: 'company_1',
|
||||
+ prefix: generated.prefix,
|
||||
+ keyHash: generated.keyHash,
|
||||
+ revokedAt: new Date('2026-06-01T00:00:00.000Z'),
|
||||
+ company: { id: 'company_1', name: 'Atlas Cars' },
|
||||
+ })
|
||||
+ const req = { headers: { 'x-api-key': generated.rawKey } } as unknown as Request
|
||||
+ const res = createResponseStub()
|
||||
+ const next = vi.fn() as NextFunction
|
||||
+
|
||||
+ await requireApiKey(req, res, next)
|
||||
+
|
||||
+ expect(res.status).toHaveBeenCalledWith(401)
|
||||
+ expect(next).not.toHaveBeenCalled()
|
||||
+ })
|
||||
+
|
||||
+ it('rejects API keys whose secret does not match the stored hash', async () => {
|
||||
+ const generated = generateCompanyApiKey()
|
||||
+ const other = generateCompanyApiKey()
|
||||
+ vi.mocked((prisma as any).companyApiKey.findUnique).mockResolvedValue({
|
||||
+ id: 'key_1',
|
||||
+ companyId: 'company_1',
|
||||
+ prefix: generated.prefix,
|
||||
+ keyHash: other.keyHash,
|
||||
+ revokedAt: null,
|
||||
+ company: { id: 'company_1', name: 'Atlas Cars' },
|
||||
+ })
|
||||
+ const req = { headers: { 'x-api-key': generated.rawKey } } as unknown as Request
|
||||
+ const res = createResponseStub()
|
||||
+ const next = vi.fn() as NextFunction
|
||||
+
|
||||
+ await requireApiKey(req, res, next)
|
||||
+
|
||||
+ expect(res.status).toHaveBeenCalledWith(401)
|
||||
+ expect(next).not.toHaveBeenCalled()
|
||||
+ })
|
||||
+
|
||||
+ it('attaches company context, updates lastUsedAt, and calls next for valid API keys', async () => {
|
||||
+ const generated = generateCompanyApiKey()
|
||||
+ const company = { id: 'company_1', name: 'Atlas Cars' }
|
||||
+ vi.mocked((prisma as any).companyApiKey.findUnique).mockResolvedValue({
|
||||
+ id: 'key_1',
|
||||
+ companyId: company.id,
|
||||
+ prefix: generated.prefix,
|
||||
+ keyHash: generated.keyHash,
|
||||
+ revokedAt: null,
|
||||
+ company,
|
||||
+ })
|
||||
+ vi.mocked((prisma as any).companyApiKey.update).mockResolvedValue({})
|
||||
+ const req = { headers: { 'x-api-key': generated.rawKey } } as unknown as Request
|
||||
+ const res = createResponseStub()
|
||||
+ const next = vi.fn() as NextFunction
|
||||
+
|
||||
+ await requireApiKey(req, res, next)
|
||||
+
|
||||
+ expect((prisma as any).companyApiKey.update).toHaveBeenCalledWith({
|
||||
+ where: { id: 'key_1' },
|
||||
+ data: { lastUsedAt: expect.any(Date) },
|
||||
+ })
|
||||
expect(req.company).toEqual(company)
|
||||
expect(req.companyId).toBe('company_1')
|
||||
expect(next).toHaveBeenCalledTimes(1)
|
||||
diff -ru '--exclude=node_modules' car_project_original/apps/api/src/middleware/requireApiKey.ts car_project_work/apps/api/src/middleware/requireApiKey.ts
|
||||
--- car_project_original/apps/api/src/middleware/requireApiKey.ts 2026-06-09 19:44:15.000000000 +0000
|
||||
+++ car_project_work/apps/api/src/middleware/requireApiKey.ts 2026-06-09 23:18:30.518795557 +0000
|
||||
@@ -21,14 +21,6 @@
|
||||
|
||||
const hashed = hashApiKey(apiKey)
|
||||
if (!keyRecord || keyRecord.revokedAt || !timingSafeEqualHex(hashed, keyRecord.keyHash)) {
|
||||
- if (process.env.ALLOW_LEGACY_COMPANY_API_KEYS === 'true') {
|
||||
- const company = await prisma.company.findUnique({ where: { apiKey } })
|
||||
- if (company) {
|
||||
- req.company = company
|
||||
- req.companyId = company.id
|
||||
- return next()
|
||||
- }
|
||||
- }
|
||||
return res.status(401).json({ error: 'invalid_api_key', message: 'Invalid API key', statusCode: 401 })
|
||||
}
|
||||
|
||||
diff -ru '--exclude=node_modules' car_project_original/apps/api/src/middleware/requireCompanyAuth.test.ts car_project_work/apps/api/src/middleware/requireCompanyAuth.test.ts
|
||||
--- car_project_original/apps/api/src/middleware/requireCompanyAuth.test.ts 2026-06-09 20:03:48.000000000 +0000
|
||||
+++ car_project_work/apps/api/src/middleware/requireCompanyAuth.test.ts 2026-06-09 23:18:50.351586123 +0000
|
||||
@@ -63,7 +63,7 @@
|
||||
await requireCompanyAuth(req, res, next)
|
||||
|
||||
expect(res.status).toHaveBeenCalledWith(401)
|
||||
- expect(res.json).toHaveBeenCalledWith({ error: 'invalid_token', message: 'Invalid token type for this endpoint', statusCode: 401 })
|
||||
+ expect(res.json).toHaveBeenCalledWith({ error: 'invalid_token', message: 'Invalid or expired session token', statusCode: 401 })
|
||||
expect(prisma.employee.findUnique).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
@@ -108,7 +108,11 @@
|
||||
|
||||
await requireCompanyDocumentAuth(req, res, next)
|
||||
|
||||
- expect(jwt.verify).toHaveBeenCalledWith('cookie-token', 'test-secret')
|
||||
+ expect(jwt.verify).toHaveBeenCalledWith('cookie-token', 'test-secret', {
|
||||
+ algorithms: ['HS256'],
|
||||
+ issuer: 'rentaldrivego-api',
|
||||
+ audience: 'employee',
|
||||
+ })
|
||||
expect(req.companyId).toBe('company_2')
|
||||
expect(next).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
diff -ru '--exclude=node_modules' car_project_original/apps/api/src/middleware/requireRenterAuth.test.ts car_project_work/apps/api/src/middleware/requireRenterAuth.test.ts
|
||||
--- car_project_original/apps/api/src/middleware/requireRenterAuth.test.ts 2026-06-09 19:40:36.000000000 +0000
|
||||
+++ car_project_work/apps/api/src/middleware/requireRenterAuth.test.ts 2026-06-09 23:18:50.352660214 +0000
|
||||
@@ -49,7 +49,7 @@
|
||||
await requireRenterAuth(req, res, next)
|
||||
|
||||
expect(res.status).toHaveBeenCalledWith(401)
|
||||
- expect(res.json).toHaveBeenCalledWith({ error: 'invalid_token', message: 'Invalid token type', statusCode: 401 })
|
||||
+ expect(res.json).toHaveBeenCalledWith({ error: 'invalid_token', message: 'Invalid or expired token', statusCode: 401 })
|
||||
expect(prisma.renter.findUnique).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
diff -ru '--exclude=node_modules' car_project_original/apps/api/src/modules/auth/auth.employee.service.ts car_project_work/apps/api/src/modules/auth/auth.employee.service.ts
|
||||
--- car_project_original/apps/api/src/modules/auth/auth.employee.service.ts 2026-06-09 19:42:41.000000000 +0000
|
||||
+++ car_project_work/apps/api/src/modules/auth/auth.employee.service.ts 2026-06-09 23:19:29.323222951 +0000
|
||||
@@ -41,13 +41,22 @@
|
||||
pwdv: getEmployeePasswordResetVersion(passwordHash),
|
||||
},
|
||||
process.env.JWT_SECRET!,
|
||||
- { expiresIn: `${RESET_TOKEN_TTL_MINUTES}m` },
|
||||
+ {
|
||||
+ algorithm: 'HS256',
|
||||
+ issuer: 'rentaldrivego-api',
|
||||
+ audience: 'employee_password_reset',
|
||||
+ expiresIn: `${RESET_TOKEN_TTL_MINUTES}m`,
|
||||
+ },
|
||||
)
|
||||
}
|
||||
|
||||
function verifyEmployeePasswordResetToken(token: string): EmployeePasswordResetPayload | null {
|
||||
try {
|
||||
- const payload = jwt.verify(token, process.env.JWT_SECRET!) as jwt.JwtPayload
|
||||
+ const payload = jwt.verify(token, process.env.JWT_SECRET!, {
|
||||
+ algorithms: ['HS256'],
|
||||
+ issuer: 'rentaldrivego-api',
|
||||
+ audience: 'employee_password_reset',
|
||||
+ }) as jwt.JwtPayload
|
||||
|
||||
if (
|
||||
typeof payload.sub !== 'string' ||
|
||||
diff -ru '--exclude=node_modules' car_project_original/apps/api/src/security/tokens.ts car_project_work/apps/api/src/security/tokens.ts
|
||||
--- car_project_original/apps/api/src/security/tokens.ts 2026-06-09 20:20:10.000000000 +0000
|
||||
+++ car_project_work/apps/api/src/security/tokens.ts 2026-06-09 23:18:30.519590128 +0000
|
||||
@@ -54,3 +54,14 @@
|
||||
|
||||
return payload as ActorTokenPayload
|
||||
}
|
||||
+
|
||||
+export function verifyAnyActorToken(token: string): ActorTokenPayload {
|
||||
+ const decoded = jwt.decode(token) as jwt.JwtPayload | null
|
||||
+ const actorType = decoded?.type
|
||||
+
|
||||
+ if (actorType !== 'admin' && actorType !== 'employee' && actorType !== 'renter') {
|
||||
+ throw new Error('Invalid actor token')
|
||||
+ }
|
||||
+
|
||||
+ return verifyActorToken(token, actorType)
|
||||
+}
|
||||
diff -ru '--exclude=node_modules' car_project_original/apps/api/src/tests/helpers/fixtures.ts car_project_work/apps/api/src/tests/helpers/fixtures.ts
|
||||
--- car_project_original/apps/api/src/tests/helpers/fixtures.ts 2026-06-09 19:40:36.000000000 +0000
|
||||
+++ car_project_work/apps/api/src/tests/helpers/fixtures.ts 2026-06-09 23:19:11.289635569 +0000
|
||||
@@ -1,8 +1,6 @@
|
||||
import bcrypt from 'bcryptjs'
|
||||
-import jwt from 'jsonwebtoken'
|
||||
import { prisma } from '../../lib/prisma'
|
||||
-
|
||||
-const JWT_SECRET = process.env.JWT_SECRET ?? 'test-secret'
|
||||
+import { signActorToken } from '../../security/tokens'
|
||||
|
||||
let counter = 0
|
||||
function uid() {
|
||||
@@ -215,28 +213,16 @@
|
||||
})
|
||||
}
|
||||
|
||||
-export function signEmployeeToken(employeeId: string, companyId: string, role: string = 'OWNER') {
|
||||
- return jwt.sign(
|
||||
- { sub: employeeId, companyId, role, type: 'employee' },
|
||||
- JWT_SECRET,
|
||||
- { expiresIn: '1h' },
|
||||
- )
|
||||
+export function signEmployeeToken(employeeId: string, _companyId: string, _role: string = 'OWNER') {
|
||||
+ return signActorToken(employeeId, 'employee', { expiresIn: '1h' })
|
||||
}
|
||||
|
||||
export function signAdminToken(adminId: string) {
|
||||
- return jwt.sign(
|
||||
- { sub: adminId, type: 'admin' },
|
||||
- JWT_SECRET,
|
||||
- { expiresIn: '1h' },
|
||||
- )
|
||||
+ return signActorToken(adminId, 'admin', { expiresIn: '1h', last2faAt: Date.now() })
|
||||
}
|
||||
|
||||
export function signRenterToken(renterId: string) {
|
||||
- return jwt.sign(
|
||||
- { sub: renterId, type: 'renter' },
|
||||
- JWT_SECRET,
|
||||
- { expiresIn: '1h' },
|
||||
- )
|
||||
+ return signActorToken(renterId, 'renter', { expiresIn: '1h' })
|
||||
}
|
||||
|
||||
export function authHeader(token: string) {
|
||||
Only in car_project_work/packages/database/prisma/migrations: 20260609233000_drop_legacy_company_api_key
|
||||
diff -ru '--exclude=node_modules' car_project_original/packages/database/prisma/schema.prisma car_project_work/packages/database/prisma/schema.prisma
|
||||
--- car_project_original/packages/database/prisma/schema.prisma 2026-06-09 20:18:52.000000000 +0000
|
||||
+++ car_project_work/packages/database/prisma/schema.prisma 2026-06-09 23:18:30.516362264 +0000
|
||||
@@ -502,7 +502,6 @@
|
||||
address Json?
|
||||
status CompanyStatus @default(PENDING)
|
||||
subscriptionPaymentRef String?
|
||||
- apiKey String @unique @default(cuid())
|
||||
|
||||
subscription Subscription?
|
||||
billingAccounts BillingAccount[]
|
||||
diff -ru '--exclude=node_modules' car_project_original/packages/database/src/index.d.ts car_project_work/packages/database/src/index.d.ts
|
||||
--- car_project_original/packages/database/src/index.d.ts 2026-06-09 19:40:36.000000000 +0000
|
||||
+++ car_project_work/packages/database/src/index.d.ts 2026-06-09 23:18:30.517981475 +0000
|
||||
@@ -33,7 +33,6 @@
|
||||
email: string
|
||||
phone: string | null
|
||||
status: string
|
||||
- apiKey: string
|
||||
}
|
||||
|
||||
export interface Employee {
|
||||
diff -ru '--exclude=node_modules' car_project_original/packages/database/src/index.ts car_project_work/packages/database/src/index.ts
|
||||
--- car_project_original/packages/database/src/index.ts 2026-06-09 19:40:36.000000000 +0000
|
||||
+++ car_project_work/packages/database/src/index.ts 2026-06-09 23:18:30.517228009 +0000
|
||||
@@ -33,7 +33,6 @@
|
||||
email: string
|
||||
phone: string | null
|
||||
status: string
|
||||
- apiKey: string
|
||||
}
|
||||
|
||||
export interface Employee {
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,32 @@
|
||||
A SECURITY_HARDENING_LEFTOVER_APPLIED_REPORT.md
|
||||
M apps/admin/src/app/dashboard/admin-users/page.tsx
|
||||
M apps/admin/src/app/dashboard/companies/[id]/page.tsx
|
||||
M apps/admin/src/app/dashboard/containers/page.tsx
|
||||
M apps/admin/src/app/dashboard/pricing/page.tsx
|
||||
M apps/admin/src/app/dashboard/renters/page.tsx
|
||||
M apps/admin/src/app/forgot-password/page.tsx
|
||||
M apps/admin/src/app/reset-password/page.tsx
|
||||
A apps/admin/src/middleware.ts
|
||||
M apps/api/src/app.ts
|
||||
M apps/api/src/index.ts
|
||||
M apps/api/src/middleware/rateLimiter.ts
|
||||
M apps/api/src/modules/admin/admin.repo.ts
|
||||
M apps/api/src/modules/admin/admin.routes.ts
|
||||
M apps/api/src/modules/admin/admin.schemas.ts
|
||||
M apps/api/src/modules/admin/admin.service.ts
|
||||
M apps/api/src/swagger/openapi.ts
|
||||
M apps/dashboard/README.md
|
||||
M apps/dashboard/src/app/(dashboard)/team/page.tsx
|
||||
M apps/dashboard/src/app/sign-in/[[...sign-in]]/SignInPageClient.tsx
|
||||
M apps/dashboard/src/components/layout/Sidebar.tsx
|
||||
M apps/dashboard/src/components/layout/TopBar.tsx
|
||||
M apps/dashboard/src/lib/api.ts
|
||||
M apps/dashboard/src/middleware.test.ts
|
||||
M apps/dashboard/src/middleware.ts
|
||||
M apps/marketplace/src/middleware.test.ts
|
||||
M apps/marketplace/src/middleware.ts
|
||||
M docs/project-design/COOKIE_POLICY.md
|
||||
M memory/project_auth_architecture.md
|
||||
A packages/database/prisma/migrations/20260610001500_add_admin_recovery_codes/migration.sql
|
||||
M packages/database/prisma/schema.prisma
|
||||
M scripts/security-static-check.mjs
|
||||
Reference in New Issue
Block a user