Browse Source
Merge pull request #2 from RobertgPatch/copilot/sub-pr-1
Fix upload path, enum mismatches, missing validation, secrets in env, and i18n inconsistencies
pull/6603/head
RobertgPatch
2 weeks ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with
41 additions and
22 deletions
-
.env.dev
-
apps/api/src/app/family-office/family-office.controller.ts
-
apps/api/src/app/upload/upload.controller.ts
-
apps/api/src/app/upload/upload.module.ts
-
apps/api/src/app/upload/upload.service.ts
-
apps/client/src/app/pages/partnership-detail/record-valuation-dialog/record-valuation-dialog.component.ts
-
apps/client/src/app/pages/partnerships/partnerships-page.routes.ts
-
docker/entrypoint.sh
|
|
|
@ -3,18 +3,18 @@ COMPOSE_PROJECT_NAME=ghostfolio-development |
|
|
|
# CACHE |
|
|
|
REDIS_HOST=localhost |
|
|
|
REDIS_PORT=6379 |
|
|
|
REDIS_PASSWORD=b126659952eb48d08cc9fc9d966903ff |
|
|
|
REDIS_PASSWORD=<INSERT_REDIS_PASSWORD> |
|
|
|
|
|
|
|
# POSTGRES |
|
|
|
POSTGRES_DB=ghostfolio-db |
|
|
|
POSTGRES_USER=user |
|
|
|
POSTGRES_PASSWORD=302653e0a3b94dd2942bc283f269fc62 |
|
|
|
POSTGRES_PASSWORD=<INSERT_POSTGRES_PASSWORD> |
|
|
|
POSTGRES_PORT=5434 |
|
|
|
|
|
|
|
# VARIOUS |
|
|
|
ACCESS_TOKEN_SALT=34f2558b9a934517a978d69ab501fcfd |
|
|
|
DATABASE_URL=postgresql://user:302653e0a3b94dd2942bc283f269fc62@localhost:5434/ghostfolio-db?connect_timeout=300&sslmode=prefer |
|
|
|
JWT_SECRET_KEY=12005231ada14bd1919967473625bec4 |
|
|
|
ACCESS_TOKEN_SALT=<INSERT_RANDOM_STRING> |
|
|
|
DATABASE_URL=postgresql://user:<INSERT_POSTGRES_PASSWORD>@localhost:5434/ghostfolio-db?connect_timeout=300&sslmode=prefer |
|
|
|
JWT_SECRET_KEY=<INSERT_RANDOM_STRING> |
|
|
|
|
|
|
|
# DEVELOPMENT |
|
|
|
HOST=0.0.0.0 |
|
|
|
|
|
|
|
@ -3,7 +3,14 @@ import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard' |
|
|
|
import { permissions } from '@ghostfolio/common/permissions'; |
|
|
|
import type { RequestWithUser } from '@ghostfolio/common/types'; |
|
|
|
|
|
|
|
import { Controller, Get, Inject, Query, UseGuards } from '@nestjs/common'; |
|
|
|
import { |
|
|
|
BadRequestException, |
|
|
|
Controller, |
|
|
|
Get, |
|
|
|
Inject, |
|
|
|
Query, |
|
|
|
UseGuards |
|
|
|
} from '@nestjs/common'; |
|
|
|
import { REQUEST } from '@nestjs/core'; |
|
|
|
import { AuthGuard } from '@nestjs/passport'; |
|
|
|
|
|
|
|
@ -92,17 +99,17 @@ export class FamilyOfficeController { |
|
|
|
@Query('year') year?: string |
|
|
|
) { |
|
|
|
if (!period || !year) { |
|
|
|
return { |
|
|
|
error: 'period and year are required query parameters' |
|
|
|
}; |
|
|
|
throw new BadRequestException( |
|
|
|
'period and year are required query parameters' |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
const validPeriods = ['MONTHLY', 'QUARTERLY', 'YEARLY']; |
|
|
|
|
|
|
|
if (!validPeriods.includes(period)) { |
|
|
|
return { |
|
|
|
error: `period must be one of: ${validPeriods.join(', ')}` |
|
|
|
}; |
|
|
|
throw new BadRequestException( |
|
|
|
`period must be one of: ${validPeriods.join(', ')}` |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
const yearNum = parseInt(year, 10); |
|
|
|
|
|
|
|
@ -4,6 +4,7 @@ import { permissions } from '@ghostfolio/common/permissions'; |
|
|
|
import type { RequestWithUser } from '@ghostfolio/common/types'; |
|
|
|
|
|
|
|
import { |
|
|
|
BadRequestException, |
|
|
|
Controller, |
|
|
|
Get, |
|
|
|
Inject, |
|
|
|
@ -36,6 +37,14 @@ export class UploadController { |
|
|
|
public async uploadDocument(@UploadedFile() file: any) { |
|
|
|
const body = this.request.body as any; |
|
|
|
|
|
|
|
const validDocumentTypes = Object.values(DocumentType); |
|
|
|
|
|
|
|
if (!body.type || !validDocumentTypes.includes(body.type as DocumentType)) { |
|
|
|
throw new BadRequestException( |
|
|
|
`type must be one of: ${validDocumentTypes.join(', ')}` |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
return this.uploadService.createDocument({ |
|
|
|
file, |
|
|
|
entityId: body.entityId, |
|
|
|
|
|
|
|
@ -4,7 +4,7 @@ import { Module } from '@nestjs/common'; |
|
|
|
import { MulterModule } from '@nestjs/platform-express'; |
|
|
|
import { diskStorage } from 'multer'; |
|
|
|
import { existsSync, mkdirSync } from 'node:fs'; |
|
|
|
import { join } from 'node:path'; |
|
|
|
import { extname, join } from 'node:path'; |
|
|
|
import { v4 as uuidv4 } from 'uuid'; |
|
|
|
|
|
|
|
import { UploadController } from './upload.controller'; |
|
|
|
@ -38,8 +38,8 @@ if (!existsSync(uploadDir)) { |
|
|
|
cb(null, subDir); |
|
|
|
}, |
|
|
|
filename: (_req, file, cb) => { |
|
|
|
const ext = file.originalname.split('.').pop(); |
|
|
|
cb(null, `${uuidv4()}.${ext}`); |
|
|
|
const ext = extname(file.originalname); |
|
|
|
cb(null, ext ? `${uuidv4()}${ext}` : uuidv4()); |
|
|
|
} |
|
|
|
}) |
|
|
|
}), |
|
|
|
|
|
|
|
@ -51,7 +51,7 @@ export class UploadService { |
|
|
|
await mkdir(subDir, { recursive: true }); |
|
|
|
} |
|
|
|
|
|
|
|
const relativePath = `/${yearDir}/${monthDir}/${file.filename}`; |
|
|
|
const relativePath = `${yearDir}/${monthDir}/${file.filename}`; |
|
|
|
|
|
|
|
return this.prismaService.document.create({ |
|
|
|
data: { |
|
|
|
|
|
|
|
@ -62,10 +62,11 @@ import { MatSelectModule } from '@angular/material/select'; |
|
|
|
<mat-form-field appearance="outline" class="w-100"> |
|
|
|
<mat-label i18n>Source</mat-label> |
|
|
|
<mat-select formControlName="source" required> |
|
|
|
<mat-option value="STATEMENT">Statement</mat-option> |
|
|
|
<mat-option value="NAV_STATEMENT">Statement</mat-option> |
|
|
|
<mat-option value="MANUAL">Manual Entry</mat-option> |
|
|
|
<mat-option value="AUDITOR">Auditor</mat-option> |
|
|
|
<mat-option value="APPRAISAL">Appraisal</mat-option> |
|
|
|
<mat-option value="FUND_ADMIN">Fund Admin</mat-option> |
|
|
|
<mat-option value="MARKET">Market</mat-option> |
|
|
|
</mat-select> |
|
|
|
</mat-form-field> |
|
|
|
</div> |
|
|
|
@ -108,7 +109,7 @@ export class GfRecordValuationDialogComponent implements OnInit { |
|
|
|
Validators.required |
|
|
|
]), |
|
|
|
nav: new FormControl(null, [Validators.required, Validators.min(0)]), |
|
|
|
source: new FormControl('STATEMENT', [Validators.required]), |
|
|
|
source: new FormControl('NAV_STATEMENT', [Validators.required]), |
|
|
|
notes: new FormControl('') |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
@ -9,6 +9,6 @@ export const routes: Routes = [ |
|
|
|
canActivate: [AuthGuard], |
|
|
|
component: GfPartnershipsPageComponent, |
|
|
|
path: '', |
|
|
|
title: 'Partnerships' |
|
|
|
title: $localize`Partnerships` |
|
|
|
} |
|
|
|
]; |
|
|
|
|
|
|
|
@ -5,8 +5,10 @@ set -e |
|
|
|
echo "==> Running database migrations" |
|
|
|
npx prisma migrate deploy |
|
|
|
|
|
|
|
echo "==> Ensuring schema is fully synced" |
|
|
|
npx prisma db push --skip-generate --accept-data-loss 2>/dev/null || echo " (db push skipped — schema already in sync)" |
|
|
|
if [ "${DB_PUSH_ON_STARTUP}" = "true" ]; then |
|
|
|
echo "==> Syncing schema (DB_PUSH_ON_STARTUP=true)" |
|
|
|
npx prisma db push --skip-generate --accept-data-loss || echo " (db push failed -- check schema and database state)" |
|
|
|
fi |
|
|
|
|
|
|
|
echo "==> Seeding the database (if applicable)" |
|
|
|
npx prisma db seed || echo " (seed skipped or already applied)" |
|
|
|
|