Enforce Ingress-style link-crossing prevention

A new link can no longer cross any existing link from either team —
segmentsIntersect() does a standard orientation-based segment test,
and routes/links.ts excludes shared-endpoint pairs (fanning multiple
links out of the same portal is fine, that's not a crossing). 6 new
tests; 37 total passing.
This commit is contained in:
2026-08-05 13:33:06 +00:00
parent d6ef514c84
commit f0d437b60f
5 changed files with 151 additions and 3 deletions
+22 -2
View File
@@ -1,7 +1,7 @@
import type { FastifyInstance } from 'fastify';
import { z } from 'zod';
import { haversineMeters } from '../services/geo.js';
import type { Claim, Place, User } from '../types.js';
import { haversineMeters, segmentsIntersect } from '../services/geo.js';
import type { Claim, LinkRow, Place, User } from '../types.js';
const linkBody = z.object({
fromPlaceId: z.number().int(),
@@ -23,6 +23,7 @@ export default async function linksRoutes(app: FastifyInstance) {
const getExistingLink = db.prepare(`
SELECT * FROM links WHERE (from_place_id = ? AND to_place_id = ?) OR (from_place_id = ? AND to_place_id = ?)
`);
const getAllLinks = db.prepare('SELECT * FROM links');
const insertLink = db.prepare(`
INSERT INTO links (from_place_id, to_place_id, team, created_by_pubkey, created_at)
VALUES (?, ?, ?, ?, ?)
@@ -62,6 +63,25 @@ export default async function linksRoutes(app: FastifyInstance) {
return reply.code(409).send({ error: 'these places are already linked' });
}
// Links are physical XM beams in the sky in Ingress — a new link can't cross
// any existing link, from either team. Links sharing an endpoint with the new
// one aren't "crossing" (that's just a portal with multiple links fanning out).
for (const existing of getAllLinks.all() as LinkRow[]) {
if (
existing.from_place_id === fromPlaceId ||
existing.from_place_id === toPlaceId ||
existing.to_place_id === fromPlaceId ||
existing.to_place_id === toPlaceId
) {
continue;
}
const existingFrom = getPlace.get(existing.from_place_id) as Place;
const existingTo = getPlace.get(existing.to_place_id) as Place;
if (segmentsIntersect(fromPlace, toPlace, existingFrom, existingTo)) {
return reply.code(409).send({ error: `would cross an existing link (id ${existing.id})` });
}
}
const result = insertLink.run(fromPlaceId, toPlaceId, user.team, req.userPubkey, nowSecs());
return { id: result.lastInsertRowid, fromPlaceId, toPlaceId, team: user.team };
});