changemaker.lite/api/dist/modules/map/locations/nar-import.routes.js

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