Skip to content

Commit e43f7bb

Browse files
Adding a "Layer API" that can be consumed using the new API keys
1 parent f89f3be commit e43f7bb

File tree

2 files changed

+81
-1
lines changed

2 files changed

+81
-1
lines changed

packages/api/src/layerApi.ts

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { Router } from "express";
2+
import { verify } from "./apiKeys";
3+
import { DBClient } from "./dbClient";
4+
5+
/**
6+
* Make available information about a layer to clients with a project
7+
* API key. This should be useful for implementing geoprocessing
8+
* functions using data hosted on SeaSketch. This information includes:
9+
* - mapbox-gl-styles (good for matching colors with cartography)
10+
* - geostats - all sorts of useful statistics
11+
* - data_upload_outputs - Find the fgb, geojson, or geotif
12+
* representation of a source
13+
* @param loadersPool
14+
*/
15+
export default function layerApi(loadersPool: DBClient) {
16+
const router = Router();
17+
18+
router.get("/:slug/:stableId", async (req, res) => {
19+
const bearerToken = req.headers.authorization;
20+
if (!bearerToken) {
21+
res.status(401).send("Unauthorized");
22+
return;
23+
}
24+
const apiKey = bearerToken.replace("Bearer ", "");
25+
const claims = await verify(apiKey, loadersPool);
26+
if (!claims) {
27+
res.status(401).send("Unauthorized");
28+
return;
29+
}
30+
const q1 = await loadersPool.query(
31+
`
32+
select
33+
data_source_id,
34+
mapbox_gl_styles,
35+
project_id
36+
from
37+
data_layers
38+
where id = (
39+
select
40+
data_layer_id
41+
from
42+
table_of_contents_items
43+
where
44+
stable_id = $1 and
45+
is_draft = true and
46+
project_id = (
47+
select id from projects where slug = $2
48+
)
49+
)`,
50+
[req.params.stableId, req.params.slug]
51+
);
52+
if (q1.rows.length === 0) {
53+
res.status(404).send("Not found");
54+
return;
55+
}
56+
const { data_source_id, mapbox_gl_styles, project_id } = q1.rows[0];
57+
if (project_id !== claims.projectId) {
58+
res.status(401).send("Unauthorized");
59+
return;
60+
}
61+
const q2 = await loadersPool.query(
62+
`select * from data_upload_outputs where data_source_id = $1`,
63+
[data_source_id]
64+
);
65+
const data_upload_outputs = q2.rows;
66+
const q3 = await loadersPool.query(
67+
`select geostats from data_sources where id = $1`,
68+
[data_source_id]
69+
);
70+
const geostats = q3.rows[0].geostats;
71+
res.json({ mapbox_gl_styles, data_upload_outputs, geostats });
72+
});
73+
74+
return router;
75+
}

packages/api/src/server.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { ManagementClient } from "auth0";
2727
import * as cache from "./cache";
2828
import { verifyEmailWithToken } from "./emailVerification";
2929
import { getRealUserVisits, getVisitorMetrics } from "./visitorMetrics";
30+
import layerApi from "./layerApi";
3031

3132
const ISSUER = (process.env.ISSUER || "seasketch.org")
3233
.split(",")
@@ -133,6 +134,11 @@ app.get("/.well-known/jwks.json", async (req, res) => {
133134
res.json(keys);
134135
});
135136

137+
const loadersPool = createPool({}, "admin");
138+
// Layers API comes before the authorization middleware because it makes
139+
// use of project api keys
140+
app.use("/layers", layerApi(loadersPool));
141+
136142
// Parse Bearer tokens and populate req.user with valid claims
137143
app.use(
138144
authorizationMiddleware,
@@ -313,7 +319,6 @@ run({
313319
const tilesetPool = createPool();
314320
const geoPool = createPool();
315321
const bookmarksPool = createPool();
316-
const loadersPool = createPool({}, "admin");
317322

318323
app.use("/verify-email", async function (req, res, next) {
319324
// get token from query string

0 commit comments

Comments
 (0)