96 lines
3.9 KiB
JavaScript
96 lines
3.9 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.narImportRouter = void 0;
|
|
const express_1 = require("express");
|
|
const client_1 = require("@prisma/client");
|
|
const zod_1 = require("zod");
|
|
const crypto_1 = require("crypto");
|
|
const nar_import_service_1 = require("./nar-import.service");
|
|
const redis_1 = require("../../../config/redis");
|
|
const logger_1 = require("../../../utils/logger");
|
|
const auth_middleware_1 = require("../../../middleware/auth.middleware");
|
|
const rbac_middleware_1 = require("../../../middleware/rbac.middleware");
|
|
const validate_1 = require("../../../middleware/validate");
|
|
const MAP_ADMIN_ROLES = [client_1.UserRole.SUPER_ADMIN, client_1.UserRole.MAP_ADMIN];
|
|
const serverImportSchema = zod_1.z.object({
|
|
provinceCode: zod_1.z.string().min(1).max(2),
|
|
filterType: zod_1.z.enum(['none', 'city', 'postalPrefix', 'cut']).default('none'),
|
|
filterCity: zod_1.z.string().optional(),
|
|
filterPostalPrefix: zod_1.z.string().min(3).max(3).optional(),
|
|
cutId: zod_1.z.string().optional(),
|
|
residentialOnly: zod_1.z.boolean().default(true),
|
|
deduplicateRadius: zod_1.z.number().min(0).max(100).default(5),
|
|
batchSize: zod_1.z.number().int().min(100).max(5000).default(1000),
|
|
});
|
|
const narImportRouter = (0, express_1.Router)();
|
|
exports.narImportRouter = narImportRouter;
|
|
narImportRouter.use(auth_middleware_1.authenticate);
|
|
narImportRouter.use((0, rbac_middleware_1.requireRole)(...MAP_ADMIN_ROLES));
|
|
// GET /api/map/nar-import/datasets — list available NAR datasets by province
|
|
narImportRouter.get('/datasets', async (_req, res, next) => {
|
|
try {
|
|
const result = await nar_import_service_1.narImportService.listDatasets();
|
|
res.json(result);
|
|
}
|
|
catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
// GET /api/map/nar-import/status/:importId — poll import progress
|
|
narImportRouter.get('/status/:importId', async (req, res, next) => {
|
|
try {
|
|
const importId = req.params.importId;
|
|
const data = await redis_1.redis.get(`nar-import:${importId}`);
|
|
if (!data) {
|
|
res.status(404).json({ error: { message: 'Import not found or expired', code: 'NOT_FOUND' } });
|
|
return;
|
|
}
|
|
res.json(JSON.parse(data));
|
|
}
|
|
catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
// POST /api/map/nar-import — start a province import (fire-and-forget, returns importId)
|
|
narImportRouter.post('/', (0, validate_1.validate)(serverImportSchema), async (req, res, next) => {
|
|
try {
|
|
const importId = (0, crypto_1.randomUUID)();
|
|
const userId = req.user.id;
|
|
// Write initial progress so status endpoint works immediately
|
|
await (0, nar_import_service_1.writeProgress)(importId, {
|
|
status: 'loading-locations',
|
|
totalRows: 0,
|
|
locationsCreated: 0,
|
|
addressesCreated: 0,
|
|
skippedDuplicate: 0,
|
|
skippedOutOfBounds: 0,
|
|
skippedNonResidential: 0,
|
|
skippedInvalid: 0,
|
|
currentFile: '',
|
|
provinceName: '',
|
|
});
|
|
// Fire and forget — errors are written to Redis
|
|
nar_import_service_1.narImportService.importProvince(userId, req.body, importId).catch(async (err) => {
|
|
const errorMsg = err instanceof Error ? err.message : 'Unknown error';
|
|
logger_1.logger.error(`NAR import ${importId} failed: ${errorMsg}`);
|
|
await (0, nar_import_service_1.writeProgress)(importId, {
|
|
status: 'failed',
|
|
totalRows: 0,
|
|
locationsCreated: 0,
|
|
addressesCreated: 0,
|
|
skippedDuplicate: 0,
|
|
skippedOutOfBounds: 0,
|
|
skippedNonResidential: 0,
|
|
skippedInvalid: 0,
|
|
currentFile: '',
|
|
provinceName: '',
|
|
error: errorMsg,
|
|
});
|
|
});
|
|
res.json({ importId });
|
|
}
|
|
catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
//# sourceMappingURL=nar-import.routes.js.map
|