Files
map-asset-gateway/docs/API.md
T
Harmon 46d83145d9 Keep the admin surface local while serving tiles on the LAN
Binding to 0.0.0.0 used to demand credentials because the admin console and
the tile routes shared one listener. That forced a choice between keeping the
console off the network and letting other machines fetch tiles.

Admin routes are now loopback-only by default: the service can bind 0.0.0.0 so
/tiles, /vectors, the catalog and the probes reach the LAN, while /admin and
/api/v1/admin answer 403 to anything arriving from another host. Credentials
are only required when ADMIN_ALLOW_REMOTE=true actually opens the console up,
which is the case worth guarding.

Tests cover both sides: a remote admin request is refused, a remote tile
request still reaches the token check.
2026-10-16 10:41:32 +08:00

11 KiB
Raw Blame History

API 调用文档

面向调用方(前端、下游系统、脚本)。服务默认监听 http://127.0.0.1:8910, 局域网开放方式见文末。


1. 认证

1.1 管理面(/api/v1/admin/*、/admin/*)

用管理员凭据,与业务令牌是两套东西:

方式 场景 配置
HTTP Basic 浏览器打开运维台 ADMIN_USER + ADMIN_PASSWORD
Bearer 脚本/接口调用 ADMIN_TOKEN
curl -u ops:change-me http://127.0.0.1:8910/api/v1/admin/basemaps
curl -H "Authorization: Bearer <ADMIN_TOKEN>" http://127.0.0.1:8910/api/v1/admin/basemaps

管理面可以签发令牌、改默认版本、配推送目标,所以不要在局域网裸奔: API_HOST 绑非回环地址时若未配置任何管理员凭据,服务会拒绝启动。

1.2 业务面(编目 / 取图 / 切片)

用服务令牌(bmp_ 开头,由管理面签发)。四种投递方式任选:

Authorization: Bearer <token>
X-Access-Token: <token>
?token=<token>
?access_token=<token>

查询参数形式是给地图客户端用的(<img>/瓦片库没法设请求头)。

令牌带 scope:read(默认,读已授权的底图/矢量)和 slice(提交切片任务)。 只带 slice 的令牌不需要任何底图授权,因此读不到任何瓦片。

POST /api/v1/admin/tokens
{"name": "integration", "scopes": ["slice"]}

1.3 错误格式

所有错误统一:

{"error": "token has no access to this basemap"}
状态码 含义
400 参数不合法
401 令牌缺失/无效/过期/停用
403 令牌有效,但没有该资源权限或缺少所需 scope
404 资源或任务不存在
409 已有扫描在执行
500 服务端错误

2. 系统端点(无需认证)

方法 路径 说明
GET /health 存活探针
GET /ready 就绪探针
GET /metrics Prometheus 文本指标(请求数、瓦片数/404、鉴权失败、运行时长)
GET /api/v1/system 服务信息(版本、各 base URL、扫描根目录等)

3. 管理 API(需管理员凭据)

编目

方法 路径 说明
GET /api/v1/admin/dashboard 运维台一次性数据(台账、令牌、推送、图层源清单等)
GET /api/v1/admin/basemaps 底图列表(含版本)
GET /api/v1/admin/vectors 矢量资源列表
GET /api/v1/admin/scan-sources 扫描源列表
POST /api/v1/admin/scan-sources 新增/更新扫描源
POST /api/v1/admin/scan-sources/{code}/run 立即扫描该源
POST /api/v1/admin/vectors/rescan 重扫矢量目录
POST /api/v1/admin/basemaps/{code}/default-version 设默认版本,body {"version":"2026-04"}
POST /api/v1/admin/health/check 立即执行资源可用性巡检

巡检返回:

{"checked_at":"2026-10-16T02:00:00Z","versions_checked":3,"versions_missing":0,
 "vectors_checked":1,"vectors_missing":0}

令牌

方法 路径 说明
GET /api/v1/admin/tokens 令牌列表(只含前缀,不含明文)
POST /api/v1/admin/tokens 签发令牌
POST /api/v1/admin/tokens/{id}/disable 停用

签发请求:

{
  "name": "portal-a",
  "basemap_codes": ["gaode-image", "google-image"],
  "vector_codes": ["全国行政区.geojson", "landuse/parcel.shp"],
  "scopes": ["slice"],
  "expires_at": "2027-01-01T00:00:00Z"
}
  • vector_codes 可写 code、相对路径或文件名
  • expires_at 省略时用 TOKEN_DEFAULT_TTL(默认 30 天);0 表示不过期
  • 响应里的 token 字段是明文,只返回这一次,务必立即保存

推送

方法 路径 说明
GET /api/v1/admin/targets 目标系统列表
POST /api/v1/admin/targets 新增/更新目标系统
POST /api/v1/admin/pushes 推送底图版本 {target_code, basemap_code, version}
POST /api/v1/admin/vector-pushes 推送矢量 {target_code, vector_code}
GET /api/v1/admin/push-records 底图推送记录(最近 20)
GET /api/v1/admin/vector-push-records 矢量推送记录
POST /api/v1/admin/push-records/{id}/retry 手动重推
POST /api/v1/admin/vector-push-records/{id}/retry 手动重推(矢量)

推送异步执行,提交返回 202 与一条 pending 记录;失败按 30s/60s 退避 自动重试最多 3 次,状态在 retrying / failed / succeeded 之间变化。


4. 编目 API(需服务令牌,read)

方法 路径 说明
GET /api/v1/catalog/basemaps 该令牌可见的底图(已按授权过滤)
GET /api/v1/catalog/basemaps/{code} 单个底图详情
GET /api/v1/catalog/vectors 可见的矢量资源
GET /api/v1/catalog/vectors/{code} 单个矢量详情

底图详情里的 url_template 就是可直接使用的取图模板:

http://<tile-base>/tiles/gaode-image/current/{z}/{x}/{y}.webp

5. 切片 API(需 slice scope 的令牌)

方法 路径 说明
POST /api/v1/slice/jobs 提交切片任务 → 202 + 任务对象
GET /api/v1/slice/jobs/{id} 轮询单个任务 → 200 + 任务对象
GET /api/v1/slice/layers/{code}/sources 该图层由哪些源影像构成

5.1 提交

{
  "code": "uav-2026-flight-b",
  "name": "2026无人机影像 - 第二次飞行",
  "target_code": "uav-2026",
  "source_paths": ["D:\\uav\\flight-b.tif"],
  "zoom_min": 0,
  "zoom_max": 18,
  "tile_size": 256,
  "tile_format": "webp",
  "callback_url": "http://portal-a/api/tile-jobs/done",
  "requested_by": "ops"
}

字段说明:

字段 必填 说明
target_code 是 图层编码,必须 ASCII(中文会被归一化掉)。显示名由调用端自己映射
source_paths 是 源影像绝对路径数组;单元素时也可用 source_path
code 否 幂等键。同一个 code 重复提交会返回既有任务,不会排队第二个
zoom_min / zoom_max 否 默认 0 / 0
tile_size 否 默认 256
tile_format 否 默认 webp(仅为元数据,实际格式由 gdal2tiles 决定)
callback_url 否 任务结束时回调
requested_by 否 审计用

两种模式,由源的数量决定:

  • 传 1 个源 → 直接切这一张,落到 输出目录,用于日常增量累加(新飞行覆盖到哪算哪)
  • 传多个源 → 先 gdalbuildvrt 拼成一张 VRT 再切,逐像素镶嵌,无瓦片接缝,用于重建整个图层

累加示例(同一个 target_code,每次只提交新飞的):

# 第一次
curl -X POST http://127.0.0.1:8910/api/v1/slice/jobs \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"target_code":"uav-2026","source_paths":["D:\\uav\\flight-a.tif"],"zoom_min":0,"zoom_max":18}'

# 几天后再飞,自动累加
curl -X POST http://127.0.0.1:8910/api/v1/slice/jobs \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"target_code":"uav-2026","source_paths":["D:\\uav\\flight-b.tif"],"zoom_min":0,"zoom_max":18}'

重建为无缝镶嵌:先取全部源,再一次性提交。

curl -H "Authorization: Bearer $TOKEN" \
  http://127.0.0.1:8910/api/v1/slice/layers/uav-2026/sources

5.2 任务对象

{
  "id": "265f65bb-b15b-40c2-a672-2103aea2c26d",
  "code": "uav-2026-flight-b",
  "name": "2026无人机影像 - 第二次飞行",
  "source_count": 1,
  "source_paths": ["D:\\uav\\flight-b.tif"],
  "target_code": "uav-2026",
  "output_root": "D:\\Code\\tile-server\\data\\tiles\\uav-2026",
  "zoom_min": 0, "zoom_max": 18,
  "tile_size": 256, "tile_format": "webp",
  "status": "pending",
  "message": "",
  "log_path": "",
  "callback_url": "http://portal-a/api/tile-jobs/done",
  "callback_status": "",
  "created_at": "2026-10-16T02:00:00Z"
}

status:pending → running → succeeded / failed。 失败原因在 message,完整 stdout/stderr 在 log_path。

任务单并发,按提交顺序执行,且不阻塞 API。

5.3 完成回调

任务结束时向 callback_url POST:

{"event": "tile.job.finished", "job": { ...任务对象... }, "delivered": "2026-10-16T02:05:00Z"}

失败按 30s/60s 退避重试,最多 3 次;状态在任务的 callback_status / callback_attempts / callback_message 里。


6. 取图与下载(需服务令牌)

方法 路径 说明
GET /tiles/{code}/{version}/{z}/{x}/{y}.ext 指定版本取图
GET /tiles/{code}/{z}/{x}/{y}.ext 兼容路由,取默认版本
GET /vectors/{code}/download 下载矢量(shapefile 自动打包 zip)
GET /geojson/{相对路径} 兼容路由,按原始相对路径取 GeoJSON
GET /shp/{相对路径} 兼容路由,按相对路径取 shapefile(返回 zip)
curl -o 0.webp "http://127.0.0.1:8910/tiles/gaode-image/current/0/0/0.webp?token=$TOKEN"

瓦片响应带 Cache-Control: public, max-age=31536000, immutable。


7. 局域网开放

默认 API_HOST=127.0.0.1,只有本机能访问。管理台和切片可以分开暴露:

推荐:切片上局域网,管理台仍只在本机

服务绑到所有网卡,但管理员路由只接受来自回环地址的请求(默认行为):

# config/service.env

# 1) 监听所有网卡,切片才能被其他机器取到
API_HOST=0.0.0.0

# 2) 对外地址改成局域网 IP(你这台是 192.168.1.62),
#    否则台账里的 url_template 发出去还是 127.0.0.1
API_BASE_URL=http://192.168.1.62:8910
TILE_PUBLIC_BASE_URL=http://192.168.1.62:8910
VECTOR_PUBLIC_BASE_URL=http://192.168.1.62:8910

# 3) 管理员路由保持仅回环(默认就是 false,写出来是为了明确)
ADMIN_ALLOW_REMOTE=false

这样:/tiles/*、/vectors/*、/api/v1/catalog/*、/health 等从局域网可达 (取图仍需令牌);/admin/* 与 /api/v1/admin/* 从其他机器请求会返回 403。

改完必须重启服务并重扫一次:

POST /api/v1/admin/scan-sources/local-basemaps/run

台账里的 url_template 是扫描时用 base URL 拼出来的,不重扫的话对外仍是旧的 127.0.0.1 地址。

放行防火墙入站:

New-NetFirewallRule -DisplayName "Basemap Pusher 8910" -Direction Inbound `
  -Protocol TCP -LocalPort 8910 -Action Allow

可选:管理台也上局域网

把 ADMIN_ALLOW_REMOTE=true 打开(或在局域网内直接用管理台)。此时必须配置 管理员凭据,否则服务拒绝启动:

ADMIN_ALLOW_REMOTE=true
ADMIN_USER=ops
ADMIN_PASSWORD=<改掉它>

浏览器会弹 Basic 认证框,此时局域网内访问 http://192.168.1.62:8910/admin/。

启动守卫规则

API_HOST ADMIN_ALLOW_REMOTE 有管理员凭据 结果
回环 任意 任意 正常启动
非回环 false(默认) 不需要 正常启动,管理员路由仅回环
非回环 true 是 正常启动,管理台可远程访问
非回环 true 否 拒绝启动

安全提醒

这是明文 HTTP,Basic 凭据与令牌在网线上只是 base64,没有加密。内网自用没问题, 但如果要跨网段或给外部系统调用,建议前面挂 Nginx 上 HTTPS。