diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 0f669fa..cf487c5 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -24,6 +24,7 @@ setServerLoggerFactory((name) => { info: (message, ...args) => winstonLogger.info(message, ...args), warning: (message, ...args) => winstonLogger.warning(message, ...args), error: (message, ...args) => winstonLogger.error(message, ...args), + crit: (message, ...args) => winstonLogger.crit(message, ...args), }; }); diff --git a/src/lib/services/user/checkPerm.ts b/src/lib/services/user/checkPerm.ts index 5bd62b2..8ed5d7e 100644 --- a/src/lib/services/user/checkPerm.ts +++ b/src/lib/services/user/checkPerm.ts @@ -1,5 +1,12 @@ import type { Bounds } from "@/lib/mapObjects/mapBounds"; -import { bbox, feature as makeFeature, featureCollection, intersect, polygon } from "@turf/turf"; +import { + bbox, + feature as makeFeature, + featureCollection, + intersect, + polygon, + union +} from "@turf/turf"; import type { Feature, Polygon } from "geojson"; import { Features, type FeaturesKey, type Perms } from "@/lib/utils/features"; import { getLogger } from "@/lib/utils/logger"; @@ -23,42 +30,70 @@ export function hasFeatureAnywhere(perms: Perms, feature: FeaturesKey) { return false; } -export function checkFeatureInBounds(perms: Perms, feature: FeaturesKey, bounds: Bounds): Bounds { +export function checkFeatureInBounds( + perms: Perms, + feature: FeaturesKey, + bounds: Bounds +): Bounds | null { if (isFeatureInFeatureList(perms.everywhere, feature)) return bounds; const start = performance.now(); - const allPolygons: Feature[] = [ - polygon([ - [ - [bounds.minLon, bounds.minLat], - [bounds.minLon, bounds.maxLat], - [bounds.maxLon, bounds.maxLat], - [bounds.maxLon, bounds.minLat], - [bounds.minLon, bounds.minLat] - ] - ]) - ]; + const viewportPolygon = polygon([ + [ + [bounds.minLon, bounds.minLat], + [bounds.minLon, bounds.maxLat], + [bounds.maxLon, bounds.maxLat], + [bounds.maxLon, bounds.minLat], + [bounds.minLon, bounds.minLat] + ] + ]); + const permittedPolygons: Feature[] = []; for (const area of perms.areas) { if (isFeatureInFeatureList(area.features, feature)) { - allPolygons.push(makeFeature(area.polygon)); + if (area.polygon) { + permittedPolygons.push(makeFeature(area.polygon)); + } } } - if (allPolygons.length === 1) { - return bounds; + // If no permitted areas have this feature (or none have polygons), deny access + if (permittedPolygons.length === 0) { + return null; } - const intersection = intersect(featureCollection(allPolygons)); + // Find intersection of viewport with each permitted area and collect results + let combinedIntersection: Feature | null = null; + for (const permittedPolygon of permittedPolygons) { + const areaIntersection = intersect( + featureCollection([viewportPolygon, permittedPolygon]) + ); + if (areaIntersection) { + if (!combinedIntersection) { + combinedIntersection = areaIntersection as Feature; + } else { + combinedIntersection = union( + featureCollection([combinedIntersection, areaIntersection as Feature]) + ) as Feature | null; + } + } + } - console.debug( - `CheckFeatureInBound for ${feature} with ${allPolygons.length} polygons took ${performance.now() - start} ms` + log.debug( + "calculated area intersections | areas: %d | feature: %s | any match permissions: %s | took: %fms", + permittedPolygons.length, + feature, + Boolean(combinedIntersection), + (performance.now() - start).toFixed(1) ); - if (!intersection) return bounds; + // If no intersection with any permitted area, deny access + if (!combinedIntersection) { + return null; + } - const result = bbox(intersection); + const result = bbox(combinedIntersection); return { minLon: result[0], diff --git a/src/lib/utils/logger.ts b/src/lib/utils/logger.ts index b8a62e5..43e69a9 100644 --- a/src/lib/utils/logger.ts +++ b/src/lib/utils/logger.ts @@ -7,12 +7,9 @@ export interface Logger { info: LogFn; warning: LogFn; error: LogFn; + crit: LogFn; } -export type DebugCategories = { - permissions?: boolean; -}; - let serverLoggerFactory: ((name: string) => Logger) | null = null; export function setServerLoggerFactory(factory: (name: string) => Logger,) { @@ -26,6 +23,7 @@ function createBrowserLogger(name: string): Logger { info: (message, ...args) => console.info(prefix, message, ...args), warning: (message, ...args) => console.warn(prefix, message, ...args), error: (message, ...args) => console.error(prefix, message, ...args), + crit: (message, ...args) => console.error(prefix, message, ...args), }; } diff --git a/src/routes/api/[queryMapObject=mapObject]/+server.ts b/src/routes/api/[queryMapObject=mapObject]/+server.ts index 41a4dc6..4002385 100644 --- a/src/routes/api/[queryMapObject=mapObject]/+server.ts +++ b/src/routes/api/[queryMapObject=mapObject]/+server.ts @@ -18,6 +18,10 @@ export async function POST({ request, locals, params }) { const type = params.queryMapObject as MapObjectType; const bounds = checkFeatureInBounds(locals.perms, params.queryMapObject, data); + if (!bounds) { + return json({ data: [] }); + } + const queried = await queryMapObjects(type, bounds, data.filter); log.info(