mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 12:30:58 +00:00
Merge branch 'develop' of github.com:Tria-plc/edr-platform into feat/customer_registration
This commit is contained in:
@@ -9,8 +9,26 @@ DB_NAME=edr_freight
|
||||
DB_USER=postgres
|
||||
DB_PASSWORD=
|
||||
|
||||
# JWT (provided by external auth package — placeholder only)
|
||||
# JWT (used by @tria-plc/api-common SharedAuthModule)
|
||||
JWT_SECRET=
|
||||
JWT_ACCESS_TOKEN_SECRET=
|
||||
JWT_REFRESH_TOKEN_SECRET=
|
||||
JWT_EXPIRES_IN=3600
|
||||
# JWT expiry for @tria-plc/api-common token utils (jsonwebtoken timespan format)
|
||||
JWT_ACCESS_TOKEN_EXPIRES=1h
|
||||
JWT_REFRESH_TOKEN_EXPIRES=7d
|
||||
|
||||
# IAM seed defaults (used by @tria-plc/iamapi-common on first boot)
|
||||
SUPER_ADMIN_EMAIL=superadmin@tria.com
|
||||
SUPER_ADMIN_PHONE=
|
||||
DEFAULT_PASSWORD=password@tria
|
||||
|
||||
# MinIO (used by @tria-plc/iamapi-common for file storage)
|
||||
MINIO_ENDPOINT=localhost
|
||||
MINIO_PORT=9000
|
||||
MINIO_USE_SSL=false
|
||||
MINIO_ACCESS_KEY=
|
||||
MINIO_SECRET_KEY=
|
||||
|
||||
# Redis
|
||||
REDIS_HOST=localhost
|
||||
|
||||
@@ -13,8 +13,6 @@
|
||||
"type-check": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tria-plc/api-common": "^0.1.0",
|
||||
"@tria-plc/iamapi-common": "^0.1.0",
|
||||
"@edr/api-common": "workspace:*",
|
||||
"@edr/types": "workspace:*",
|
||||
"@nestjs/common": "^11.0.0",
|
||||
@@ -24,8 +22,13 @@
|
||||
"@nestjs/platform-express": "^11.0.0",
|
||||
"@nestjs/swagger": "^11.4.2",
|
||||
"@nestjs/typeorm": "^11.0.1",
|
||||
"@tria-plc/api-common": "^0.1.4",
|
||||
"@tria-plc/iamapi-common": "^0.1.6",
|
||||
"amqp-connection-manager": "^5.0.0",
|
||||
"amqplib": "^2.0.1",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.1",
|
||||
"dotenv": "^17.4.2",
|
||||
"pg": "^8.13.0",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rxjs": "^7.8.1",
|
||||
@@ -33,12 +36,13 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@edr/api-common": "workspace:*",
|
||||
"@edr/types": "workspace:*",
|
||||
"@edr/eslint-config": "workspace:*",
|
||||
"@edr/tsconfig": "workspace:*",
|
||||
"@edr/types": "workspace:*",
|
||||
"@nestjs/cli": "^11.0.0",
|
||||
"@nestjs/schematics": "^11.0.0",
|
||||
"@nestjs/testing": "^11.0.0",
|
||||
"@types/amqplib": "^0.10.8",
|
||||
"@types/express": "^5.0.0",
|
||||
"@types/jest": "^29.5.13",
|
||||
"@types/node": "^20.14.0",
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Module } from "@nestjs/common";
|
||||
import { Module, OnApplicationBootstrap } from "@nestjs/common";
|
||||
import { ConfigModule, ConfigService } from "@nestjs/config";
|
||||
import { TypeOrmModule, TypeOrmModuleOptions } from "@nestjs/typeorm";
|
||||
import { IamModule, DataSeeder } from "@tria-plc/iamapi-common";
|
||||
import { SharedAuthModule } from "@tria-plc/api-common/modules/auth/shared-auth.module";
|
||||
|
||||
import appConfig from "./config/app.config";
|
||||
import databaseConfig from "./config/database.config";
|
||||
@@ -24,6 +26,8 @@ import { NotificationsModule } from "./modules/notifications/notifications.modul
|
||||
useFactory: (config: ConfigService): TypeOrmModuleOptions =>
|
||||
config.get<TypeOrmModuleOptions>("database")!,
|
||||
}),
|
||||
SharedAuthModule,
|
||||
IamModule.forRoot(),
|
||||
BookingsModule,
|
||||
ConsignmentsModule,
|
||||
TrainsModule,
|
||||
@@ -33,4 +37,10 @@ import { NotificationsModule } from "./modules/notifications/notifications.modul
|
||||
NotificationsModule,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
export class AppModule implements OnApplicationBootstrap {
|
||||
constructor(private readonly seeder: DataSeeder) {}
|
||||
|
||||
async onApplicationBootstrap() {
|
||||
await this.seeder.run();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,94 @@
|
||||
import { registerAs } from "@nestjs/config";
|
||||
import { TypeOrmModuleOptions } from "@nestjs/typeorm";
|
||||
import {
|
||||
DefaultPosition,
|
||||
DefaultUnit,
|
||||
EmployeePosition,
|
||||
Employee,
|
||||
OrganizationConfiguration,
|
||||
GlobalOrganizationConfiguration,
|
||||
OrganizationType,
|
||||
Organization,
|
||||
PositionConfiguration,
|
||||
PositionPermission,
|
||||
PositionTypeConfiguration,
|
||||
PositionTypePermission,
|
||||
PositionType,
|
||||
Position,
|
||||
Project,
|
||||
UnitConfiguration,
|
||||
GlobalUnitConfiguration,
|
||||
Unit,
|
||||
EmployeeSignature,
|
||||
EmployeeStamp,
|
||||
RecordFooter,
|
||||
RecordHeader,
|
||||
Seal,
|
||||
AccountConfiguration,
|
||||
Application,
|
||||
DocumentaryRequirement,
|
||||
Permission,
|
||||
RolePermission,
|
||||
Role,
|
||||
Session,
|
||||
UserCredential,
|
||||
UserDocument,
|
||||
UserRole,
|
||||
UserVerification,
|
||||
User,
|
||||
Notification,
|
||||
NotificationEvent,
|
||||
NotificationPlaceholder,
|
||||
NotificationReceiverField,
|
||||
NotificationReceiver,
|
||||
NotificationTemplate,
|
||||
} from "@tria-plc/iamapi-common";
|
||||
import { OrganizationSetting } from "@tria-plc/iamapi-common/entities/iam/organization-structure/organization-setting.entity";
|
||||
|
||||
const iamEntities = [
|
||||
DefaultPosition,
|
||||
DefaultUnit,
|
||||
EmployeePosition,
|
||||
Employee,
|
||||
OrganizationConfiguration,
|
||||
GlobalOrganizationConfiguration,
|
||||
OrganizationSetting,
|
||||
OrganizationType,
|
||||
Organization,
|
||||
PositionConfiguration,
|
||||
PositionPermission,
|
||||
PositionTypeConfiguration,
|
||||
PositionTypePermission,
|
||||
PositionType,
|
||||
Position,
|
||||
Project,
|
||||
UnitConfiguration,
|
||||
GlobalUnitConfiguration,
|
||||
Unit,
|
||||
EmployeeSignature,
|
||||
EmployeeStamp,
|
||||
RecordFooter,
|
||||
RecordHeader,
|
||||
Seal,
|
||||
AccountConfiguration,
|
||||
Application,
|
||||
DocumentaryRequirement,
|
||||
Permission,
|
||||
RolePermission,
|
||||
Role,
|
||||
Session,
|
||||
UserCredential,
|
||||
UserDocument,
|
||||
UserRole,
|
||||
UserVerification,
|
||||
User,
|
||||
Notification,
|
||||
NotificationEvent,
|
||||
NotificationPlaceholder,
|
||||
NotificationReceiverField,
|
||||
NotificationReceiver,
|
||||
NotificationTemplate,
|
||||
];
|
||||
|
||||
export default registerAs(
|
||||
"database",
|
||||
@@ -10,8 +99,15 @@ export default registerAs(
|
||||
username: process.env.DB_USER ?? "postgres",
|
||||
password: process.env.DB_PASSWORD ?? "",
|
||||
database: process.env.DB_NAME ?? "edr_freight",
|
||||
entities: [__dirname + "/../**/*.entity.{ts,js}"],
|
||||
migrations: [__dirname + "/../../migrations/*.{ts,js}"],
|
||||
entities: [__dirname + "/../**/*.entity.{ts,js}", ...iamEntities],
|
||||
autoLoadEntities: true,
|
||||
migrations: [
|
||||
// IAM schema + tables must be created before freight entity sync
|
||||
__dirname +
|
||||
"/../../node_modules/@tria-plc/iamapi-common/dist/db/migrations/*.js",
|
||||
__dirname + "/../../migrations/*.{ts,js}",
|
||||
],
|
||||
migrationsRun: true,
|
||||
// Never enable synchronize in production. Use migrations.
|
||||
synchronize: process.env.NODE_ENV === "development",
|
||||
logging: process.env.NODE_ENV === "development",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import "reflect-metadata";
|
||||
import * as dotenv from "dotenv";
|
||||
dotenv.config();
|
||||
import { NestFactory } from "@nestjs/core";
|
||||
import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger";
|
||||
import {
|
||||
|
||||
@@ -4,7 +4,6 @@ import { ApiOperation, ApiTags } from "@nestjs/swagger";
|
||||
import { BillingService } from "./billing.service";
|
||||
|
||||
@ApiTags("billing")
|
||||
// @UseGuards(JwtAuthGuard) — TODO: integrate @edr/auth
|
||||
@Controller("billing")
|
||||
export class BillingController {
|
||||
constructor(private readonly billingService: BillingService) {}
|
||||
|
||||
@@ -16,7 +16,6 @@ import { CreateBookingDto } from "./dto/create-booking.dto";
|
||||
import { FilterBookingDto } from "./dto/filter-booking.dto";
|
||||
|
||||
@ApiTags("bookings")
|
||||
// @UseGuards(JwtAuthGuard) — TODO: integrate @edr/auth
|
||||
@Controller("bookings")
|
||||
export class BookingsController {
|
||||
constructor(private readonly bookingsService: BookingsService) {}
|
||||
|
||||
@@ -14,7 +14,6 @@ import { CreateConsignmentDto } from "./dto/create-consignment.dto";
|
||||
import { FilterConsignmentDto } from "./dto/filter-consignment.dto";
|
||||
|
||||
@ApiTags("consignments")
|
||||
// @UseGuards(JwtAuthGuard) — TODO: integrate @edr/auth
|
||||
@Controller("consignments")
|
||||
export class ConsignmentsController {
|
||||
constructor(private readonly consignmentsService: ConsignmentsService) {}
|
||||
|
||||
@@ -12,7 +12,6 @@ import { CustomersService } from "./customers.service";
|
||||
import { CreateCustomerDto } from "./dto/create-customer.dto";
|
||||
|
||||
@ApiTags("customers")
|
||||
// @UseGuards(JwtAuthGuard) — TODO: integrate @edr/auth
|
||||
@Controller("customers")
|
||||
export class CustomersController {
|
||||
constructor(private readonly customersService: CustomersService) {}
|
||||
|
||||
@@ -4,7 +4,6 @@ import { ApiOperation, ApiTags } from "@nestjs/swagger";
|
||||
import { TrackingService } from "./tracking.service";
|
||||
|
||||
@ApiTags("tracking")
|
||||
// @UseGuards(JwtAuthGuard) — TODO: integrate @edr/auth
|
||||
@Controller("tracking")
|
||||
export class TrackingController {
|
||||
constructor(private readonly trackingService: TrackingService) {}
|
||||
|
||||
@@ -12,7 +12,6 @@ import { CreateTrainDto } from "./dto/create-train.dto";
|
||||
import { TrainsService } from "./trains.service";
|
||||
|
||||
@ApiTags("trains")
|
||||
// @UseGuards(JwtAuthGuard) — TODO: integrate @edr/auth
|
||||
@Controller("trains")
|
||||
export class TrainsController {
|
||||
constructor(private readonly trainsService: TrainsService) {}
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
"rootDir": "./src",
|
||||
"noEmit": false,
|
||||
"incremental": true,
|
||||
"tsBuildInfoFile": "./.tsbuildinfo"
|
||||
"tsBuildInfoFile": "./.tsbuildinfo",
|
||||
"module": "node16",
|
||||
"moduleResolution": "node16"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ import TrackingPage from "./pages/tracking/TrackingPage";
|
||||
import BillingPage from "./pages/billing/BillingPage";
|
||||
import TrainsPage from "./pages/trains/TrainsPage";
|
||||
import DashboardPage from "./pages/dashboard/DashboardPage";
|
||||
import { IamLoginPage } from "@tria-plc/iamui-common";
|
||||
import { IamLoginPage, LoadingScreen, useAuth } from "@tria-plc/iamui-common";
|
||||
import CustomersPage from "./pages/customers/CustomersPage";
|
||||
import CustomerDetailPage from "./pages/customers/CustomerDetailPage";
|
||||
import NewCustomerPage from "./pages/customers/NewCustomerPage";
|
||||
@@ -52,8 +52,13 @@ const sidebarItems: SidebarItem[] = [
|
||||
const App = () => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const { user, loading } = useAuth();
|
||||
|
||||
if (location.pathname === "/auth") {
|
||||
if (loading) {
|
||||
return <LoadingScreen />;
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/auth" element={<IamLoginPage />} />
|
||||
@@ -62,6 +67,10 @@ const App = () => {
|
||||
);
|
||||
}
|
||||
|
||||
if (location.pathname === "/auth") {
|
||||
return <Navigate to="/" replace />;
|
||||
}
|
||||
|
||||
return (
|
||||
<DashboardLayout
|
||||
title="EDR Freight"
|
||||
|
||||
@@ -10,13 +10,29 @@ import {
|
||||
BrandingProvider,
|
||||
configureIam,
|
||||
UserProvider,
|
||||
axiosInstance,
|
||||
} from "@tria-plc/iamui-common";
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
window.__IAM_CONFIG__ = {
|
||||
apiUrl: `${import.meta.env.VITE_BASE_API_URL}/api`,
|
||||
postLoginPath: "/user-management",
|
||||
postLoginPath: "/",
|
||||
};
|
||||
|
||||
// Unwrap the StandardResponse envelope ({ success, data, timestamp }) that the
|
||||
// freight API's ResponseTransformInterceptor adds to every response, so that
|
||||
// iamui-common can read response.data.token / response.data fields as expected.
|
||||
axiosInstance.interceptors.response.use((response) => {
|
||||
if (
|
||||
response.data &&
|
||||
typeof response.data === "object" &&
|
||||
"success" in response.data &&
|
||||
"data" in response.data
|
||||
) {
|
||||
response.data = response.data.data;
|
||||
}
|
||||
return response;
|
||||
});
|
||||
window.__USER_MANAGEMENT_BRANDING__ = {
|
||||
organizationName: "EDR Platform",
|
||||
appName: "EDR Portal",
|
||||
|
||||
@@ -4,6 +4,23 @@ export const api = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_URL,
|
||||
});
|
||||
|
||||
// TODO: integrate @edr/auth — add a request interceptor here that attaches
|
||||
// the bearer token from the auth package and a response interceptor that
|
||||
// triggers a refresh on 401.
|
||||
api.interceptors.request.use((config) => {
|
||||
const token = document.cookie
|
||||
.split("; ")
|
||||
.find((row) => row.startsWith("auth-token="))
|
||||
?.split("=")[1];
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
});
|
||||
|
||||
api.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
if (error.response?.status === 401) {
|
||||
window.location.href = "/auth";
|
||||
}
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user