mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-19 13:21:46 +00:00
refactor(places): merge KML/KMZ routes into single POST /import/map endpoint
This commit is contained in:
@@ -105,13 +105,9 @@ export const placesApi = {
|
|||||||
const fd = new FormData(); fd.append('file', file)
|
const fd = new FormData(); fd.append('file', file)
|
||||||
return apiClient.post(`/trips/${tripId}/places/import/gpx`, fd, { headers: { 'Content-Type': 'multipart/form-data' } }).then(r => r.data)
|
return apiClient.post(`/trips/${tripId}/places/import/gpx`, fd, { headers: { 'Content-Type': 'multipart/form-data' } }).then(r => r.data)
|
||||||
},
|
},
|
||||||
importKml: (tripId: number | string, file: File) => {
|
importMapFile: (tripId: number | string, file: File) => {
|
||||||
const fd = new FormData(); fd.append('file', file)
|
const fd = new FormData(); fd.append('file', file)
|
||||||
return apiClient.post(`/trips/${tripId}/places/import/kml`, fd, { headers: { 'Content-Type': 'multipart/form-data' } }).then(r => r.data)
|
return apiClient.post(`/trips/${tripId}/places/import/map`, fd, { headers: { 'Content-Type': 'multipart/form-data' } }).then(r => r.data)
|
||||||
},
|
|
||||||
importKmz: (tripId: number | string, file: File) => {
|
|
||||||
const fd = new FormData(); fd.append('file', file)
|
|
||||||
return apiClient.post(`/trips/${tripId}/places/import/kmz`, fd, { headers: { 'Content-Type': 'multipart/form-data' } }).then(r => r.data)
|
|
||||||
},
|
},
|
||||||
importGoogleList: (tripId: number | string, url: string) =>
|
importGoogleList: (tripId: number | string, url: string) =>
|
||||||
apiClient.post(`/trips/${tripId}/places/import/google-list`, { url }).then(r => r.data),
|
apiClient.post(`/trips/${tripId}/places/import/google-list`, { url }).then(r => r.data),
|
||||||
|
|||||||
@@ -111,9 +111,7 @@ const PlacesSidebar = React.memo(function PlacesSidebar({
|
|||||||
setKmlKmzSummary(null)
|
setKmlKmzSummary(null)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = ext === 'kmz'
|
const result = await placesApi.importMapFile(tripId, kmlKmzFile)
|
||||||
? await placesApi.importKmz(tripId, kmlKmzFile)
|
|
||||||
: await placesApi.importKml(tripId, kmlKmzFile)
|
|
||||||
|
|
||||||
await loadTrip(tripId)
|
await loadTrip(tripId)
|
||||||
setKmlKmzSummary(result.summary || null)
|
setKmlKmzSummary(result.summary || null)
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ import {
|
|||||||
updatePlace,
|
updatePlace,
|
||||||
deletePlace,
|
deletePlace,
|
||||||
importGpx,
|
importGpx,
|
||||||
importKmlPlaces,
|
importMapFile,
|
||||||
importKmzPlaces,
|
|
||||||
importGoogleList,
|
importGoogleList,
|
||||||
searchPlaceImage,
|
searchPlaceImage,
|
||||||
} from '../services/placeService';
|
} from '../services/placeService';
|
||||||
@@ -74,7 +73,7 @@ router.post('/import/gpx', authenticate, requireTripAccess, uploadMulter.single(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/import/kml', authenticate, requireTripAccess, uploadMulter.single('file'), (req: Request, res: Response) => {
|
router.post('/import/map', authenticate, requireTripAccess, uploadMulter.single('file'), async (req: Request, res: Response) => {
|
||||||
const authReq = req as AuthRequest;
|
const authReq = req as AuthRequest;
|
||||||
if (!checkPermission('place_edit', authReq.user.role, authReq.trip!.user_id, authReq.user.id, authReq.trip!.user_id !== authReq.user.id)) {
|
if (!checkPermission('place_edit', authReq.user.role, authReq.trip!.user_id, authReq.user.id, authReq.trip!.user_id !== authReq.user.id)) {
|
||||||
return res.status(403).json({ error: 'No permission' });
|
return res.status(403).json({ error: 'No permission' });
|
||||||
@@ -85,9 +84,9 @@ router.post('/import/kml', authenticate, requireTripAccess, uploadMulter.single(
|
|||||||
if (!file) return res.status(400).json({ error: 'No file uploaded' });
|
if (!file) return res.status(400).json({ error: 'No file uploaded' });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = importKmlPlaces(tripId, file.buffer);
|
const result = await importMapFile(tripId, file.buffer, file.originalname);
|
||||||
if (result.count === 0) {
|
if (result.count === 0) {
|
||||||
return res.status(400).json({ error: 'No valid Placemarks found in KML file', summary: result.summary });
|
return res.status(400).json({ error: 'No valid Placemarks found in map file', summary: result.summary });
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(201).json(result);
|
res.status(201).json(result);
|
||||||
@@ -95,33 +94,7 @@ router.post('/import/kml', authenticate, requireTripAccess, uploadMulter.single(
|
|||||||
broadcast(tripId, 'place:created', { place }, req.headers['x-socket-id'] as string);
|
broadcast(tripId, 'place:created', { place }, req.headers['x-socket-id'] as string);
|
||||||
}
|
}
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
const message = err instanceof Error ? err.message : 'Failed to import KML file';
|
const message = err instanceof Error ? err.message : 'Failed to import map file';
|
||||||
res.status(400).json({ error: message });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post('/import/kmz', authenticate, requireTripAccess, uploadMulter.single('file'), async (req: Request, res: Response) => {
|
|
||||||
const authReq = req as AuthRequest;
|
|
||||||
if (!checkPermission('place_edit', authReq.user.role, authReq.trip!.user_id, authReq.user.id, authReq.trip!.user_id !== authReq.user.id)) {
|
|
||||||
return res.status(403).json({ error: 'No permission' });
|
|
||||||
}
|
|
||||||
|
|
||||||
const { tripId } = req.params;
|
|
||||||
const file = (req as any).file;
|
|
||||||
if (!file) return res.status(400).json({ error: 'No file uploaded' });
|
|
||||||
|
|
||||||
try {
|
|
||||||
const result = await importKmzPlaces(tripId, file.buffer);
|
|
||||||
if (result.count === 0) {
|
|
||||||
return res.status(400).json({ error: 'No valid Placemarks found in KMZ file', summary: result.summary });
|
|
||||||
}
|
|
||||||
|
|
||||||
res.status(201).json(result);
|
|
||||||
for (const place of result.places) {
|
|
||||||
broadcast(tripId, 'place:created', { place }, req.headers['x-socket-id'] as string);
|
|
||||||
}
|
|
||||||
} catch (err: unknown) {
|
|
||||||
const message = err instanceof Error ? err.message : 'Failed to import KMZ file';
|
|
||||||
res.status(400).json({ error: message });
|
res.status(400).json({ error: message });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -411,6 +411,13 @@ export async function importKmzPlaces(tripId: string, kmzBuffer: Buffer): Promis
|
|||||||
return importKmlPlaces(tripId, kmlBuffer);
|
return importKmlPlaces(tripId, kmlBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function importMapFile(tripId: string, fileBuffer: Buffer, filename: string): Promise<PlaceImportResult> {
|
||||||
|
const ext = filename.toLowerCase().split('.').pop();
|
||||||
|
if (ext === 'kmz') return importKmzPlaces(tripId, fileBuffer);
|
||||||
|
if (ext === 'kml') return importKmlPlaces(tripId, fileBuffer);
|
||||||
|
throw new Error(`Unsupported map file format: .${ext}. Please upload a .kml or .kmz file.`);
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Import Google Maps list
|
// Import Google Maps list
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -546,7 +546,7 @@ describe('KML/KMZ Import', () => {
|
|||||||
.run('Museums', '#3b82f6', 'Landmark', user.id);
|
.run('Museums', '#3b82f6', 'Landmark', user.id);
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post(`/api/trips/${trip.id}/places/import/kml`)
|
.post(`/api/trips/${trip.id}/places/import/map`)
|
||||||
.set('Cookie', authCookie(user.id))
|
.set('Cookie', authCookie(user.id))
|
||||||
.attach('file', KML_FIXTURE);
|
.attach('file', KML_FIXTURE);
|
||||||
|
|
||||||
@@ -572,7 +572,7 @@ describe('KML/KMZ Import', () => {
|
|||||||
.run('Parks', '#22c55e', 'Trees', user.id);
|
.run('Parks', '#22c55e', 'Trees', user.id);
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post(`/api/trips/${trip.id}/places/import/kml`)
|
.post(`/api/trips/${trip.id}/places/import/map`)
|
||||||
.set('Cookie', authCookie(user.id))
|
.set('Cookie', authCookie(user.id))
|
||||||
.attach('file', KML_NESTED_FIXTURE);
|
.attach('file', KML_NESTED_FIXTURE);
|
||||||
|
|
||||||
@@ -596,7 +596,7 @@ describe('KML/KMZ Import', () => {
|
|||||||
const trip = createTrip(testDb, user.id);
|
const trip = createTrip(testDb, user.id);
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post(`/api/trips/${trip.id}/places/import/kml`)
|
.post(`/api/trips/${trip.id}/places/import/map`)
|
||||||
.set('Cookie', authCookie(user.id))
|
.set('Cookie', authCookie(user.id))
|
||||||
.attach('file', KML_MALFORMED_FIXTURE);
|
.attach('file', KML_MALFORMED_FIXTURE);
|
||||||
|
|
||||||
@@ -614,7 +614,7 @@ describe('KML/KMZ Import', () => {
|
|||||||
const nonUtf8Kml = Buffer.concat([prefix, invalidByte, suffix]);
|
const nonUtf8Kml = Buffer.concat([prefix, invalidByte, suffix]);
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post(`/api/trips/${trip.id}/places/import/kml`)
|
.post(`/api/trips/${trip.id}/places/import/map`)
|
||||||
.set('Cookie', authCookie(user.id))
|
.set('Cookie', authCookie(user.id))
|
||||||
.attach('file', nonUtf8Kml, 'non-utf8.kml');
|
.attach('file', nonUtf8Kml, 'non-utf8.kml');
|
||||||
|
|
||||||
@@ -629,7 +629,7 @@ describe('KML/KMZ Import', () => {
|
|||||||
const trip = createTrip(testDb, user.id);
|
const trip = createTrip(testDb, user.id);
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post(`/api/trips/${trip.id}/places/import/kmz`)
|
.post(`/api/trips/${trip.id}/places/import/map`)
|
||||||
.set('Cookie', authCookie(user.id))
|
.set('Cookie', authCookie(user.id))
|
||||||
.attach('file', KMZ_FIXTURE);
|
.attach('file', KMZ_FIXTURE);
|
||||||
|
|
||||||
@@ -643,7 +643,7 @@ describe('KML/KMZ Import', () => {
|
|||||||
const trip = createTrip(testDb, user.id);
|
const trip = createTrip(testDb, user.id);
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post(`/api/trips/${trip.id}/places/import/kmz`)
|
.post(`/api/trips/${trip.id}/places/import/map`)
|
||||||
.set('Cookie', authCookie(user.id))
|
.set('Cookie', authCookie(user.id))
|
||||||
.attach('file', Buffer.from('not-a-zip-archive'), 'invalid.kmz');
|
.attach('file', Buffer.from('not-a-zip-archive'), 'invalid.kmz');
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user