From 07b08e30140be18c056c4bf166e0cb14229065fe Mon Sep 17 00:00:00 2001 From: David Date: Mon, 27 Oct 2025 20:49:06 +0100 Subject: [PATCH] fix path controller --- .claude/settings.local.json | 7 +- .../controllers/admin/csv-rates.controller.ts | 2 +- .../controllers/audit.controller.ts | 2 +- .../controllers/bookings.controller.ts | 2 +- .../controllers/notifications.controller.ts | 2 +- .../controllers/organizations.controller.ts | 2 +- .../controllers/rates.controller.ts | 2 +- .../controllers/users.controller.ts | 2 +- .../controllers/webhooks.controller.ts | 2 +- .../dashboard/dashboard.controller.ts | 2 +- ...700000000000-EnableFuzzySearch.ts.disabled | 52 - .../migrations/1730000000002-CreateUsers.ts | 132 +- ...730000000007-EnableFuzzySearch.ts.disabled | 52 - ...000000008-CreateAuditLogsTable.ts.disabled | 137 -- ...00009-CreateNotificationsTable.ts.disabled | 109 - ...0000000010-CreateWebhooksTable.ts.disabled | 99 - postman/README.md | 455 ++++ ...ditis_Complete_API.postman_collection.json | 2029 +++++++++++++++++ .../Xpeditis_Local.postman_environment.json | 63 + 19 files changed, 2628 insertions(+), 525 deletions(-) delete mode 100644 apps/backend/src/infrastructure/persistence/typeorm/migrations/1700000000000-EnableFuzzySearch.ts.disabled delete mode 100644 apps/backend/src/infrastructure/persistence/typeorm/migrations/1730000000007-EnableFuzzySearch.ts.disabled delete mode 100644 apps/backend/src/infrastructure/persistence/typeorm/migrations/1730000000008-CreateAuditLogsTable.ts.disabled delete mode 100644 apps/backend/src/infrastructure/persistence/typeorm/migrations/1730000000009-CreateNotificationsTable.ts.disabled delete mode 100644 apps/backend/src/infrastructure/persistence/typeorm/migrations/1730000000010-CreateWebhooksTable.ts.disabled create mode 100644 postman/README.md create mode 100644 postman/Xpeditis_Complete_API.postman_collection.json create mode 100644 postman/Xpeditis_Local.postman_environment.json diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 016ba16..7cb3f26 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -22,7 +22,12 @@ "Bash(PGPASSWORD=xpeditis_dev_password psql -h localhost -p 5432 -U xpeditis -d xpeditis_dev -c \"SELECT id, name FROM organizations LIMIT 5;\")", "Read(//Users/david/Documents/xpeditis/**)", "Bash(lsof:*)", - "Bash(xargs kill:*)" + "Bash(xargs kill:*)", + "Bash(TOKEN=\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMzg1MDVkMi1hMmVlLTQ5NmMtOWNjZC1iNjUyN2FjMzcxODgiLCJlbWFpbCI6InRlc3Q0QHhwZWRpdGlzLmNvbSIsInJvbGUiOiJ1c2VyIiwib3JnYW5pemF0aW9uSWQiOiJhMTIzNDU2Ny0wMDAwLTQwMDAtODAwMC0wMDAwMDAwMDAwMDEiLCJ0eXBlIjoiYWNjZXNzIiwiaWF0IjoxNzYxNTkwNzYxLCJleHAiOjE3NjE1OTE2NjF9.Jr9BbldL3TGW4pbXXc1XomzVMBRHn4lIgkKJ7XyjJgw\")", + "Bash(PGPASSWORD=xpeditis_dev_password psql -h localhost -p 5432 -U xpeditis -d xpeditis_dev -c \"SELECT id, email, role FROM users LIMIT 5;\")", + "Bash(PGPASSWORD=xpeditis_dev_password psql -h localhost -p 5432 -U xpeditis -d xpeditis_dev -c \"UPDATE users SET role = ''ADMIN'' WHERE email = ''test4@xpeditis.com''; SELECT id, email, role FROM users WHERE email = ''test4@xpeditis.com'';\")", + "Bash(TOKEN=\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMzg1MDVkMi1hMmVlLTQ5NmMtOWNjZC1iNjUyN2FjMzcxODgiLCJlbWFpbCI6InRlc3Q0QHhwZWRpdGlzLmNvbSIsInJvbGUiOiJBRE1JTiIsIm9yZ2FuaXphdGlvbklkIjoiYTEyMzQ1NjctMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMDAxIiwidHlwZSI6ImFjY2VzcyIsImlhdCI6MTc2MTU5MDg0OSwiZXhwIjoxNzYxNTkxNzQ5fQ.CPFhvgASXuklZ81FiuX_XwYZfh8xKG4tNG70JQ4Dv8M\")", + "Bash(PGPASSWORD=xpeditis_dev_password psql -h localhost -p 5432 -U xpeditis -d xpeditis_dev -c \"UPDATE users SET role = ''ADMIN'' WHERE email = ''dharnaud77@hotmail.fr''; SELECT id, email, role FROM users WHERE email = ''dharnaud77@hotmail.fr'';\")" ], "deny": [], "ask": [] diff --git a/apps/backend/src/application/controllers/admin/csv-rates.controller.ts b/apps/backend/src/application/controllers/admin/csv-rates.controller.ts index 89c7cea..a0113af 100644 --- a/apps/backend/src/application/controllers/admin/csv-rates.controller.ts +++ b/apps/backend/src/application/controllers/admin/csv-rates.controller.ts @@ -45,7 +45,7 @@ import { CsvRateMapper } from '../../mappers/csv-rate.mapper'; * Protected by JWT + Roles guard */ @ApiTags('Admin - CSV Rates') -@Controller('api/v1/admin/csv-rates') +@Controller('admin/csv-rates') @ApiBearerAuth() @UseGuards(JwtAuthGuard, RolesGuard) @Roles('ADMIN') // ⚠️ ONLY ADMIN can access these endpoints diff --git a/apps/backend/src/application/controllers/audit.controller.ts b/apps/backend/src/application/controllers/audit.controller.ts index 587537a..4ea6e87 100644 --- a/apps/backend/src/application/controllers/audit.controller.ts +++ b/apps/backend/src/application/controllers/audit.controller.ts @@ -52,7 +52,7 @@ class AuditLogQueryDto { @ApiTags('Audit Logs') @ApiBearerAuth() -@Controller('api/v1/audit-logs') +@Controller('audit-logs') @UseGuards(JwtAuthGuard, RolesGuard) export class AuditController { constructor(private readonly auditService: AuditService) {} diff --git a/apps/backend/src/application/controllers/bookings.controller.ts b/apps/backend/src/application/controllers/bookings.controller.ts index 3322b44..1fc91ef 100644 --- a/apps/backend/src/application/controllers/bookings.controller.ts +++ b/apps/backend/src/application/controllers/bookings.controller.ts @@ -56,7 +56,7 @@ import { WebhookService } from '../services/webhook.service'; import { WebhookEvent } from '../../domain/entities/webhook.entity'; @ApiTags('Bookings') -@Controller('api/v1/bookings') +@Controller('bookings') @UseGuards(JwtAuthGuard) @ApiBearerAuth() export class BookingsController { diff --git a/apps/backend/src/application/controllers/notifications.controller.ts b/apps/backend/src/application/controllers/notifications.controller.ts index 87d1e6a..9861a20 100644 --- a/apps/backend/src/application/controllers/notifications.controller.ts +++ b/apps/backend/src/application/controllers/notifications.controller.ts @@ -44,7 +44,7 @@ class NotificationResponseDto { @ApiTags('Notifications') @ApiBearerAuth() -@Controller('api/v1/notifications') +@Controller('notifications') @UseGuards(JwtAuthGuard) export class NotificationsController { constructor(private readonly notificationService: NotificationService) {} diff --git a/apps/backend/src/application/controllers/organizations.controller.ts b/apps/backend/src/application/controllers/organizations.controller.ts index e13eb31..8826414 100644 --- a/apps/backend/src/application/controllers/organizations.controller.ts +++ b/apps/backend/src/application/controllers/organizations.controller.ts @@ -54,7 +54,7 @@ import { v4 as uuidv4 } from 'uuid'; * - List organizations */ @ApiTags('Organizations') -@Controller('api/v1/organizations') +@Controller('organizations') @UseGuards(JwtAuthGuard, RolesGuard) @ApiBearerAuth() export class OrganizationsController { diff --git a/apps/backend/src/application/controllers/rates.controller.ts b/apps/backend/src/application/controllers/rates.controller.ts index f3c7de8..6cf5145 100644 --- a/apps/backend/src/application/controllers/rates.controller.ts +++ b/apps/backend/src/application/controllers/rates.controller.ts @@ -29,7 +29,7 @@ import { AvailableCompaniesDto, FilterOptionsDto } from '../dto/csv-rate-upload. import { CsvRateMapper } from '../mappers/csv-rate.mapper'; @ApiTags('Rates') -@Controller('api/v1/rates') +@Controller('rates') @ApiBearerAuth() export class RatesController { private readonly logger = new Logger(RatesController.name); diff --git a/apps/backend/src/application/controllers/users.controller.ts b/apps/backend/src/application/controllers/users.controller.ts index 57089e7..c747aed 100644 --- a/apps/backend/src/application/controllers/users.controller.ts +++ b/apps/backend/src/application/controllers/users.controller.ts @@ -61,7 +61,7 @@ import * as crypto from 'crypto'; * - Update own password */ @ApiTags('Users') -@Controller('api/v1/users') +@Controller('users') @UseGuards(JwtAuthGuard, RolesGuard) @ApiBearerAuth() export class UsersController { diff --git a/apps/backend/src/application/controllers/webhooks.controller.ts b/apps/backend/src/application/controllers/webhooks.controller.ts index 07348be..c96a882 100644 --- a/apps/backend/src/application/controllers/webhooks.controller.ts +++ b/apps/backend/src/application/controllers/webhooks.controller.ts @@ -59,7 +59,7 @@ class WebhookResponseDto { @ApiTags('Webhooks') @ApiBearerAuth() -@Controller('api/v1/webhooks') +@Controller('webhooks') @UseGuards(JwtAuthGuard, RolesGuard) export class WebhooksController { constructor(private readonly webhookService: WebhookService) {} diff --git a/apps/backend/src/application/dashboard/dashboard.controller.ts b/apps/backend/src/application/dashboard/dashboard.controller.ts index 945e98e..23871f9 100644 --- a/apps/backend/src/application/dashboard/dashboard.controller.ts +++ b/apps/backend/src/application/dashboard/dashboard.controller.ts @@ -8,7 +8,7 @@ import { Controller, Get, UseGuards, Request } from '@nestjs/common'; import { AnalyticsService } from '../services/analytics.service'; import { JwtAuthGuard } from '../guards/jwt-auth.guard'; -@Controller('api/v1/dashboard') +@Controller('dashboard') @UseGuards(JwtAuthGuard) export class DashboardController { constructor(private readonly analyticsService: AnalyticsService) {} diff --git a/apps/backend/src/infrastructure/persistence/typeorm/migrations/1700000000000-EnableFuzzySearch.ts.disabled b/apps/backend/src/infrastructure/persistence/typeorm/migrations/1700000000000-EnableFuzzySearch.ts.disabled deleted file mode 100644 index 51f82e9..0000000 --- a/apps/backend/src/infrastructure/persistence/typeorm/migrations/1700000000000-EnableFuzzySearch.ts.disabled +++ /dev/null @@ -1,52 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class EnableFuzzySearch1700000000000 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - // Enable pg_trgm extension for trigram similarity search - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS pg_trgm;`); - - // Create GIN indexes for full-text search on bookings - await queryRunner.query(` - CREATE INDEX IF NOT EXISTS idx_booking_number_trgm - ON bookings USING gin(booking_number gin_trgm_ops); - `); - - await queryRunner.query(` - CREATE INDEX IF NOT EXISTS idx_shipper_name_trgm - ON bookings USING gin(shipper_name gin_trgm_ops); - `); - - await queryRunner.query(` - CREATE INDEX IF NOT EXISTS idx_consignee_name_trgm - ON bookings USING gin(consignee_name gin_trgm_ops); - `); - - // Create full-text search indexes using ts_vector - await queryRunner.query(` - CREATE INDEX IF NOT EXISTS idx_booking_number_fts - ON bookings USING gin(to_tsvector('english', booking_number)); - `); - - await queryRunner.query(` - CREATE INDEX IF NOT EXISTS idx_shipper_name_fts - ON bookings USING gin(to_tsvector('english', shipper_name)); - `); - - await queryRunner.query(` - CREATE INDEX IF NOT EXISTS idx_consignee_name_fts - ON bookings USING gin(to_tsvector('english', consignee_name)); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - // Drop indexes - await queryRunner.query(`DROP INDEX IF EXISTS idx_booking_number_trgm;`); - await queryRunner.query(`DROP INDEX IF EXISTS idx_shipper_name_trgm;`); - await queryRunner.query(`DROP INDEX IF EXISTS idx_consignee_name_trgm;`); - await queryRunner.query(`DROP INDEX IF EXISTS idx_booking_number_fts;`); - await queryRunner.query(`DROP INDEX IF EXISTS idx_shipper_name_fts;`); - await queryRunner.query(`DROP INDEX IF EXISTS idx_consignee_name_fts;`); - - // Note: We don't drop the pg_trgm extension as other parts of the system might use it - } -} diff --git a/apps/backend/src/infrastructure/persistence/typeorm/migrations/1730000000002-CreateUsers.ts b/apps/backend/src/infrastructure/persistence/typeorm/migrations/1730000000002-CreateUsers.ts index 16ac3ae..74c039e 100644 --- a/apps/backend/src/infrastructure/persistence/typeorm/migrations/1730000000002-CreateUsers.ts +++ b/apps/backend/src/infrastructure/persistence/typeorm/migrations/1730000000002-CreateUsers.ts @@ -1,66 +1,66 @@ -/** - * Migration: Create Users Table - */ - -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateUsers1730000000002 implements MigrationInterface { - name = 'CreateUsers1730000000002'; - - public async up(queryRunner: QueryRunner): Promise { - // Create users table - await queryRunner.query(` - CREATE TABLE "users" ( - "id" UUID NOT NULL DEFAULT uuid_generate_v4(), - "organization_id" UUID NOT NULL, - "email" VARCHAR(255) NOT NULL, - "password_hash" VARCHAR(255) NOT NULL, - "role" VARCHAR(50) NOT NULL, - "first_name" VARCHAR(100) NOT NULL, - "last_name" VARCHAR(100) NOT NULL, - "phone_number" VARCHAR(20) NULL, - "totp_secret" VARCHAR(255) NULL, - "is_email_verified" BOOLEAN NOT NULL DEFAULT FALSE, - "is_active" BOOLEAN NOT NULL DEFAULT TRUE, - "last_login_at" TIMESTAMP NULL, - "created_at" TIMESTAMP NOT NULL DEFAULT NOW(), - "updated_at" TIMESTAMP NOT NULL DEFAULT NOW(), - CONSTRAINT "pk_users" PRIMARY KEY ("id"), - CONSTRAINT "uq_users_email" UNIQUE ("email"), - CONSTRAINT "fk_users_organization" FOREIGN KEY ("organization_id") - REFERENCES "organizations"("id") ON DELETE CASCADE, - CONSTRAINT "chk_users_email" CHECK (LOWER("email") = "email"), - CONSTRAINT "chk_users_role" CHECK ("role" IN ('ADMIN', 'MANAGER', 'USER', 'VIEWER')) - ) - `); - - // Create indexes - await queryRunner.query(` - CREATE INDEX "idx_users_email" ON "users" ("email") - `); - await queryRunner.query(` - CREATE INDEX "idx_users_organization" ON "users" ("organization_id") - `); - await queryRunner.query(` - CREATE INDEX "idx_users_role" ON "users" ("role") - `); - await queryRunner.query(` - CREATE INDEX "idx_users_active" ON "users" ("is_active") - `); - - // Add comments - await queryRunner.query(` - COMMENT ON TABLE "users" IS 'User accounts for authentication and authorization' - `); - await queryRunner.query(` - COMMENT ON COLUMN "users"."password_hash" IS 'Bcrypt hash (12+ rounds)' - `); - await queryRunner.query(` - COMMENT ON COLUMN "users"."totp_secret" IS 'TOTP secret for 2FA (optional)' - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "users"`); - } -} +/** + * Migration: Create Users Table + */ + +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class CreateUsers1730000000002 implements MigrationInterface { + name = 'CreateUsers1730000000002'; + + public async up(queryRunner: QueryRunner): Promise { + // Create users table + await queryRunner.query(` + CREATE TABLE "users" ( + "id" UUID NOT NULL DEFAULT uuid_generate_v4(), + "organization_id" UUID NOT NULL, + "email" VARCHAR(255) NOT NULL, + "password_hash" VARCHAR(255) NOT NULL, + "role" VARCHAR(50) NOT NULL, + "first_name" VARCHAR(100) NOT NULL, + "last_name" VARCHAR(100) NOT NULL, + "phone_number" VARCHAR(20) NULL, + "totp_secret" VARCHAR(255) NULL, + "is_email_verified" BOOLEAN NOT NULL DEFAULT FALSE, + "is_active" BOOLEAN NOT NULL DEFAULT TRUE, + "last_login_at" TIMESTAMP NULL, + "created_at" TIMESTAMP NOT NULL DEFAULT NOW(), + "updated_at" TIMESTAMP NOT NULL DEFAULT NOW(), + CONSTRAINT "pk_users" PRIMARY KEY ("id"), + CONSTRAINT "uq_users_email" UNIQUE ("email"), + CONSTRAINT "fk_users_organization" FOREIGN KEY ("organization_id") + REFERENCES "organizations"("id") ON DELETE CASCADE, + CONSTRAINT "chk_users_email" CHECK (LOWER("email") = "email"), + CONSTRAINT "chk_users_role" CHECK ("role" IN ('ADMIN', 'MANAGER', 'USER', 'VIEWER')) + ) + `); + + // Create indexes + await queryRunner.query(` + CREATE INDEX "idx_users_email" ON "users" ("email") + `); + await queryRunner.query(` + CREATE INDEX "idx_users_organization" ON "users" ("organization_id") + `); + await queryRunner.query(` + CREATE INDEX "idx_users_role" ON "users" ("role") + `); + await queryRunner.query(` + CREATE INDEX "idx_users_active" ON "users" ("is_active") + `); + + // Add comments + await queryRunner.query(` + COMMENT ON TABLE "users" IS 'User accounts for authentication and authorization' + `); + await queryRunner.query(` + COMMENT ON COLUMN "users"."password_hash" IS 'Bcrypt hash (12+ rounds)' + `); + await queryRunner.query(` + COMMENT ON COLUMN "users"."totp_secret" IS 'TOTP secret for 2FA (optional)' + `); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE "users"`); + } +} diff --git a/apps/backend/src/infrastructure/persistence/typeorm/migrations/1730000000007-EnableFuzzySearch.ts.disabled b/apps/backend/src/infrastructure/persistence/typeorm/migrations/1730000000007-EnableFuzzySearch.ts.disabled deleted file mode 100644 index 337fe01..0000000 --- a/apps/backend/src/infrastructure/persistence/typeorm/migrations/1730000000007-EnableFuzzySearch.ts.disabled +++ /dev/null @@ -1,52 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class EnableFuzzySearch1730000000007 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - // Enable pg_trgm extension for trigram similarity search - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS pg_trgm;`); - - // Create GIN indexes for full-text search on bookings - await queryRunner.query(` - CREATE INDEX IF NOT EXISTS idx_booking_number_trgm - ON bookings USING gin(booking_number gin_trgm_ops); - `); - - await queryRunner.query(` - CREATE INDEX IF NOT EXISTS idx_shipper_name_trgm - ON bookings USING gin(shipper_name gin_trgm_ops); - `); - - await queryRunner.query(` - CREATE INDEX IF NOT EXISTS idx_consignee_name_trgm - ON bookings USING gin(consignee_name gin_trgm_ops); - `); - - // Create full-text search indexes using ts_vector - await queryRunner.query(` - CREATE INDEX IF NOT EXISTS idx_booking_number_fts - ON bookings USING gin(to_tsvector('english', booking_number)); - `); - - await queryRunner.query(` - CREATE INDEX IF NOT EXISTS idx_shipper_name_fts - ON bookings USING gin(to_tsvector('english', shipper_name)); - `); - - await queryRunner.query(` - CREATE INDEX IF NOT EXISTS idx_consignee_name_fts - ON bookings USING gin(to_tsvector('english', consignee_name)); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - // Drop indexes - await queryRunner.query(`DROP INDEX IF EXISTS idx_booking_number_trgm;`); - await queryRunner.query(`DROP INDEX IF EXISTS idx_shipper_name_trgm;`); - await queryRunner.query(`DROP INDEX IF EXISTS idx_consignee_name_trgm;`); - await queryRunner.query(`DROP INDEX IF EXISTS idx_booking_number_fts;`); - await queryRunner.query(`DROP INDEX IF EXISTS idx_shipper_name_fts;`); - await queryRunner.query(`DROP INDEX IF EXISTS idx_consignee_name_fts;`); - - // Note: We don't drop the pg_trgm extension as other parts of the system might use it - } -} diff --git a/apps/backend/src/infrastructure/persistence/typeorm/migrations/1730000000008-CreateAuditLogsTable.ts.disabled b/apps/backend/src/infrastructure/persistence/typeorm/migrations/1730000000008-CreateAuditLogsTable.ts.disabled deleted file mode 100644 index 514d3cc..0000000 --- a/apps/backend/src/infrastructure/persistence/typeorm/migrations/1730000000008-CreateAuditLogsTable.ts.disabled +++ /dev/null @@ -1,137 +0,0 @@ -import { MigrationInterface, QueryRunner, Table, TableIndex } from 'typeorm'; - -export class CreateAuditLogsTable1730000000008 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.createTable( - new Table({ - name: 'audit_logs', - columns: [ - { - name: 'id', - type: 'uuid', - isPrimary: true, - }, - { - name: 'action', - type: 'varchar', - length: '100', - isNullable: false, - }, - { - name: 'status', - type: 'varchar', - length: '20', - isNullable: false, - }, - { - name: 'user_id', - type: 'uuid', - isNullable: false, - }, - { - name: 'user_email', - type: 'varchar', - length: '255', - isNullable: false, - }, - { - name: 'organization_id', - type: 'uuid', - isNullable: false, - }, - { - name: 'resource_type', - type: 'varchar', - length: '100', - isNullable: true, - }, - { - name: 'resource_id', - type: 'varchar', - length: '255', - isNullable: true, - }, - { - name: 'resource_name', - type: 'varchar', - length: '255', - isNullable: true, - }, - { - name: 'metadata', - type: 'jsonb', - isNullable: true, - }, - { - name: 'ip_address', - type: 'varchar', - length: '45', - isNullable: true, - }, - { - name: 'user_agent', - type: 'text', - isNullable: true, - }, - { - name: 'error_message', - type: 'text', - isNullable: true, - }, - { - name: 'timestamp', - type: 'timestamp', - default: 'CURRENT_TIMESTAMP', - isNullable: false, - }, - ], - }), - true, - ); - - // Create indexes for efficient querying - await queryRunner.createIndex( - 'audit_logs', - new TableIndex({ - name: 'idx_audit_logs_organization_timestamp', - columnNames: ['organization_id', 'timestamp'], - }), - ); - - await queryRunner.createIndex( - 'audit_logs', - new TableIndex({ - name: 'idx_audit_logs_user_timestamp', - columnNames: ['user_id', 'timestamp'], - }), - ); - - await queryRunner.createIndex( - 'audit_logs', - new TableIndex({ - name: 'idx_audit_logs_resource', - columnNames: ['resource_type', 'resource_id'], - }), - ); - - await queryRunner.createIndex( - 'audit_logs', - new TableIndex({ - name: 'idx_audit_logs_action', - columnNames: ['action'], - }), - ); - - await queryRunner.createIndex( - 'audit_logs', - new TableIndex({ - name: 'idx_audit_logs_timestamp', - columnNames: ['timestamp'], - }), - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropTable('audit_logs'); - } -} diff --git a/apps/backend/src/infrastructure/persistence/typeorm/migrations/1730000000009-CreateNotificationsTable.ts.disabled b/apps/backend/src/infrastructure/persistence/typeorm/migrations/1730000000009-CreateNotificationsTable.ts.disabled deleted file mode 100644 index 2320cd4..0000000 --- a/apps/backend/src/infrastructure/persistence/typeorm/migrations/1730000000009-CreateNotificationsTable.ts.disabled +++ /dev/null @@ -1,109 +0,0 @@ -import { MigrationInterface, QueryRunner, Table, TableIndex } from 'typeorm'; - -export class CreateNotificationsTable1730000000009 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.createTable( - new Table({ - name: 'notifications', - columns: [ - { - name: 'id', - type: 'uuid', - isPrimary: true, - }, - { - name: 'user_id', - type: 'uuid', - isNullable: false, - }, - { - name: 'organization_id', - type: 'uuid', - isNullable: false, - }, - { - name: 'type', - type: 'varchar', - length: '50', - isNullable: false, - }, - { - name: 'priority', - type: 'varchar', - length: '20', - isNullable: false, - }, - { - name: 'title', - type: 'varchar', - length: '255', - isNullable: false, - }, - { - name: 'message', - type: 'text', - isNullable: false, - }, - { - name: 'metadata', - type: 'jsonb', - isNullable: true, - }, - { - name: 'read', - type: 'boolean', - default: false, - isNullable: false, - }, - { - name: 'read_at', - type: 'timestamp', - isNullable: true, - }, - { - name: 'action_url', - type: 'varchar', - length: '500', - isNullable: true, - }, - { - name: 'created_at', - type: 'timestamp', - default: 'CURRENT_TIMESTAMP', - isNullable: false, - }, - ], - }), - true, - ); - - // Create indexes for efficient querying - await queryRunner.createIndex( - 'notifications', - new TableIndex({ - name: 'idx_notifications_user_read_created', - columnNames: ['user_id', 'read', 'created_at'], - }), - ); - - await queryRunner.createIndex( - 'notifications', - new TableIndex({ - name: 'idx_notifications_organization_created', - columnNames: ['organization_id', 'created_at'], - }), - ); - - await queryRunner.createIndex( - 'notifications', - new TableIndex({ - name: 'idx_notifications_user_created', - columnNames: ['user_id', 'created_at'], - }), - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropTable('notifications'); - } -} diff --git a/apps/backend/src/infrastructure/persistence/typeorm/migrations/1730000000010-CreateWebhooksTable.ts.disabled b/apps/backend/src/infrastructure/persistence/typeorm/migrations/1730000000010-CreateWebhooksTable.ts.disabled deleted file mode 100644 index 3168aa1..0000000 --- a/apps/backend/src/infrastructure/persistence/typeorm/migrations/1730000000010-CreateWebhooksTable.ts.disabled +++ /dev/null @@ -1,99 +0,0 @@ -import { MigrationInterface, QueryRunner, Table, TableIndex } from 'typeorm'; - -export class CreateWebhooksTable1730000000010 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.createTable( - new Table({ - name: 'webhooks', - columns: [ - { - name: 'id', - type: 'uuid', - isPrimary: true, - }, - { - name: 'organization_id', - type: 'uuid', - isNullable: false, - }, - { - name: 'url', - type: 'varchar', - length: '500', - isNullable: false, - }, - { - name: 'events', - type: 'text', - isNullable: false, - }, - { - name: 'secret', - type: 'varchar', - length: '255', - isNullable: false, - }, - { - name: 'status', - type: 'varchar', - length: '20', - isNullable: false, - }, - { - name: 'description', - type: 'text', - isNullable: true, - }, - { - name: 'headers', - type: 'jsonb', - isNullable: true, - }, - { - name: 'retry_count', - type: 'int', - default: 0, - isNullable: false, - }, - { - name: 'last_triggered_at', - type: 'timestamp', - isNullable: true, - }, - { - name: 'failure_count', - type: 'int', - default: 0, - isNullable: false, - }, - { - name: 'created_at', - type: 'timestamp', - default: 'CURRENT_TIMESTAMP', - isNullable: false, - }, - { - name: 'updated_at', - type: 'timestamp', - default: 'CURRENT_TIMESTAMP', - isNullable: false, - }, - ], - }), - true, - ); - - // Create index for efficient querying - await queryRunner.createIndex( - 'webhooks', - new TableIndex({ - name: 'idx_webhooks_organization_status', - columnNames: ['organization_id', 'status'], - }), - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropTable('webhooks'); - } -} diff --git a/postman/README.md b/postman/README.md new file mode 100644 index 0000000..bc4d2a6 --- /dev/null +++ b/postman/README.md @@ -0,0 +1,455 @@ +# Xpeditis API - Postman Collection + +Complete Postman collection for testing the Xpeditis B2B Maritime Freight Platform API. + +## Overview + +This Postman collection provides comprehensive API testing coverage for all Xpeditis endpoints, including: + +- Authentication (register, login, refresh tokens) +- Rate search (standard and CSV-based with advanced filters) +- Booking management (create, list, search, export) +- User management (CRUD operations) +- Organization management +- Notifications +- Audit logs +- GDPR compliance +- Webhooks +- Dashboard analytics +- Admin CSV rate uploads +- Health checks + +## Files + +- **Xpeditis_Complete_API.postman_collection.json** - Complete API collection with all endpoints +- **Xpeditis_Local.postman_environment.json** - Local development environment variables + +## Quick Start + +### 1. Import Collection and Environment + +1. Open Postman +2. Click **Import** button +3. Select both files: + - `Xpeditis_Complete_API.postman_collection.json` + - `Xpeditis_Local.postman_environment.json` +4. Click **Import** + +### 2. Select Environment + +1. Click the environment dropdown (top right) +2. Select **Xpeditis Local Environment** + +### 3. Start Backend Server + +Ensure the Xpeditis backend is running: + +```bash +cd apps/backend +npm run dev +``` + +Backend will be available at: `http://localhost:4000` + +### 4. Test Authentication + +1. Open the **Authentication** folder +2. Run **Register New User** or **Login** +3. The access token will be automatically saved to the environment + +Now you're ready to test all authenticated endpoints! + +## API Endpoint Summary + +### Authentication (5 endpoints) +- **POST** `/auth/register` - Register new user (Public) +- **POST** `/auth/login` - Login with credentials (Public) +- **POST** `/auth/refresh` - Refresh access token (Public) +- **POST** `/auth/logout` - Logout user (Auth required) +- **GET** `/auth/me` - Get current user profile (Auth required) + +### Rates (4 endpoints) +- **POST** `/api/v1/rates/search` - Search shipping rates (Auth required) +- **POST** `/api/v1/rates/search-csv` - Advanced CSV rate search (Auth required) +- **GET** `/api/v1/rates/companies` - Get available companies (Auth required) +- **GET** `/api/v1/rates/filters/options` - Get filter options (Auth required) + +### Bookings (8 endpoints) +- **POST** `/api/v1/bookings` - Create booking (Auth required) +- **GET** `/api/v1/bookings/:id` - Get booking by ID (Auth required) +- **GET** `/api/v1/bookings/number/:bookingNumber` - Get booking by number (Auth required) +- **GET** `/api/v1/bookings` - List bookings with pagination (Auth required) +- **GET** `/api/v1/bookings/search/fuzzy` - Fuzzy search bookings (Auth required) +- **GET** `/api/v1/bookings/advanced/search` - Advanced search with filters (Auth required) +- **POST** `/api/v1/bookings/export` - Export bookings (CSV/Excel/JSON) (Auth required) + +### Users (6 endpoints) +- **POST** `/api/v1/users` - Create user (Admin/Manager) +- **GET** `/api/v1/users/:id` - Get user by ID (Auth required) +- **PATCH** `/api/v1/users/:id` - Update user (Admin/Manager) +- **DELETE** `/api/v1/users/:id` - Delete user (Admin only) +- **GET** `/api/v1/users` - List users (Auth required) +- **PATCH** `/api/v1/users/me/password` - Update own password (Auth required) + +### Organizations (4 endpoints) +- **POST** `/api/v1/organizations` - Create organization (Admin only) +- **GET** `/api/v1/organizations/:id` - Get organization by ID (Auth required) +- **PATCH** `/api/v1/organizations/:id` - Update organization (Admin/Manager) +- **GET** `/api/v1/organizations` - List organizations (Auth required) + +### Notifications (6 endpoints) +- **GET** `/api/v1/notifications` - Get notifications (Auth required) +- **GET** `/api/v1/notifications/unread` - Get unread notifications (Auth required) +- **GET** `/api/v1/notifications/unread/count` - Get unread count (Auth required) +- **PATCH** `/api/v1/notifications/:id/read` - Mark as read (Auth required) +- **POST** `/api/v1/notifications/read-all` - Mark all as read (Auth required) +- **DELETE** `/api/v1/notifications/:id` - Delete notification (Auth required) + +### Audit Logs (4 endpoints) +- **GET** `/api/v1/audit-logs` - Get audit logs with filters (Admin/Manager) +- **GET** `/api/v1/audit-logs/resource/:type/:id` - Get resource audit trail (Auth required) +- **GET** `/api/v1/audit-logs/organization/activity` - Get organization activity (Admin/Manager) +- **GET** `/api/v1/audit-logs/user/:userId/activity` - Get user activity (Admin/Manager) + +### GDPR (6 endpoints) +- **GET** `/gdpr/export` - Export user data as JSON (Auth required) +- **GET** `/gdpr/export/csv` - Export user data as CSV (Auth required) +- **DELETE** `/gdpr/delete-account` - Delete account (Auth required) +- **POST** `/gdpr/consent` - Record consent (Auth required) +- **POST** `/gdpr/consent/withdraw` - Withdraw consent (Auth required) +- **GET** `/gdpr/consent` - Get consent status (Auth required) + +### Webhooks (7 endpoints) +- **POST** `/api/v1/webhooks` - Create webhook (Admin/Manager) +- **GET** `/api/v1/webhooks` - Get all webhooks (Admin/Manager) +- **GET** `/api/v1/webhooks/:id` - Get webhook by ID (Admin/Manager) +- **PATCH** `/api/v1/webhooks/:id` - Update webhook (Admin/Manager) +- **POST** `/api/v1/webhooks/:id/activate` - Activate webhook (Admin/Manager) +- **POST** `/api/v1/webhooks/:id/deactivate` - Deactivate webhook (Admin/Manager) +- **DELETE** `/api/v1/webhooks/:id` - Delete webhook (Admin/Manager) + +### Dashboard (4 endpoints) +- **GET** `/api/v1/dashboard/kpis` - Get dashboard KPIs (Auth required) +- **GET** `/api/v1/dashboard/bookings-chart` - Get bookings chart data (Auth required) +- **GET** `/api/v1/dashboard/top-trade-lanes` - Get top trade lanes (Auth required) +- **GET** `/api/v1/dashboard/alerts` - Get dashboard alerts (Auth required) + +### Admin - CSV Rates (5 endpoints) +- **POST** `/api/v1/admin/csv-rates/upload` - Upload CSV rate file (Admin only) +- **GET** `/api/v1/admin/csv-rates/config` - Get all configurations (Admin only) +- **GET** `/api/v1/admin/csv-rates/config/:companyName` - Get config by company (Admin only) +- **POST** `/api/v1/admin/csv-rates/validate/:companyName` - Validate CSV file (Admin only) +- **DELETE** `/api/v1/admin/csv-rates/config/:companyName` - Delete configuration (Admin only) + +### Health (3 endpoints) +- **GET** `/health` - Health check (Public) +- **GET** `/health/ready` - Readiness check (Public) +- **GET** `/health/live` - Liveness check (Public) + +**Total: 68 endpoints** + +## Environment Variables + +The environment file includes the following variables: + +| Variable | Description | Auto-populated | +|----------|-------------|----------------| +| `baseUrl` | API base URL | No (default: `http://localhost:4000`) | +| `token` | JWT access token | Yes (after login/register) | +| `refreshToken` | JWT refresh token | Yes (after login/register) | +| `userId` | Current user ID | Yes (after login/register) | +| `userEmail` | Current user email | Yes (after login/register) | +| `organizationId` | Current user's organization ID | Yes (after login/register) | +| `rateQuoteId` | Last searched rate quote ID | Yes (after rate search) | +| `bookingId` | Last created booking ID | Yes (after booking creation) | +| `bookingNumber` | Last created booking number | Yes (after booking creation) | + +Variables marked "Yes" under **Auto-populated** are automatically set by test scripts when you run the corresponding requests. + +## Common Workflows + +### 1. Complete Booking Flow + +1. **Register/Login** → Auto-saves token +2. **Search Rates** → Auto-saves `rateQuoteId` +3. **Create Booking** → Auto-saves `bookingId` and `bookingNumber` +4. **Get Booking Details** → Use saved `bookingId` +5. **List Bookings** → View all bookings +6. **Export Bookings** → Download CSV/Excel + +### 2. User Management Flow + +1. **Login as Admin** → Auto-saves token +2. **Create User** → Create new team member +3. **List Users** → View all users +4. **Update User** → Change role or details +5. **Delete User** → Deactivate account + +### 3. CSV Rate Upload Flow (Admin) + +1. **Login as Admin** → Auto-saves token +2. **Upload CSV File** → Upload carrier rates +3. **Validate CSV** → Check file structure +4. **Search CSV Rates** → Test the uploaded rates +5. **Get Filter Options** → View available carriers + +## Authentication + +Most endpoints require authentication. The collection uses **Bearer Token** authentication. + +### How It Works + +1. Run **Register** or **Login** request +2. Test script automatically saves `accessToken` to `{{token}}` variable +3. All authenticated requests use `{{token}}` in the Authorization header +4. Tokens expire after 15 minutes (configurable) +5. Use **Refresh Access Token** to get a new token + +### Manual Token Setup + +If needed, you can manually set the token: + +1. Copy the access token from a login response +2. Go to Environments → Xpeditis Local Environment +3. Paste into the `token` variable +4. Save + +## Role-Based Access Control (RBAC) + +Different endpoints require different roles: + +- **Public**: No authentication required +- **User**: Any authenticated user +- **Manager**: Manager or Admin role +- **Admin**: Admin role only + +| Role | Permissions | +|------|-------------| +| **viewer** | Read-only access to bookings and rates | +| **user** | Create and view own bookings, search rates | +| **manager** | Manage organization bookings and users | +| **admin** | Full system access, manage all organizations | + +## Testing Best Practices + +### 1. Run Requests in Order + +For best results, run requests in this order: + +1. **Health Check** (verify backend is running) +2. **Register/Login** (get authentication token) +3. **Test feature-specific endpoints** + +### 2. Check Response Status + +- ✅ **200/201** - Success +- ⚠️ **400** - Validation error (check request body) +- 🔒 **401** - Unauthorized (token missing/expired) +- 🚫 **403** - Forbidden (insufficient permissions) +- ❌ **404** - Not found +- 💥 **500** - Server error + +### 3. Use Console for Debugging + +The collection includes test scripts that log useful info: + +1. Open Postman Console (bottom left) +2. Run a request +3. View logged variables and debug info + +### 4. Validate Responses + +Test scripts automatically validate responses and extract key data. Check the **Test Results** tab after each request. + +## Example Request Bodies + +### Register User + +```json +{ + "email": "john.doe@acme.com", + "password": "SecurePassword123!", + "firstName": "John", + "lastName": "Doe" +} +``` + +### Search Rates + +```json +{ + "origin": "NLRTM", + "destination": "CNSHA", + "containerType": "40HC", + "mode": "FCL", + "departureDate": "2025-02-15", + "quantity": 2, + "weight": 20000, + "volume": 50.5, + "isHazmat": false +} +``` + +### Create Booking + +```json +{ + "rateQuoteId": "{{rateQuoteId}}", + "shipper": { + "name": "Acme Corporation", + "address": { + "street": "123 Main Street", + "city": "Rotterdam", + "postalCode": "3000 AB", + "country": "NL" + }, + "contactName": "John Doe", + "contactEmail": "john.doe@acme.com", + "contactPhone": "+31612345678" + }, + "consignee": { + "name": "Global Trading Ltd", + "address": { + "street": "456 Harbor Road", + "city": "Shanghai", + "postalCode": "200000", + "country": "CN" + }, + "contactName": "Jane Smith", + "contactEmail": "jane.smith@globaltrading.com", + "contactPhone": "+8613800000000" + }, + "cargoDescription": "Electronics and consumer goods - fragile items", + "containers": [ + { + "type": "40HC", + "vgm": 22000, + "sealNumber": "SEAL123456" + } + ], + "specialInstructions": "Please handle with care. Delivery before 5 PM." +} +``` + +## Advanced Features + +### 1. Fuzzy Search + +The fuzzy search endpoint is tolerant to typos and partial matches: + +``` +GET /api/v1/bookings/search/fuzzy?q=WCM-2025&limit=20 +``` + +Searches across: +- Booking number +- Shipper name +- Consignee name + +### 2. Advanced Filtering + +Use the advanced search endpoint for complex queries: + +``` +GET /api/v1/bookings/advanced/search? + status=confirmed,in_transit& + createdFrom=2025-01-01& + shipper=Acme& + sortBy=createdAt& + sortOrder=desc +``` + +### 3. Export Formats + +Export bookings in multiple formats: + +- **CSV** - Spreadsheet format +- **XLSX** - Excel format +- **JSON** - Raw data format + +Specify fields to export: + +```json +{ + "format": "csv", + "fields": [ + "bookingNumber", + "status", + "shipper", + "consignee", + "origin", + "destination", + "carrier", + "createdAt" + ] +} +``` + +### 4. Webhook Events + +Available webhook events: + +- `booking.created` +- `booking.confirmed` +- `booking.cancelled` +- `shipment.departed` +- `shipment.arrived` +- `document.uploaded` + +## Troubleshooting + +### Issue: 401 Unauthorized + +**Solution**: Token expired or missing +1. Run **Login** request again +2. Or use **Refresh Access Token** + +### Issue: 403 Forbidden + +**Solution**: Insufficient permissions +- Check your user role +- Some endpoints require Admin or Manager role + +### Issue: 404 Not Found + +**Solution**: Resource doesn't exist +- Verify the ID in the URL +- Check if the resource belongs to your organization + +### Issue: 500 Internal Server Error + +**Solution**: Backend error +1. Check backend logs +2. Verify database is running +3. Check Redis connection + +### Issue: Connection Refused + +**Solution**: Backend not running +```bash +cd apps/backend +npm run dev +``` + +## Additional Resources + +- **Swagger UI**: http://localhost:4000/api/docs +- **Architecture Docs**: [ARCHITECTURE.md](../ARCHITECTURE.md) +- **Deployment Guide**: [DEPLOYMENT.md](../DEPLOYMENT.md) +- **Test Coverage**: [TEST_COVERAGE_REPORT.md](../TEST_COVERAGE_REPORT.md) + +## Support + +For issues or questions: + +1. Check the [API documentation](http://localhost:4000/api/docs) +2. Review controller source code in `apps/backend/src/application/controllers/` +3. Check DTO definitions in `apps/backend/src/application/dto/` + +## Version History + +- **v1.0.0** (2025-10-27) - Initial complete collection with all 68 endpoints + +--- + +**Happy Testing!** 🚢✨ diff --git a/postman/Xpeditis_Complete_API.postman_collection.json b/postman/Xpeditis_Complete_API.postman_collection.json new file mode 100644 index 0000000..a0bc812 --- /dev/null +++ b/postman/Xpeditis_Complete_API.postman_collection.json @@ -0,0 +1,2029 @@ +{ + "info": { + "name": "Xpeditis Complete API", + "description": "Complete Postman collection for Xpeditis B2B Maritime Freight Platform API.\n\n## Overview\nXpeditis is a maritime freight booking and management platform that allows freight forwarders to search shipping rates, book containers, and manage shipments.\n\n## Authentication\nMost endpoints require JWT authentication. Use the `/auth/login` or `/auth/register` endpoints first to obtain an access token, which will be automatically saved to the `{{token}}` variable.\n\n## Base URL\nDefault: `http://localhost:4000`\n\n## Getting Started\n1. Import the environment file `Xpeditis_Local.postman_environment.json`\n2. Register a new user or login\n3. The access token will be automatically saved\n4. Start making authenticated requests\n\n## Documentation\n- Swagger UI: http://localhost:4000/api/docs\n- GitHub: https://github.com/xpeditis", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Authentication", + "item": [ + { + "name": "Register New User", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "if (pm.response.code === 201) {", + " const jsonData = pm.response.json();", + " pm.environment.set('token', jsonData.accessToken);", + " pm.environment.set('refreshToken', jsonData.refreshToken);", + " pm.environment.set('userId', jsonData.user.id);", + " pm.environment.set('userEmail', jsonData.user.email);", + " pm.environment.set('organizationId', jsonData.user.organizationId);", + " console.log('Access token saved to environment');", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"john.doe@acme.com\",\n \"password\": \"SecurePassword123!\",\n \"firstName\": \"John\",\n \"lastName\": \"Doe\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/auth/register", + "host": ["{{baseUrl}}"], + "path": ["auth", "register"] + }, + "description": "Create a new user account with email and password. Returns JWT tokens.\n\n**No authentication required**" + }, + "response": [] + }, + { + "name": "Login", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "if (pm.response.code === 200) {", + " const jsonData = pm.response.json();", + " pm.environment.set('token', jsonData.accessToken);", + " pm.environment.set('refreshToken', jsonData.refreshToken);", + " pm.environment.set('userId', jsonData.user.id);", + " pm.environment.set('userEmail', jsonData.user.email);", + " pm.environment.set('organizationId', jsonData.user.organizationId);", + " console.log('Access token saved to environment');", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"john.doe@acme.com\",\n \"password\": \"SecurePassword123!\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/auth/login", + "host": ["{{baseUrl}}"], + "path": ["auth", "login"] + }, + "description": "Authenticate with email and password. Returns JWT tokens.\n\n**No authentication required**" + }, + "response": [] + }, + { + "name": "Refresh Access Token", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"refreshToken\": \"{{refreshToken}}\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/auth/refresh", + "host": ["{{baseUrl}}"], + "path": ["auth", "refresh"] + }, + "description": "Get a new access token using a valid refresh token. Refresh tokens are long-lived (7 days).\n\n**No authentication required**" + }, + "response": [] + }, + { + "name": "Logout", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "url": { + "raw": "{{baseUrl}}/auth/logout", + "host": ["{{baseUrl}}"], + "path": ["auth", "logout"] + }, + "description": "Logout the current user. Currently handled client-side by removing tokens.\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Get Current User Profile", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/auth/me", + "host": ["{{baseUrl}}"], + "path": ["auth", "me"] + }, + "description": "Returns the profile of the authenticated user.\n\n**Requires authentication**" + }, + "response": [] + } + ], + "description": "Authentication endpoints for user registration, login, and token management." + }, + { + "name": "Rates", + "item": [ + { + "name": "Search Shipping Rates", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "if (pm.response.code === 200) {", + " const jsonData = pm.response.json();", + " if (jsonData.quotes && jsonData.quotes.length > 0) {", + " pm.environment.set('rateQuoteId', jsonData.quotes[0].id);", + " console.log('Rate quote ID saved:', jsonData.quotes[0].id);", + " }", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"origin\": \"NLRTM\",\n \"destination\": \"CNSHA\",\n \"containerType\": \"40HC\",\n \"mode\": \"FCL\",\n \"departureDate\": \"2025-02-15\",\n \"quantity\": 2,\n \"weight\": 20000,\n \"volume\": 50.5,\n \"isHazmat\": false\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/v1/rates/search", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "rates", "search"] + }, + "description": "Search for available shipping rates from multiple carriers. Results are cached for 15 minutes.\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Search CSV Rates (Advanced)", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"origin\": \"NLRTM\",\n \"destination\": \"USNYC\",\n \"volumeCBM\": 25.5,\n \"weightKG\": 3500,\n \"palletCount\": 10,\n \"containerType\": \"LCL\",\n \"filters\": {\n \"companies\": [\"SSC Consolidation\", \"ECU Worldwide\"],\n \"maxPrice\": 2000,\n \"minPrice\": 500,\n \"maxTransitDays\": 30,\n \"currency\": \"USD\"\n }\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/v1/rates/search-csv", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "rates", "search-csv"] + }, + "description": "Search for rates from CSV-loaded carriers (SSC, ECU, TCC, NVO) with advanced filtering options.\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Get Available Companies", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/rates/companies", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "rates", "companies"] + }, + "description": "Returns list of all available carrier companies in the CSV rate system.\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Get Filter Options", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/rates/filters/options", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "rates", "filters", "options"] + }, + "description": "Returns available options for all filters (companies, container types, currencies).\n\n**Requires authentication**" + }, + "response": [] + } + ], + "description": "Rate search endpoints for finding shipping quotes from multiple carriers." + }, + { + "name": "Bookings", + "item": [ + { + "name": "Create Booking", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "if (pm.response.code === 201) {", + " const jsonData = pm.response.json();", + " pm.environment.set('bookingId', jsonData.id);", + " pm.environment.set('bookingNumber', jsonData.bookingNumber);", + " console.log('Booking created:', jsonData.bookingNumber);", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"rateQuoteId\": \"{{rateQuoteId}}\",\n \"shipper\": {\n \"name\": \"Acme Corporation\",\n \"address\": {\n \"street\": \"123 Main Street\",\n \"city\": \"Rotterdam\",\n \"postalCode\": \"3000 AB\",\n \"country\": \"NL\"\n },\n \"contactName\": \"John Doe\",\n \"contactEmail\": \"john.doe@acme.com\",\n \"contactPhone\": \"+31612345678\"\n },\n \"consignee\": {\n \"name\": \"Global Trading Ltd\",\n \"address\": {\n \"street\": \"456 Harbor Road\",\n \"city\": \"Shanghai\",\n \"postalCode\": \"200000\",\n \"country\": \"CN\"\n },\n \"contactName\": \"Jane Smith\",\n \"contactEmail\": \"jane.smith@globaltrading.com\",\n \"contactPhone\": \"+8613800000000\"\n },\n \"cargoDescription\": \"Electronics and consumer goods - fragile items\",\n \"containers\": [\n {\n \"type\": \"40HC\",\n \"vgm\": 22000,\n \"sealNumber\": \"SEAL123456\"\n }\n ],\n \"specialInstructions\": \"Please handle with care. Delivery before 5 PM.\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/v1/bookings", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "bookings"] + }, + "description": "Create a new booking based on a rate quote. The booking will be in 'draft' status initially.\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Get Booking by ID", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/bookings/{{bookingId}}", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "bookings", "{{bookingId}}"] + }, + "description": "Retrieve detailed information about a specific booking.\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Get Booking by Number", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/bookings/number/{{bookingNumber}}", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "bookings", "number", "{{bookingNumber}}"] + }, + "description": "Retrieve detailed information about a specific booking using its booking number.\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "List Bookings", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/bookings?page=1&pageSize=20&status=confirmed", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "bookings"], + "query": [ + { + "key": "page", + "value": "1", + "description": "Page number (1-based)" + }, + { + "key": "pageSize", + "value": "20", + "description": "Items per page" + }, + { + "key": "status", + "value": "confirmed", + "description": "Filter by status: draft, pending_confirmation, confirmed, in_transit, delivered, cancelled", + "disabled": true + } + ] + }, + "description": "Retrieve a paginated list of bookings for the authenticated user's organization.\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Fuzzy Search Bookings", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/bookings/search/fuzzy?q=WCM-2025&limit=20", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "bookings", "search", "fuzzy"], + "query": [ + { + "key": "q", + "value": "WCM-2025", + "description": "Search query (minimum 2 characters)" + }, + { + "key": "limit", + "value": "20", + "description": "Maximum number of results" + } + ] + }, + "description": "Search bookings using fuzzy matching. Tolerant to typos and partial matches. Searches across booking number, shipper, and consignee names.\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Advanced Search Bookings", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/bookings/advanced/search?page=1&pageSize=20&status=confirmed,in_transit&createdFrom=2025-01-01&sortBy=createdAt&sortOrder=desc", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "bookings", "advanced", "search"], + "query": [ + { + "key": "page", + "value": "1" + }, + { + "key": "pageSize", + "value": "20" + }, + { + "key": "status", + "value": "confirmed,in_transit", + "description": "Filter by status (comma-separated)" + }, + { + "key": "search", + "value": "WCM-2025", + "description": "Search term (booking number)", + "disabled": true + }, + { + "key": "shipper", + "value": "Acme", + "description": "Filter by shipper name", + "disabled": true + }, + { + "key": "consignee", + "value": "Global", + "description": "Filter by consignee name", + "disabled": true + }, + { + "key": "createdFrom", + "value": "2025-01-01", + "description": "Filter by creation date (from)" + }, + { + "key": "createdTo", + "value": "2025-12-31", + "description": "Filter by creation date (to)", + "disabled": true + }, + { + "key": "sortBy", + "value": "createdAt", + "description": "Sort field: bookingNumber, status, createdAt" + }, + { + "key": "sortOrder", + "value": "desc", + "description": "Sort order: asc, desc" + } + ] + }, + "description": "Search bookings with advanced filtering options including status, date ranges, carrier, ports, shipper/consignee.\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Export Bookings (CSV)", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"format\": \"csv\",\n \"fields\": [\n \"bookingNumber\",\n \"status\",\n \"shipper\",\n \"consignee\",\n \"origin\",\n \"destination\",\n \"carrier\",\n \"createdAt\"\n ]\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/v1/bookings/export", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "bookings", "export"] + }, + "description": "Export bookings to CSV format with optional filtering.\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Export Bookings (Excel)", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"format\": \"xlsx\",\n \"bookingIds\": [\"{{bookingId}}\"]\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/v1/bookings/export", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "bookings", "export"] + }, + "description": "Export specific bookings to Excel format.\n\n**Requires authentication**" + }, + "response": [] + } + ], + "description": "Booking management endpoints for creating, retrieving, and exporting bookings." + }, + { + "name": "Users", + "item": [ + { + "name": "Create User (Admin/Manager)", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"jane.doe@acme.com\",\n \"firstName\": \"Jane\",\n \"lastName\": \"Doe\",\n \"role\": \"user\",\n \"organizationId\": \"{{organizationId}}\",\n \"password\": \"TempPassword123!\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/v1/users", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "users"] + }, + "description": "Create a new user account. Admin can create in any org, manager only in their own.\n\n**Requires authentication (Admin/Manager role)**" + }, + "response": [] + }, + { + "name": "Get User by ID", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/users/{{userId}}", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "users", "{{userId}}"] + }, + "description": "Retrieve user details. Users can view users in their org, admins can view any.\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Update User", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"firstName\": \"Jane\",\n \"lastName\": \"Doe-Smith\",\n \"role\": \"manager\",\n \"isActive\": true\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/v1/users/{{userId}}", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "users", "{{userId}}"] + }, + "description": "Update user details (name, role, status).\n\n**Requires authentication (Admin/Manager role)**" + }, + "response": [] + }, + { + "name": "Delete User", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/users/{{userId}}", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "users", "{{userId}}"] + }, + "description": "Deactivate a user account.\n\n**Requires authentication (Admin role)**" + }, + "response": [] + }, + { + "name": "List Users", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/users?page=1&pageSize=20&role=user", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "users"], + "query": [ + { + "key": "page", + "value": "1" + }, + { + "key": "pageSize", + "value": "20" + }, + { + "key": "role", + "value": "user", + "description": "Filter by role: admin, manager, user, viewer", + "disabled": true + } + ] + }, + "description": "Retrieve a paginated list of users in your organization. Admins can see all users.\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Update Own Password", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"currentPassword\": \"SecurePassword123!\",\n \"newPassword\": \"NewSecurePassword456!\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/v1/users/me/password", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "users", "me", "password"] + }, + "description": "Update your own password. Requires current password.\n\n**Requires authentication**" + }, + "response": [] + } + ], + "description": "User management endpoints for creating, updating, and listing users." + }, + { + "name": "Organizations", + "item": [ + { + "name": "Create Organization (Admin)", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"Acme Freight Forwarding\",\n \"type\": \"FREIGHT_FORWARDER\",\n \"address\": {\n \"street\": \"123 Main Street\",\n \"city\": \"Rotterdam\",\n \"state\": \"South Holland\",\n \"postalCode\": \"3000 AB\",\n \"country\": \"NL\"\n },\n \"logoUrl\": \"https://example.com/logo.png\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/v1/organizations", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "organizations"] + }, + "description": "Create a new organization (freight forwarder, carrier, or shipper).\n\n**Requires authentication (Admin role)**" + }, + "response": [] + }, + { + "name": "Get Organization by ID", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/organizations/{{organizationId}}", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "organizations", "{{organizationId}}"] + }, + "description": "Retrieve organization details. Users can view their own organization, admins can view any.\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Update Organization", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"Acme Freight Forwarding Inc.\",\n \"address\": {\n \"street\": \"456 New Street\",\n \"city\": \"Rotterdam\",\n \"postalCode\": \"3000 AB\",\n \"country\": \"NL\"\n },\n \"isActive\": true\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/v1/organizations/{{organizationId}}", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "organizations", "{{organizationId}}"] + }, + "description": "Update organization details (name, address, logo, status).\n\n**Requires authentication (Admin/Manager role)**" + }, + "response": [] + }, + { + "name": "List Organizations", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/organizations?page=1&pageSize=20&type=FREIGHT_FORWARDER", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "organizations"], + "query": [ + { + "key": "page", + "value": "1" + }, + { + "key": "pageSize", + "value": "20" + }, + { + "key": "type", + "value": "FREIGHT_FORWARDER", + "description": "Filter by type: FREIGHT_FORWARDER, CARRIER, SHIPPER", + "disabled": true + } + ] + }, + "description": "Retrieve a paginated list of organizations. Admins see all, others see only their own.\n\n**Requires authentication**" + }, + "response": [] + } + ], + "description": "Organization management endpoints for creating, updating, and listing organizations." + }, + { + "name": "Notifications", + "item": [ + { + "name": "Get Notifications", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/notifications?page=1&limit=20&read=false", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "notifications"], + "query": [ + { + "key": "page", + "value": "1" + }, + { + "key": "limit", + "value": "20" + }, + { + "key": "read", + "value": "false", + "description": "Filter by read status", + "disabled": true + } + ] + }, + "description": "Get user notifications with pagination and filtering.\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Get Unread Notifications", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/notifications/unread?limit=50", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "notifications", "unread"], + "query": [ + { + "key": "limit", + "value": "50" + } + ] + }, + "description": "Get unread notifications.\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Get Unread Count", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/notifications/unread/count", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "notifications", "unread", "count"] + }, + "description": "Get unread notifications count.\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Mark Notification as Read", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "PATCH", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/notifications/:id/read", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "notifications", ":id", "read"], + "variable": [ + { + "key": "id", + "value": "notification-id" + } + ] + }, + "description": "Mark notification as read.\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Mark All as Read", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/notifications/read-all", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "notifications", "read-all"] + }, + "description": "Mark all notifications as read.\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Delete Notification", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/notifications/:id", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "notifications", ":id"], + "variable": [ + { + "key": "id", + "value": "notification-id" + } + ] + }, + "description": "Delete notification.\n\n**Requires authentication**" + }, + "response": [] + } + ], + "description": "Notification management endpoints for managing user notifications." + }, + { + "name": "Audit Logs", + "item": [ + { + "name": "Get Audit Logs", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/audit-logs?page=1&limit=50&action=BOOKING_CREATED&status=success", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "audit-logs"], + "query": [ + { + "key": "page", + "value": "1" + }, + { + "key": "limit", + "value": "50" + }, + { + "key": "userId", + "value": "user-id", + "description": "Filter by user ID", + "disabled": true + }, + { + "key": "action", + "value": "BOOKING_CREATED", + "description": "Filter by action (comma-separated)", + "disabled": true + }, + { + "key": "status", + "value": "success", + "description": "Filter by status (comma-separated)", + "disabled": true + }, + { + "key": "resourceType", + "value": "booking", + "description": "Filter by resource type", + "disabled": true + }, + { + "key": "startDate", + "value": "2025-01-01T00:00:00Z", + "description": "Filter by start date (ISO 8601)", + "disabled": true + }, + { + "key": "endDate", + "value": "2025-12-31T23:59:59Z", + "description": "Filter by end date (ISO 8601)", + "disabled": true + } + ] + }, + "description": "Get audit logs with filters.\n\n**Requires authentication (Admin/Manager role)**" + }, + "response": [] + }, + { + "name": "Get Resource Audit Trail", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/audit-logs/resource/:type/:id", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "audit-logs", "resource", ":type", ":id"], + "variable": [ + { + "key": "type", + "value": "booking" + }, + { + "key": "id", + "value": "{{bookingId}}" + } + ] + }, + "description": "Get audit trail for a specific resource.\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Get Organization Activity", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/audit-logs/organization/activity?limit=50", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "audit-logs", "organization", "activity"], + "query": [ + { + "key": "limit", + "value": "50" + } + ] + }, + "description": "Get recent organization activity.\n\n**Requires authentication (Admin/Manager role)**" + }, + "response": [] + }, + { + "name": "Get User Activity", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/audit-logs/user/:userId/activity?limit=50", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "audit-logs", "user", ":userId", "activity"], + "query": [ + { + "key": "limit", + "value": "50" + } + ], + "variable": [ + { + "key": "userId", + "value": "{{userId}}" + } + ] + }, + "description": "Get user activity history.\n\n**Requires authentication (Admin/Manager role)**" + }, + "response": [] + } + ], + "description": "Audit log endpoints for compliance and security monitoring." + }, + { + "name": "GDPR", + "item": [ + { + "name": "Export User Data (JSON)", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/gdpr/export", + "host": ["{{baseUrl}}"], + "path": ["gdpr", "export"] + }, + "description": "Export all personal data in JSON format (GDPR Article 20 - Right to Data Portability).\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Export User Data (CSV)", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/gdpr/export/csv", + "host": ["{{baseUrl}}"], + "path": ["gdpr", "export", "csv"] + }, + "description": "Export personal data in CSV format for easy viewing.\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Delete Account", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"confirmEmail\": \"{{userEmail}}\",\n \"reason\": \"No longer using the service\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/gdpr/delete-account", + "host": ["{{baseUrl}}"], + "path": ["gdpr", "delete-account"] + }, + "description": "Permanently delete or anonymize user data (GDPR Article 17 - Right to Erasure).\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Record Consent", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"consentType\": \"marketing\",\n \"granted\": true\n}" + }, + "url": { + "raw": "{{baseUrl}}/gdpr/consent", + "host": ["{{baseUrl}}"], + "path": ["gdpr", "consent"] + }, + "description": "Record consent for marketing, analytics, etc. (GDPR Article 7).\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Withdraw Consent", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"consentType\": \"marketing\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/gdpr/consent/withdraw", + "host": ["{{baseUrl}}"], + "path": ["gdpr", "consent", "withdraw"] + }, + "description": "Withdraw consent for marketing or analytics (GDPR Article 7.3).\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Get Consent Status", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/gdpr/consent", + "host": ["{{baseUrl}}"], + "path": ["gdpr", "consent"] + }, + "description": "Retrieve current consent preferences.\n\n**Requires authentication**" + }, + "response": [] + } + ], + "description": "GDPR compliance endpoints for data portability, erasure, and consent management." + }, + { + "name": "Webhooks", + "item": [ + { + "name": "Create Webhook", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"url\": \"https://your-domain.com/webhooks/xpeditis\",\n \"events\": [\"booking.created\", \"booking.confirmed\", \"shipment.departed\"],\n \"description\": \"Production webhook for booking events\",\n \"headers\": {\n \"X-API-Key\": \"your-api-key\"\n }\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/v1/webhooks", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "webhooks"] + }, + "description": "Create a new webhook.\n\n**Requires authentication (Admin/Manager role)**" + }, + "response": [] + }, + { + "name": "Get All Webhooks", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/webhooks", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "webhooks"] + }, + "description": "Get all webhooks for organization.\n\n**Requires authentication (Admin/Manager role)**" + }, + "response": [] + }, + { + "name": "Get Webhook by ID", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/webhooks/:id", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "webhooks", ":id"], + "variable": [ + { + "key": "id", + "value": "webhook-id" + } + ] + }, + "description": "Get webhook by ID.\n\n**Requires authentication (Admin/Manager role)**" + }, + "response": [] + }, + { + "name": "Update Webhook", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"url\": \"https://your-domain.com/webhooks/xpeditis-v2\",\n \"events\": [\"booking.created\", \"booking.confirmed\"],\n \"description\": \"Updated webhook\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/v1/webhooks/:id", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "webhooks", ":id"], + "variable": [ + { + "key": "id", + "value": "webhook-id" + } + ] + }, + "description": "Update webhook.\n\n**Requires authentication (Admin/Manager role)**" + }, + "response": [] + }, + { + "name": "Activate Webhook", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/webhooks/:id/activate", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "webhooks", ":id", "activate"], + "variable": [ + { + "key": "id", + "value": "webhook-id" + } + ] + }, + "description": "Activate webhook.\n\n**Requires authentication (Admin/Manager role)**" + }, + "response": [] + }, + { + "name": "Deactivate Webhook", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/webhooks/:id/deactivate", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "webhooks", ":id", "deactivate"], + "variable": [ + { + "key": "id", + "value": "webhook-id" + } + ] + }, + "description": "Deactivate webhook.\n\n**Requires authentication (Admin/Manager role)**" + }, + "response": [] + }, + { + "name": "Delete Webhook", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/webhooks/:id", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "webhooks", ":id"], + "variable": [ + { + "key": "id", + "value": "webhook-id" + } + ] + }, + "description": "Delete webhook.\n\n**Requires authentication (Admin/Manager role)**" + }, + "response": [] + } + ], + "description": "Webhook management endpoints for creating and managing event webhooks." + }, + { + "name": "Dashboard", + "item": [ + { + "name": "Get Dashboard KPIs", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/dashboard/kpis", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "dashboard", "kpis"] + }, + "description": "Get dashboard KPIs (total bookings, revenue, etc.).\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Get Bookings Chart Data", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/dashboard/bookings-chart", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "dashboard", "bookings-chart"] + }, + "description": "Get bookings chart data (last 6 months).\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Get Top Trade Lanes", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/dashboard/top-trade-lanes", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "dashboard", "top-trade-lanes"] + }, + "description": "Get top 5 trade lanes by volume.\n\n**Requires authentication**" + }, + "response": [] + }, + { + "name": "Get Dashboard Alerts", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/dashboard/alerts", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "dashboard", "alerts"] + }, + "description": "Get dashboard alerts (delayed shipments, pending confirmations, etc.).\n\n**Requires authentication**" + }, + "response": [] + } + ], + "description": "Dashboard analytics endpoints for KPIs, charts, and alerts." + }, + { + "name": "Admin - CSV Rates", + "item": [ + { + "name": "Upload CSV Rate File", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "companyName", + "value": "SSC Consolidation", + "type": "text" + }, + { + "key": "file", + "type": "file", + "src": "/path/to/rates.csv" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/api/v1/admin/csv-rates/upload", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "admin", "csv-rates", "upload"] + }, + "description": "Upload a CSV file containing shipping rates for a carrier company. File must be valid CSV format with required columns. Maximum file size: 10MB.\n\n**Requires authentication (ADMIN role)**" + }, + "response": [] + }, + { + "name": "Get All CSV Configurations", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/admin/csv-rates/config", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "admin", "csv-rates", "config"] + }, + "description": "Get all CSV rate configurations.\n\n**Requires authentication (ADMIN role)**" + }, + "response": [] + }, + { + "name": "Get CSV Config by Company", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/admin/csv-rates/config/:companyName", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "admin", "csv-rates", "config", ":companyName"], + "variable": [ + { + "key": "companyName", + "value": "SSC Consolidation" + } + ] + }, + "description": "Get CSV configuration for specific company.\n\n**Requires authentication (ADMIN role)**" + }, + "response": [] + }, + { + "name": "Validate CSV File", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/admin/csv-rates/validate/:companyName", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "admin", "csv-rates", "validate", ":companyName"], + "variable": [ + { + "key": "companyName", + "value": "SSC Consolidation" + } + ] + }, + "description": "Validate CSV file structure and data for a specific company.\n\n**Requires authentication (ADMIN role)**" + }, + "response": [] + }, + { + "name": "Delete CSV Configuration", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v1/admin/csv-rates/config/:companyName", + "host": ["{{baseUrl}}"], + "path": ["api", "v1", "admin", "csv-rates", "config", ":companyName"], + "variable": [ + { + "key": "companyName", + "value": "SSC Consolidation" + } + ] + }, + "description": "Delete CSV rate configuration for a company.\n\n**Requires authentication (ADMIN role)**" + }, + "response": [] + } + ], + "description": "Admin-only endpoints for managing CSV rate files and configurations." + }, + { + "name": "Health", + "item": [ + { + "name": "Health Check", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/health", + "host": ["{{baseUrl}}"], + "path": ["health"] + }, + "description": "Health check endpoint. Returns service status and uptime.\n\n**No authentication required**" + }, + "response": [] + }, + { + "name": "Readiness Check", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/health/ready", + "host": ["{{baseUrl}}"], + "path": ["health", "ready"] + }, + "description": "Readiness check endpoint. Verifies database and Redis connectivity.\n\n**No authentication required**" + }, + "response": [] + }, + { + "name": "Liveness Check", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/health/live", + "host": ["{{baseUrl}}"], + "path": ["health", "live"] + }, + "description": "Liveness check endpoint. Returns alive status.\n\n**No authentication required**" + }, + "response": [] + } + ], + "description": "Health check endpoints for monitoring service status." + } + ] +} diff --git a/postman/Xpeditis_Local.postman_environment.json b/postman/Xpeditis_Local.postman_environment.json new file mode 100644 index 0000000..1699593 --- /dev/null +++ b/postman/Xpeditis_Local.postman_environment.json @@ -0,0 +1,63 @@ +{ + "id": "xpeditis-local-env", + "name": "Xpeditis Local Environment", + "values": [ + { + "key": "baseUrl", + "value": "http://localhost:4000", + "type": "default", + "enabled": true + }, + { + "key": "token", + "value": "", + "type": "secret", + "enabled": true + }, + { + "key": "refreshToken", + "value": "", + "type": "secret", + "enabled": true + }, + { + "key": "userId", + "value": "", + "type": "default", + "enabled": true + }, + { + "key": "userEmail", + "value": "", + "type": "default", + "enabled": true + }, + { + "key": "organizationId", + "value": "", + "type": "default", + "enabled": true + }, + { + "key": "rateQuoteId", + "value": "", + "type": "default", + "enabled": true + }, + { + "key": "bookingId", + "value": "", + "type": "default", + "enabled": true + }, + { + "key": "bookingNumber", + "value": "", + "type": "default", + "enabled": true + } + ], + "_postman_variable_scope": "environment", + "_postman_exported_at": "2025-10-27T00:00:00.000Z", + "_postman_exported_using": "Postman/10.0.0" +}