From 0015f645cf78376044c90cf58fc78198a1d81f62 Mon Sep 17 00:00:00 2001 From: Stephanos A Date: Wed, 1 Jul 2026 13:38:08 +0300 Subject: [PATCH 1/2] Coach edit related fixes --- apps/edr-passenger-api/src/modules/fleet/fleet.dto.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/edr-passenger-api/src/modules/fleet/fleet.dto.ts b/apps/edr-passenger-api/src/modules/fleet/fleet.dto.ts index 9e1f20c01..2f2a76f48 100644 --- a/apps/edr-passenger-api/src/modules/fleet/fleet.dto.ts +++ b/apps/edr-passenger-api/src/modules/fleet/fleet.dto.ts @@ -1,6 +1,6 @@ import { IsString, IsInt, IsOptional, IsArray, IsBoolean } from 'class-validator'; import { Transform } from 'class-transformer'; -import { ApiProperty, ApiPropertyOptional, PartialType, OmitType } from '@nestjs/swagger'; +import { ApiProperty, ApiPropertyOptional, PartialType } from '@nestjs/swagger'; export class CreateTrainDto { @ApiProperty({ example: '301', description: 'Unique train service number' }) @IsString() number: string; @@ -32,7 +32,7 @@ export class CreateCoachDto { @IsOptional() @IsInt() sequence?: number; } -export class UpdateCoachDto extends PartialType(OmitType(CreateCoachDto, ['number'] as const)) { +export class UpdateCoachDto extends PartialType(CreateCoachDto) { @ApiPropertyOptional({ example: 1, description: 'Sequence number for ordering' }) @IsOptional() @IsInt() sequence?: number; } From 33d90df5a9b374ba77239b2a7753605fc4630325 Mon Sep 17 00:00:00 2001 From: Abubeker Yasin Date: Wed, 1 Jul 2026 15:29:49 +0300 Subject: [PATCH 2/2] feat: ( dmoney ) create redirect page to dmoney --- apps/edr-passenger-web/portal/.env.example | 4 + .../portal/src/app/go/page.tsx | 100 ++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 apps/edr-passenger-web/portal/src/app/go/page.tsx diff --git a/apps/edr-passenger-web/portal/.env.example b/apps/edr-passenger-web/portal/.env.example index 25ffe6909..6560533f2 100644 --- a/apps/edr-passenger-web/portal/.env.example +++ b/apps/edr-passenger-web/portal/.env.example @@ -1,5 +1,9 @@ # API Configuration NEXT_PUBLIC_API_URL=https://your-api-domain.com +# Allowlisted destination hosts for the /go D-Money redirect bounce page (comma-separated). +# The /go?url=... page only forwards to https hosts that match one of these (host or subdomain). +NEXT_PUBLIC_DMONEY_ALLOWED_HOSTS=d-money.dj + # GitHub Packages Token GITHUB_PACKAGE_TOKEN=$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 \ No newline at end of file diff --git a/apps/edr-passenger-web/portal/src/app/go/page.tsx b/apps/edr-passenger-web/portal/src/app/go/page.tsx new file mode 100644 index 000000000..255b3980a --- /dev/null +++ b/apps/edr-passenger-web/portal/src/app/go/page.tsx @@ -0,0 +1,100 @@ +"use client"; + +import { Suspense, useEffect, useMemo } from "react"; +import { useSearchParams } from "next/navigation"; +import { Loader2, ShieldAlert, ExternalLink } from "lucide-react"; + + + +const ALLOWED_HOSTS = ( + process.env.NEXT_PUBLIC_DMONEY_ALLOWED_HOSTS ?? "d-money.dj" +) + .split(",") + .map((h) => h.trim().toLowerCase()) + .filter(Boolean); + +/** True only for https URLs whose host is (or is a subdomain of) an allowlisted host. */ +function isTrustedDMoneyUrl(raw: string | null): raw is string { + if (!raw) return false; + let parsed: URL; + try { + parsed = new URL(raw); + } catch { + return false; + } + if (parsed.protocol !== "https:") return false; + const host = parsed.hostname.toLowerCase(); + return ALLOWED_HOSTS.some( + (allowed) => host === allowed || host.endsWith(`.${allowed}`), + ); +} + +const REDIRECT_DELAY_MS = 1000; + +function RedirectView() { + const searchParams = useSearchParams(); + const raw = searchParams.get("url"); + const target = useMemo(() => (isTrustedDMoneyUrl(raw) ? raw : null), [raw]); + + useEffect(() => { + if (!target) return; + const timer = setTimeout(() => { + + window.location.replace(target); + }, REDIRECT_DELAY_MS); + return () => clearTimeout(timer); + }, [target]); + + if (!target) { + return ( +
+
+ +
+

+ Can't continue +

+

+ This link is missing a valid D-Money checkout address or points to an + untrusted destination. Please start the payment again from the app. +

+
+ ); + } + + return ( +
+
+ +
+

+ Redirecting to D-Money +

+

+ Taking you to the secure D-Money checkout to complete your payment… +

+ + + Continue to D-Money + + +
+ ); +} + +export default function GoPage() { + return ( +
+ + } + > + + +
+ ); +}