feat(gps): фильтры heartbeat-эхо/valid=false/spike; доступ к GPS и Финансам — матрица на /users (пользователи+роли)
This commit is contained in:
@@ -43,6 +43,8 @@ interface NormalizedPoint {
|
||||
course: number | null;
|
||||
accuracy: number | null;
|
||||
recordedAt: Date | null;
|
||||
/** false только у нативного формата Traccar (position.valid === false) */
|
||||
valid: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,6 +71,7 @@ function normalizePoint(raw: any): NormalizedPoint | null {
|
||||
course: parseNum(p.course),
|
||||
accuracy: parseNum(p.accuracy),
|
||||
recordedAt: parseDateParam(p.fixTime ?? p.deviceTime),
|
||||
valid: p.valid !== false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -84,6 +87,7 @@ function normalizePoint(raw: any): NormalizedPoint | null {
|
||||
course: parseNum(raw.course),
|
||||
accuracy: parseNum(raw.accuracy),
|
||||
recordedAt: parseDateParam(raw.timestamp),
|
||||
valid: true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -118,6 +122,14 @@ async function handleIngest(req: AuthenticatedRequest, res: Response) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Traccar пометил позицию как невалидную (valid=false) — не сохраняем,
|
||||
// но считаем признаком жизни устройства
|
||||
if (!point.valid) {
|
||||
await gpsStorage.touchAssetSeen(asset.id);
|
||||
processed++;
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
await processPosition(asset.id, asset.organizationId, {
|
||||
lat: point.lat,
|
||||
@@ -182,7 +194,7 @@ export function registerGpsRoutes(app: Express) {
|
||||
}
|
||||
|
||||
const settings = await gpsStorage.getSettings(orgId);
|
||||
const allowed = await hasGpsAccess(req.user!, settings);
|
||||
const allowed = await hasGpsAccess(req.user!);
|
||||
const defaultCenter =
|
||||
settings.defaultLat !== null && settings.defaultLng !== null && settings.defaultZoom !== null
|
||||
? { lat: settings.defaultLat, lng: settings.defaultLng, zoom: settings.defaultZoom }
|
||||
@@ -298,15 +310,13 @@ export function registerGpsRoutes(app: Express) {
|
||||
return badRequest(res, "tripStopMinutes: целое число от 0 до 120 (0 = не делить на поездки)");
|
||||
}
|
||||
|
||||
// Поля центра карты и доступа — только для admin (у остальных молча игнорируются)
|
||||
// Центр карты по умолчанию — только для admin (у остальных молча игнорируется)
|
||||
let defaultLat = current.defaultLat;
|
||||
let defaultLng = current.defaultLng;
|
||||
let defaultZoom = current.defaultZoom;
|
||||
let allowedUserIds = current.allowedUserIds;
|
||||
let allowedRoleIds = current.allowedRoleIds;
|
||||
|
||||
if (isAdmin) {
|
||||
// Центр по умолчанию: либо все три поля заданы, либо все null (очистка)
|
||||
// Либо все три поля заданы, либо все null (очистка)
|
||||
const hasCenterField =
|
||||
req.body?.defaultLat !== undefined || req.body?.defaultLng !== undefined || req.body?.defaultZoom !== undefined;
|
||||
if (hasCenterField) {
|
||||
@@ -331,26 +341,6 @@ export function registerGpsRoutes(app: Express) {
|
||||
defaultZoom = zoom;
|
||||
}
|
||||
}
|
||||
|
||||
// Списки доступа: массивы целых чисел или null (очистка)
|
||||
if (req.body?.allowedUserIds !== undefined) {
|
||||
if (req.body.allowedUserIds === null) {
|
||||
allowedUserIds = null;
|
||||
} else if (!isIntArray(req.body.allowedUserIds)) {
|
||||
return badRequest(res, "allowedUserIds: массив целых чисел или null");
|
||||
} else {
|
||||
allowedUserIds = req.body.allowedUserIds;
|
||||
}
|
||||
}
|
||||
if (req.body?.allowedRoleIds !== undefined) {
|
||||
if (req.body.allowedRoleIds === null) {
|
||||
allowedRoleIds = null;
|
||||
} else if (!isIntArray(req.body.allowedRoleIds)) {
|
||||
return badRequest(res, "allowedRoleIds: массив целых чисел или null");
|
||||
} else {
|
||||
allowedRoleIds = req.body.allowedRoleIds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const row = await gpsStorage.upsertSettings(req.user!.organizationId, {
|
||||
@@ -360,8 +350,6 @@ export function registerGpsRoutes(app: Express) {
|
||||
defaultLat,
|
||||
defaultLng,
|
||||
defaultZoom,
|
||||
allowedUserIds,
|
||||
allowedRoleIds,
|
||||
});
|
||||
res.json({
|
||||
success: true,
|
||||
@@ -371,8 +359,6 @@ export function registerGpsRoutes(app: Express) {
|
||||
defaultLat: row.defaultLat,
|
||||
defaultLng: row.defaultLng,
|
||||
defaultZoom: row.defaultZoom,
|
||||
allowedUserIds: row.allowedUserIds,
|
||||
allowedRoleIds: row.allowedRoleIds,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -689,7 +675,3 @@ function isValidPolygon(polygon: unknown): polygon is Array<[number, number]> {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function isIntArray(value: unknown): value is number[] {
|
||||
return Array.isArray(value) && value.every((v) => Number.isInteger(v));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user