Compare commits

..
16 Commits
Author SHA1 Message Date
nomdn 2a4e417b28 前端:地图打点改回统一实心圆点样式 2026-11-01 21:43:25 +08:00
nomdn 65001d1eff 前端:地图打点改环形+实心混排,重合不再互相遮盖\n\n挪绘制顺序只是把遮挡换个方向(LemonDB 置顶后 GeoCN 又被盖)。改为\nLemonDB 画空心大环(radius 14)套在外面,其余源实心小点(radius 7,\n深色描边增强辨识)在内,重合时全部可见。 2026-11-01 21:41:23 +08:00
nomdn 4d2273c885 前端:location 地图 LemonDB 打点置顶\n\n圆点按 SOURCE_DEFS 顺序绘制,后画的盖在先画的上面;各源坐标基本重合,\nLemonDB 排首位先画被完全遮住。挪到末尾最后画,琥珀色点置顶可见。 2026-11-01 21:38:38 +08:00
nomdn a837f38c8f 前端:location 页 LemonDB 行去掉(自家全球库)标注 2026-11-01 21:36:58 +08:00
nomdn 5f202a3a2b docs(ipdb)/前端:LemonDB 坐标统一 WGS-84 的跟随调整
源库 world 阶段已把 division-coords(GCJ-02)逆解为 WGS-84,coord_system
恒为 wgs84。前端 location 页 lemondb 打点回归 wgs84ToGcj02 统一转换路径
(coord_system 动态判断保留作前向兼容),注释与 locate.md 同步。
2026-11-01 21:33:29 +08:00
nomdn df9f7857ed docs(ipdb): 注明 LemonDB 拉取节奏——启动即拉 + 每日条件请求,中断恢复后一天内追平 2026-11-01 21:10:19 +08:00
nomdn 61788fa7ae feat(ipdb): 接入 LemonDB(自家全球库)作为定位与 ASN 源
- ipdb.go:PullDatabase 新增 LEMON-LOCATION.mmdb / LEMON-ASN.mmdb 下载任务
  (nomdn/LemonDB 月度 release,走 ghproxy 镜像链)
- searchip.go:mmdbFiles 注册 lemondb_location / lemondb_asn;
  searchLemonDB 解析全球定位记录(country/region/city/district/division_code/
  isp/type/coord_system——坐标国内 GCJ-02、境外 WGS-84,透传给前端);
  lemondb_asn 与 GeoLite2-ASN 记录同键,复用 searchMMDBASNIn
- allDatabases 加入 lemondb / lemondb_asn,/v1/location 全源输出自动带上
- resolveASN 优先级:lemondb_asn 打头(GeoLite2 底子相同、CN 组织名更准),
  /v1/asn 与 ws asn 分支透传 lemondb_asn
- 前端 location 页新增 LemonDB 行与地图打点(coord_system 动态判断 GCJ-02);
  asn 页新增 LemonDB ASN 行;locate.md 补字段说明
2026-11-01 20:55:25 +08:00
nomdn c9d1f21fa7 feat(location): GeoCN 补充区划政府驻地坐标打点
- 内置 division-coords.json(GB/T 2260 区划代码 → 政府驻地 [lng,lat],
  GCJ-02,数据来自 AreaCity),go:embed 编译期内建
- searchGeoCN 按 division_code 查表输出 latitude/longitude,新设区划查不到时省略
- 修复:var 初始化时解析结果未赋回 map,导致对照表恒为空(新增单测覆盖)
- 前端归属地地图增加 GeoCN 打点(天青色,GCJ-02 直接用)
2026-10-31 14:35:36 +08:00
nomdn fbf5c9e3b0 fix(location): 地图加 maxNativeZoom=17,修复最大缩放级瓦片空白
高德栅格瓦片原生层级约到 17:部分区域(尤其县乡)z=18 会返回纯米色
无数据空瓦片,Leaflet 照常贴上就是用户看到的放大到底一片空白。
声明 maxNativeZoom 后,Leaflet 对更高缩放自动放大 17 级瓦片
(轻度模糊但绝不变空白)。
2026-10-31 13:49:54 +08:00
nomdn af0b5e1ea0 fix(location): MaxMind/DB-IP 打点字段名修正(JSON 键为小写 latitude/longitude)
源定义误用了 Go 结构体字段名(Latitude/Longitude),与 JSON 序列化键
不一致导致这两源永远取不到坐标。修正后四源打点全部生效(实测
223.68.218.79:bilibili/IP2Location 宿迁 + MaxMind 郑州质心 +
DB-IP 南京,视野自适应包全)
2026-10-31 13:14:51 +08:00
nomdn 4d608a6697 feat(location): 地图数据源补充 DB-IP City
DB-IP 对中国 IP 常给省会级坐标(如宿迁 IP 给南京),与 bilibili/IP2Location
同图对比可直观看出免费库的城市级精度差异。
2026-10-31 12:54:43 +08:00
nomdn afd9df87c1 feat(location): 归属地地图改多源打点
每个有坐标的来源各画一个点(bilibili 绿 / MaxMind 蓝 / IP2Location 橙),
fitBounds 自适应包住全部点;只画优先级第一点改为多点对比展示——
归属地页的核心本就是多源差异对比,图上也应直观可见。
2026-10-31 12:19:39 +08:00
nomdn 4987cea8e0 feat(location): 归属地地图(高德瓦片直连 + Leaflet + GCJ-02 转换)
- 高德矢量瓦片直连(webrd0-4,免 key,底图自带审图号),attribution 标注来源
- 打点坐标优先级:bilibili(国内服务已是 GCJ-02 直接用)→
  MaxMind / IP2Location(WGS84 经 utils/gcj02 转 GCJ-02 与底图对齐)
- circleMarker 打点 + popup(IP 与坐标来源),无默认 marker 图标的打包问题
- leaflet 走动态 import(免 SSR 访问 window),CSS 以非 scoped 引入
  (动态创建的窗格拿不到 data-v 属性)
2026-10-31 12:09:00 +08:00
nomdn 0dce0fef14 feat(frontend-ssr): ASN 页节点池改为双栈∩IPLocationAPI 交集
- asn.vue:apiList 取两个池都有的节点(按 id 交集过滤),ASN 与
  location/双栈节点对齐;任一池单独加节点不会进本页
- config:cn2-sichuan / cn-jiangsu 补入 DualStack 池(与 IPLocationAPI
  共享),交集立即生效并扩展 whois/ssl/detail 的 CN 节点覆盖
2026-10-31 11:23:52 +08:00
nomdn fac8be622c feat(frontend-ssr): 恢复西安电信节点(cn-xian) 2026-10-31 10:55:40 +08:00
nomdn 8ae615d0e3 fix(ipdb): GeoCN 行政区划下载补镜像列表,jsdelivr 被墙不再导致数据永久缺失
full.txt/short.txt 是 12 个下载任务里唯一没有镜像可退的(其余 10 个走
ghproxy+raw 均可达):cdn.jsdelivr.net 在国内移动网络下 v4/v6 双栈都被
RST,重试 3 次全打在同一域名上,行政区划数据随之永久缺失(GeoCN 归属
地只剩 division_code 没有省市区文案)。

- downloadTask 改为镜像列表:jsdelivr → fastly → gcore → ghproxy+raw
  依次轮试,每镜像 2 次尝试;单栈节点错误协议族/单镜像被墙均秒级 failover
- 下载失败日志区分「保留本地旧副本」与「本地无副本、相关库降级至下次同步」
  (原日志在本地无文件时谎称 keep existing)

实测(江苏移动家宽,与故障节点同网络):后台初始化全量拉取成功,
division data loaded full=3595 short=3595,/v1/location 返回完整省市区。
2026-10-31 10:45:22 +08:00
13 changed files with 625 additions and 214 deletions

No files matched your search

+14 -1
View File
@@ -44,6 +44,7 @@ useHead({
interface ASNResult {
ip: string
lemondb_asn?: { asn: string; org: string }
geolite2_asn?: { asn: string; org: string }
dbip_asn?: { asn: string; org: string }
ip2location_asn?: { asn: string; as: string }
@@ -56,7 +57,12 @@ const result = ref<ASNResult | null>(null)
const error = ref('')
const userIP = ref('')
const apiList = config.IPLocationAPI
// ASN 页节点池:DualStack ∩ IPLocationAPI 的交集(按 id 取两个池都有的节点)。
// 交集保证:节点在 IPLocationAPI 里(中间件 asn 分支按它解析转发),
// 同时也是双栈池在册的全功能节点;任一池单独加节点都不会进本页。
const apiList = config.IPLocationAPI.filter((loc) =>
config.APIBaseURL.DualStack.some((d) => d.id === loc.id)
)
const currentApiIndex = ref(0)
const backendID = computed(() => apiList[currentApiIndex.value]?.id || '')
@@ -173,6 +179,13 @@ function getStatusClass(status: string): string {
<td class="table-label">IP 地址</td>
<td class="table-value">{{ result.ip }}</td>
</tr>
<tr v-if="result.lemondb_asn?.asn">
<td class="table-label">LemonDB ASN</td>
<td class="table-value">
{{ result.lemondb_asn.asn }}
<span v-if="result.lemondb_asn.org" style="color: #999; font-size: 0.9em;">{{ result.lemondb_asn.org }}</span>
</td>
</tr>
<tr v-if="result.geolite2_asn?.asn">
<td class="table-label">Maxmind ASN</td>
<td class="table-value">
+109
View File
@@ -6,6 +6,7 @@ import { useMiddlewareFetch } from '../../utils/middleware';
import { CircleCheckFilled, CircleCloseFilled } from '@element-plus/icons-vue';
import { useRoute } from 'vue-router'
import { isIPv4 } from '../../utils/tools';
import { wgs84ToGcj02 } from '../../utils/gcj02';
const route = useRoute();
const loading = ref(false);
@@ -47,6 +48,8 @@ interface IPLocationType {
bilibili?: any;
ip?: string;
geocn?: any;
lemondb?: any;
lemondb_asn?: any;
ip2region?: any;
ip2location?: any;
ip2location_asn?: any;
@@ -90,6 +93,88 @@ const ipAddress = ref('');
const IPLocation = ref<IPLocationType>({});
const UserIP = ref('');
const error = ref('')
// —— 归属地地图:高德瓦片(GCJ-02)+ Leaflet,免 key ——
// 每个有坐标的来源各画一个点(bilibili / geocn 坐标已是 GCJ-02 直接用;
// maxmind / ip2location / dbip / lemondb 为 WGS84 → 转 GCJ-02 与底图对齐),
// 多点同时展示可直观看出各库定位的差异与离散度。
const mapContainer = ref<HTMLElement | null>(null)
let mapInstance: any = null
let markerGroup: any = null
const SOURCE_DEFS = [
{ key: 'lemondb', lat: 'latitude', lng: 'longitude', gcj: false, label: 'LemonDB', color: '#ffab00' },
{ key: 'bilibili', lat: 'latitude', lng: 'longitude', gcj: true, label: 'bilibili Live', color: '#299764' },
{ key: 'maxmind_city', lat: 'latitude', lng: 'longitude', gcj: false, label: 'MaxMind', color: '#1d4ed8' },
{ key: 'ip2location', lat: 'latitude', lng: 'longitude', gcj: false, label: 'IP2Location', color: '#f97316' },
{ key: 'dbip_city', lat: 'latitude', lng: 'longitude', gcj: false, label: 'DB-IP', color: '#8b5cf6' },
{ key: 'geocn', lat: 'latitude', lng: 'longitude', gcj: true, label: 'GeoCN', color: '#0ea5e9' },
]
const mapPoints = computed(() => {
const r = IPLocation.value
if (!r) return []
const pts: Array<{ lat: number; lng: number; label: string; color: string }> = []
for (const s of SOURCE_DEFS) {
const lat = parseFloat(r[s.key]?.[s.lat])
const lng = parseFloat(r[s.key]?.[s.lng])
if (Number.isFinite(lat) && Number.isFinite(lng) && !(lat === 0 && lng === 0)) {
// LemonDB 全库统一 WGS-84(coord_system 恒为 wgs84,这里留个动态判断作前向兼容,
// 若哪天源库换坐标系标注会自动跟随);其余源维持静态标记
const gcj = s.gcj || r[s.key]?.coord_system === 'gcj02'
const p = gcj ? { lat, lng } : wgs84ToGcj02(lat, lng)
pts.push({ ...p, label: s.label, color: s.color })
}
}
return pts
})
let leafletModule: any = null
async function renderLocationMap() {
if (!import.meta.client || !mapPoints.value.length || !mapContainer.value) return
const pts = mapPoints.value
leafletModule = leafletModule || (await import('leaflet'))
const L = leafletModule.default ?? leafletModule
if (!mapInstance) {
mapInstance = L.map(mapContainer.value, { attributionControl: true })
L.tileLayer('https://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}', {
subdomains: ['1', '2', '3', '4'],
maxZoom: 18,
// 高德栅格瓦片原生层级上限约 17:部分区域(尤其县乡)z=18 会返回纯米色
// "无数据"空瓦片。声明 maxNativeZoom 后,Leaflet 对更高缩放自动放大
// 17 级瓦片(轻度模糊但绝不变空白)。
maxNativeZoom: 17,
attribution: '&copy; <a href="https://www.amap.com/" target="_blank" rel="noreferrer">高德地图</a>',
}).addTo(mapInstance)
}
if (markerGroup) markerGroup.remove()
markerGroup = L.featureGroup()
for (const p of pts) {
L.circleMarker([p.lat, p.lng], {
radius: 8,
color: p.color,
fillColor: p.color,
fillOpacity: 0.85,
weight: 2,
})
.bindPopup(`<b>${p.label}</b><br>${ipAddress.value || UserIP.value}<br>${p.lat.toFixed(5)}, ${p.lng.toFixed(5)}`)
.addTo(markerGroup)
}
markerGroup.addTo(mapInstance)
// 多点:视野自适应包住全部点;单点:定位到 10 级
if (pts.length > 1) {
mapInstance.fitBounds(markerGroup.getBounds().pad(0.3))
} else {
mapInstance.setView([pts[0].lat, pts[0].lng], 10)
}
}
watch(IPLocation, async () => {
if (mapPoints.value.length) {
await nextTick()
await renderLocationMap()
}
})
const locationUrl = computed(() => "/middleware/" + backendID.value + "/location/" + encodeURIComponent(ipAddress.value));
const { data: locationData, error: locationError, execute: executeLocation } = useMiddlewareFetch<IPLocationType>(locationUrl, {
@@ -187,6 +272,17 @@ onMounted(async () => {
<div v-if="IPLocation" class="result-section">
<table class="result-table">
<tbody>
<tr v-if="IPLocation.lemondb && (IPLocation.lemondb.country || IPLocation.lemondb.administrative_area || IPLocation.lemondb.city)">
<td class="table-label">LemonDB</td>
<td class="table-value">
{{ IPLocation.lemondb?.country }}&nbsp;{{ IPLocation.lemondb?.administrative_area }}&nbsp;{{ IPLocation.lemondb?.city }}&nbsp;{{ IPLocation.lemondb?.district }}
</td>
<td class="table-value">
<span>
{{ IPLocation.lemondb?.isp }}&nbsp;{{ IPLocation.lemondb?.type }}
</span>
</td>
</tr>
<tr v-if="IPLocation.bilibili && (IPLocation.bilibili.administrative_area || IPLocation.bilibili.city)">
<td class="table-label">bilibili Live接口</td>
<td class="table-value">
@@ -233,6 +329,8 @@ onMounted(async () => {
</tbody>
</table>
</div>
<!-- 归属地地图:高德瓦片(GCJ-02)+ Leaflet,每个有坐标的来源各画一点 -->
<div v-if="mapPoints.length" ref="mapContainer" class="location-map"></div>
</div>
<div style="font-size: 1.5em;">
<h3 v-if="isIPv6(UserIP)"><el-icon><CircleCheckFilled style="color: lightgreen;"/></el-icon>您的网络IPv6优先</h3>
@@ -258,6 +356,17 @@ onMounted(async () => {
<style scoped>
@import "../style.css";
@import "../../assets/css/tool-common.css";
/* leaflet 基础样式必须非 scoped 引入(动态创建的窗格拿不到 data-v 属性) */
</style>
<style>
@import "leaflet/dist/leaflet.css";
.location-map {
height: 320px;
margin-top: 16px;
border-radius: 8px;
border: 1px solid var(--el-border-color-lighter, #ebeef5);
z-index: 0; /* 压住 leaflet 内部窗格的层级,避免盖过下拉/弹窗 */
}
</style>
<style>
@import "../style.css";
+17 -5
View File
@@ -93,16 +93,28 @@ const config = {
id: "hk-shatin",
url: "https://china-hk-kowloon-shatindistrict-newcloud.ipwapi.zfap.wsmdn.top/"
},
// {
// label: "中国 陕西 西安 北经济技术开发区 未央区凤城 中国电信[ZFC]",
// id: "cn-xian",
// url: "https://cn-xian-shaanxiprovince.api-ipw.wsmdn.top/"
// },
{
label: "中国 陕西 西安 北经济技术开发区 未央区凤城 中国电信[ZFC]",
id: "cn-xian",
url: "https://cn-xian-shaanxiprovince.api-ipw.wsmdn.top/"
},
{
label: "美国/加利福尼亚州/洛杉矶/蒙特利奇帕克/USCD[ZFC]",
id: "us-la",
url: "https://america-california-losangeles.ipwapi.zfap.wsmdn.top/"
},
{
// 与 IPLocationAPI 池共享的节点:ASN 页取两池交集,
// 只有同时出现在两个池里的节点才会显示(cn-xian 仅双栈池故不显示)
label: "中国 四川 沙渠 电信[ZFC]",
id: "cn2-sichuan",
url: "https://cn2-sichuan.api-ipw.wsmdn.top/"
},
{
label: "中国 江苏 移动",
id: "cn-jiangsu",
url: "https://cn-jiangsu.api-ipw.wsmdn.top/"
},
// IP 直连新节点:URL 留空,仅经独立中间件转发
{
label: "香港 Cogent",
+2
View File
@@ -21,12 +21,14 @@
"@shikijs/engine-javascript": "^4.4.1",
"@sindresorhus/slugify": "^3.0.0",
"@tailwindcss/vite": "^4.3.3",
"@types/leaflet": "^1.9.22",
"@vueuse/core": "^14.4.0",
"@vueuse/nuxt": "^14.4.0",
"element-plus": "^2.14.3",
"github-markdown-css": "^5.9.0",
"github-slugger": "^2.0.0",
"is-ip": "^5.0.1",
"leaflet": "1.9.4",
"markdown-it": "^14.3.0",
"markdown-it-anchor": "^9.2.1",
"nuxt": "^4.5.1",
+163 -150
View File
@@ -16,13 +16,13 @@ importers:
version: 1.1.5(@element-plus/icons-vue@2.3.2(vue@3.5.40(typescript@5.9.3)))(element-plus@2.14.3(vue@3.5.40(typescript@5.9.3)))(esbuild@0.28.1)(magicast@0.5.4)(oxc-parser@0.140.0)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))
'@nuxtjs/robots':
specifier: ^6.1.3
version: 6.1.3(d7dabbf3135c346ebe465b397dbcc84c)
version: 6.1.3(44499e2290ee408d98cc04f71ac84946)
'@nuxtjs/sitemap':
specifier: ^8.3.2
version: 8.3.2(d7dabbf3135c346ebe465b397dbcc84c)
version: 8.3.2(44499e2290ee408d98cc04f71ac84946)
'@nuxtjs/tailwindcss':
specifier: 6.14.0
version: 6.14.0(magicast@0.5.4)(supports-color@10.0.0)(yaml@2.9.0)
version: 6.14.0(magicast@0.5.4)(yaml@2.9.0)
'@shikijs/core':
specifier: ^4.4.1
version: 4.4.1
@@ -35,12 +35,15 @@ importers:
'@tailwindcss/vite':
specifier: ^4.3.3
version: 4.3.3(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))
'@types/leaflet':
specifier: ^1.9.22
version: 1.9.22
'@vueuse/core':
specifier: ^14.4.0
version: 14.4.0(vue@3.5.40(typescript@5.9.3))
'@vueuse/nuxt':
specifier: ^14.4.0
version: 14.4.0(magic-string@1.1.0)(magicast@0.5.4)(nuxt@4.5.1(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.0.0)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.0.0)))(@oxc-project/types@0.142.0)(@parcel/watcher@2.5.6)(@vue/compiler-sfc@3.5.40)(better-sqlite3@12.11.1)(db0@0.3.4(better-sqlite3@12.11.1))(esbuild@0.28.1)(ioredis@5.11.1(supports-color@10.0.0))(lightningcss@1.33.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.1)(rollup@4.62.4))(rollup@4.62.4)(sass-embedded@1.100.0)(sass@1.100.0)(srvx@0.11.22)(supports-color@10.0.0)(terser@5.49.0)(typescript@5.9.3)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(yaml@2.9.0))(oxc-parser@0.140.0)(rolldown@1.2.1)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)))(vue@3.5.40(typescript@5.9.3))
version: 14.4.0(magic-string@1.1.0)(magicast@0.5.4)(nuxt@4.5.1(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@oxc-project/types@0.142.0)(@parcel/watcher@2.5.6)(@vue/compiler-sfc@3.5.40)(better-sqlite3@12.11.1)(db0@0.3.4(better-sqlite3@12.11.1))(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.33.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.1)(rollup@4.62.4))(rollup@4.62.4)(sass-embedded@1.100.0)(sass@1.100.0)(srvx@0.11.22)(terser@5.49.0)(typescript@5.9.3)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(yaml@2.9.0))(oxc-parser@0.140.0)(rolldown@1.2.1)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)))(vue@3.5.40(typescript@5.9.3))
element-plus:
specifier: ^2.14.3
version: 2.14.3(vue@3.5.40(typescript@5.9.3))
@@ -53,6 +56,9 @@ importers:
is-ip:
specifier: ^5.0.1
version: 5.0.1
leaflet:
specifier: 1.9.4
version: 1.9.4
markdown-it:
specifier: ^14.3.0
version: 14.3.0
@@ -61,10 +67,10 @@ importers:
version: 9.2.1(@types/markdown-it@14.1.2)(markdown-it@14.3.0)
nuxt:
specifier: ^4.5.1
version: 4.5.1(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.0.0)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.0.0)))(@oxc-project/types@0.142.0)(@parcel/watcher@2.5.6)(@vue/compiler-sfc@3.5.40)(better-sqlite3@12.11.1)(db0@0.3.4(better-sqlite3@12.11.1))(esbuild@0.28.1)(ioredis@5.11.1(supports-color@10.0.0))(lightningcss@1.33.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.1)(rollup@4.62.4))(rollup@4.62.4)(sass-embedded@1.100.0)(sass@1.100.0)(srvx@0.11.22)(supports-color@10.0.0)(terser@5.49.0)(typescript@5.9.3)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(yaml@2.9.0)
version: 4.5.1(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@oxc-project/types@0.142.0)(@parcel/watcher@2.5.6)(@vue/compiler-sfc@3.5.40)(better-sqlite3@12.11.1)(db0@0.3.4(better-sqlite3@12.11.1))(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.33.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.1)(rollup@4.62.4))(rollup@4.62.4)(sass-embedded@1.100.0)(sass@1.100.0)(srvx@0.11.22)(terser@5.49.0)(typescript@5.9.3)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(yaml@2.9.0)
nuxt-security:
specifier: 2.6.0
version: 2.6.0(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rolldown@1.2.1)(rollup@4.62.4)(supports-color@10.0.0)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)))
version: 2.6.0(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rolldown@1.2.1)(rollup@4.62.4)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)))
vue:
specifier: ^3.5.40
version: 3.5.40(typescript@5.9.3)
@@ -1658,12 +1664,18 @@ packages:
'@types/estree@1.0.9':
resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
'@types/geojson@7946.0.16':
resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==}
'@types/hast@3.0.5':
resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==}
'@types/jsesc@2.5.1':
resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==}
'@types/leaflet@1.9.22':
resolution: {integrity: sha512-h3lhECYEKDasG7LFHu+GiHqAvsgLuQvlJvVZzJDGONo3sEL+wUOqSFLnwkZlK0qVxnxbuGFW8iBlJNYs5wgndA==}
'@types/linkify-it@5.0.0':
resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==}
@@ -3118,6 +3130,9 @@ packages:
resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==}
engines: {node: '>= 0.6.3'}
leaflet@1.9.4:
resolution: {integrity: sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==}
lightningcss-android-arm64@1.32.0:
resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
engines: {node: '>= 12.0.0'}
@@ -5267,20 +5282,20 @@ snapshots:
'@babel/compat-data@7.29.7': {}
'@babel/core@7.29.7(supports-color@10.0.0)':
'@babel/core@7.29.7':
dependencies:
'@babel/code-frame': 7.29.7
'@babel/generator': 7.29.8
'@babel/helper-compilation-targets': 7.29.7
'@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.0.0))(supports-color@10.0.0)
'@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7)
'@babel/helpers': 7.29.7
'@babel/parser': 7.29.8
'@babel/template': 7.29.7
'@babel/traverse': 7.29.8(supports-color@10.0.0)
'@babel/traverse': 7.29.8
'@babel/types': 7.29.8
'@jridgewell/remapping': 2.3.5
convert-source-map: 2.0.0
debug: 4.4.3(supports-color@10.0.0)
debug: 4.4.3
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
@@ -5316,41 +5331,41 @@ snapshots:
lru-cache: 5.1.1
semver: 6.3.1
'@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7(supports-color@10.0.0))(supports-color@10.0.0)':
'@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7)':
dependencies:
'@babel/core': 7.29.7(supports-color@10.0.0)
'@babel/core': 7.29.7
'@babel/helper-annotate-as-pure': 7.29.7
'@babel/helper-member-expression-to-functions': 7.29.7(supports-color@10.0.0)
'@babel/helper-member-expression-to-functions': 7.29.7
'@babel/helper-optimise-call-expression': 7.29.7
'@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7(supports-color@10.0.0))(supports-color@10.0.0)
'@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.0.0)
'@babel/traverse': 7.29.8(supports-color@10.0.0)
'@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7)
'@babel/helper-skip-transparent-expression-wrappers': 7.29.7
'@babel/traverse': 7.29.8
semver: 6.3.1
transitivePeerDependencies:
- supports-color
'@babel/helper-globals@7.29.7': {}
'@babel/helper-member-expression-to-functions@7.29.7(supports-color@10.0.0)':
'@babel/helper-member-expression-to-functions@7.29.7':
dependencies:
'@babel/traverse': 7.29.8(supports-color@10.0.0)
'@babel/traverse': 7.29.8
'@babel/types': 7.29.8
transitivePeerDependencies:
- supports-color
'@babel/helper-module-imports@7.29.7(supports-color@10.0.0)':
'@babel/helper-module-imports@7.29.7':
dependencies:
'@babel/traverse': 7.29.8(supports-color@10.0.0)
'@babel/traverse': 7.29.8
'@babel/types': 7.29.8
transitivePeerDependencies:
- supports-color
'@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7(supports-color@10.0.0))(supports-color@10.0.0)':
'@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)':
dependencies:
'@babel/core': 7.29.7(supports-color@10.0.0)
'@babel/helper-module-imports': 7.29.7(supports-color@10.0.0)
'@babel/core': 7.29.7
'@babel/helper-module-imports': 7.29.7
'@babel/helper-validator-identifier': 7.29.7
'@babel/traverse': 7.29.8(supports-color@10.0.0)
'@babel/traverse': 7.29.8
transitivePeerDependencies:
- supports-color
@@ -5360,18 +5375,18 @@ snapshots:
'@babel/helper-plugin-utils@7.29.7': {}
'@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7(supports-color@10.0.0))(supports-color@10.0.0)':
'@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7)':
dependencies:
'@babel/core': 7.29.7(supports-color@10.0.0)
'@babel/helper-member-expression-to-functions': 7.29.7(supports-color@10.0.0)
'@babel/core': 7.29.7
'@babel/helper-member-expression-to-functions': 7.29.7
'@babel/helper-optimise-call-expression': 7.29.7
'@babel/traverse': 7.29.8(supports-color@10.0.0)
'@babel/traverse': 7.29.8
transitivePeerDependencies:
- supports-color
'@babel/helper-skip-transparent-expression-wrappers@7.29.7(supports-color@10.0.0)':
'@babel/helper-skip-transparent-expression-wrappers@7.29.7':
dependencies:
'@babel/traverse': 7.29.8(supports-color@10.0.0)
'@babel/traverse': 7.29.8
'@babel/types': 7.29.8
transitivePeerDependencies:
- supports-color
@@ -5399,24 +5414,24 @@ snapshots:
dependencies:
'@babel/types': 8.0.4
'@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.0.0))':
'@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7)':
dependencies:
'@babel/core': 7.29.7(supports-color@10.0.0)
'@babel/core': 7.29.7
'@babel/helper-plugin-utils': 7.29.7
'@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.0.0))':
'@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7)':
dependencies:
'@babel/core': 7.29.7(supports-color@10.0.0)
'@babel/core': 7.29.7
'@babel/helper-plugin-utils': 7.29.7
'@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.0.0))(supports-color@10.0.0)':
'@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7)':
dependencies:
'@babel/core': 7.29.7(supports-color@10.0.0)
'@babel/core': 7.29.7
'@babel/helper-annotate-as-pure': 7.29.7
'@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.0.0))(supports-color@10.0.0)
'@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7)
'@babel/helper-plugin-utils': 7.29.7
'@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.0.0)
'@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.0.0))
'@babel/helper-skip-transparent-expression-wrappers': 7.29.7
'@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7)
transitivePeerDependencies:
- supports-color
@@ -5426,7 +5441,7 @@ snapshots:
'@babel/parser': 7.29.8
'@babel/types': 7.29.8
'@babel/traverse@7.29.8(supports-color@10.0.0)':
'@babel/traverse@7.29.8':
dependencies:
'@babel/code-frame': 7.29.7
'@babel/generator': 7.29.8
@@ -5434,7 +5449,7 @@ snapshots:
'@babel/parser': 7.29.8
'@babel/template': 7.29.7
'@babel/types': 7.29.8
debug: 4.4.3(supports-color@10.0.0)
debug: 4.4.3
transitivePeerDependencies:
- supports-color
@@ -5875,9 +5890,9 @@ snapshots:
dependencies:
zod: 4.4.3
'@koa/router@12.0.1(supports-color@10.0.0)':
'@koa/router@12.0.1':
dependencies:
debug: 4.4.3(supports-color@10.0.0)
debug: 4.4.3
http-errors: 2.0.1
koa-compose: 4.1.0
methods: 1.1.2
@@ -5885,19 +5900,19 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@kwsites/file-exists@1.1.1(supports-color@10.0.0)':
'@kwsites/file-exists@1.1.1':
dependencies:
debug: 4.4.3(supports-color@10.0.0)
debug: 4.4.3
transitivePeerDependencies:
- supports-color
'@kwsites/promise-deferred@1.1.1': {}
'@mapbox/node-pre-gyp@2.0.0(supports-color@10.0.0)':
'@mapbox/node-pre-gyp@2.0.0':
dependencies:
consola: 3.4.2
detect-libc: 2.1.2
https-proxy-agent: 7.0.5(supports-color@10.0.0)
https-proxy-agent: 7.0.5
node-fetch: 2.7.0
nopt: 8.0.0
semver: 7.8.5
@@ -5937,7 +5952,7 @@ snapshots:
'@nodelib/fs.scandir': 2.1.5
fastq: 1.20.1
'@nuxt/cli@3.37.0(@nuxt/schema@4.5.1)(@parcel/watcher@2.5.6)(magicast@0.5.4)(supports-color@10.0.0)':
'@nuxt/cli@3.37.0(@nuxt/schema@4.5.1)(@parcel/watcher@2.5.6)(magicast@0.5.4)':
dependencies:
'@bomb.sh/tab': 0.0.19(citty@0.2.2)
'@clack/prompts': 1.7.0
@@ -5945,7 +5960,7 @@ snapshots:
citty: 0.2.2
confbox: 0.2.4
consola: 3.4.2
debug: 4.4.3(supports-color@10.0.0)
debug: 4.4.3
defu: 6.1.7
exsolve: 1.1.1
fuse.js: 7.5.0
@@ -6013,7 +6028,7 @@ snapshots:
pkg-types: 2.3.1
semver: 7.8.5
'@nuxt/devtools@3.4.0(db0@0.3.4(better-sqlite3@12.11.1))(ioredis@5.11.1(supports-color@10.0.0))(magic-string@1.1.0)(oxc-parser@0.140.0)(rolldown@1.2.1)(supports-color@10.0.0)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)))(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3))':
'@nuxt/devtools@3.4.0(db0@0.3.4(better-sqlite3@12.11.1))(ioredis@5.11.1)(magic-string@1.1.0)(oxc-parser@0.140.0)(rolldown@1.2.1)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)))(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3))':
dependencies:
'@nuxt/devtools-kit': 3.4.0(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rolldown@1.2.1)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)))(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))
'@nuxt/devtools-wizard': 3.4.0
@@ -6039,11 +6054,11 @@ snapshots:
perfect-debounce: 2.1.0
pkg-types: 2.3.1
semver: 7.8.5
simple-git: 3.36.0(supports-color@10.0.0)
simple-git: 3.36.0
sirv: 3.0.2
structured-clone-es: 2.0.1
tinyglobby: 0.2.17
unstorage: 1.17.5(db0@0.3.4(better-sqlite3@12.11.1))(ioredis@5.11.1(supports-color@10.0.0))
unstorage: 1.17.5(db0@0.3.4(better-sqlite3@12.11.1))(ioredis@5.11.1)
vite: 8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)
vite-plugin-inspect: 11.4.1(@nuxt/kit@4.5.1(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rolldown@1.2.1)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))))(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))
vite-plugin-vue-tracer: 1.4.0(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3))
@@ -6164,7 +6179,7 @@ snapshots:
- rolldown
- unplugin
'@nuxt/nitro-server@4.5.1(00f251ea06a52b7897ea2534a2e9c338)':
'@nuxt/nitro-server@4.5.1(4be7fb31efaae22ce2a686c8078eaafa)':
dependencies:
'@nuxt/devalue': 2.0.2
'@nuxt/kit': 4.5.1(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rolldown@1.2.1)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)))
@@ -6181,9 +6196,9 @@ snapshots:
impound: 1.1.6(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))
klona: 2.0.6
mocked-exports: 0.1.1
nitropack: 2.13.4(@parcel/watcher@2.5.6)(better-sqlite3@12.11.1)(oxc-parser@0.140.0)(rolldown@1.2.1)(srvx@0.11.22)(supports-color@10.0.0)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))
nitropack: 2.13.4(@parcel/watcher@2.5.6)(better-sqlite3@12.11.1)(oxc-parser@0.140.0)(rolldown@1.2.1)(srvx@0.11.22)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))
nostics: 1.2.0
nuxt: 4.5.1(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.0.0)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.0.0)))(@oxc-project/types@0.142.0)(@parcel/watcher@2.5.6)(@vue/compiler-sfc@3.5.40)(better-sqlite3@12.11.1)(db0@0.3.4(better-sqlite3@12.11.1))(esbuild@0.28.1)(ioredis@5.11.1(supports-color@10.0.0))(lightningcss@1.33.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.1)(rollup@4.62.4))(rollup@4.62.4)(sass-embedded@1.100.0)(sass@1.100.0)(srvx@0.11.22)(supports-color@10.0.0)(terser@5.49.0)(typescript@5.9.3)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(yaml@2.9.0)
nuxt: 4.5.1(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@oxc-project/types@0.142.0)(@parcel/watcher@2.5.6)(@vue/compiler-sfc@3.5.40)(better-sqlite3@12.11.1)(db0@0.3.4(better-sqlite3@12.11.1))(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.33.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.1)(rollup@4.62.4))(rollup@4.62.4)(sass-embedded@1.100.0)(sass@1.100.0)(srvx@0.11.22)(terser@5.49.0)(typescript@5.9.3)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(yaml@2.9.0)
nypm: 0.6.9
ohash: 2.0.11
pathe: 2.0.3
@@ -6191,12 +6206,12 @@ snapshots:
std-env: 4.2.0
ufo: 1.6.4
unctx: 3.0.0(magic-string@1.1.0)(oxc-parser@0.140.0)(rolldown@1.2.1)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)))
unstorage: 1.17.5(db0@0.3.4(better-sqlite3@12.11.1))(ioredis@5.11.1(supports-color@10.0.0))
unstorage: 1.17.5(db0@0.3.4(better-sqlite3@12.11.1))(ioredis@5.11.1)
vue: 3.5.40(typescript@5.9.3)
vue-bundle-renderer: 2.3.1
vue-devtools-stub: 0.1.0
optionalDependencies:
'@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.0.0))
'@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7)
transitivePeerDependencies:
- '@azure/app-configuration'
- '@azure/cosmos'
@@ -6269,11 +6284,11 @@ snapshots:
rc9: 3.0.1
std-env: 4.2.0
'@nuxt/vite-builder@4.5.1(90e47d9be2a5bdbf0faa18fc6b372899)':
'@nuxt/vite-builder@4.5.1(d3f4e6fef6236f395bf4699fde3f7ca6)':
dependencies:
'@nuxt/kit': 4.5.1(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rolldown@1.2.1)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)))
'@vitejs/plugin-vue': 6.0.8(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3))
'@vitejs/plugin-vue-jsx': 5.1.6(supports-color@10.0.0)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3))
'@vitejs/plugin-vue-jsx': 5.1.6(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3))
autoprefixer: 10.5.4(postcss@8.5.25)
consola: 3.4.2
cssnano: 8.0.2(postcss@8.5.25)
@@ -6287,7 +6302,7 @@ snapshots:
knitwork: 1.3.0
mlly: 1.8.2
mocked-exports: 0.1.1
nuxt: 4.5.1(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.0.0)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.0.0)))(@oxc-project/types@0.142.0)(@parcel/watcher@2.5.6)(@vue/compiler-sfc@3.5.40)(better-sqlite3@12.11.1)(db0@0.3.4(better-sqlite3@12.11.1))(esbuild@0.28.1)(ioredis@5.11.1(supports-color@10.0.0))(lightningcss@1.33.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.1)(rollup@4.62.4))(rollup@4.62.4)(sass-embedded@1.100.0)(sass@1.100.0)(srvx@0.11.22)(supports-color@10.0.0)(terser@5.49.0)(typescript@5.9.3)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(yaml@2.9.0)
nuxt: 4.5.1(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@oxc-project/types@0.142.0)(@parcel/watcher@2.5.6)(@vue/compiler-sfc@3.5.40)(better-sqlite3@12.11.1)(db0@0.3.4(better-sqlite3@12.11.1))(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.33.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.1)(rollup@4.62.4))(rollup@4.62.4)(sass-embedded@1.100.0)(sass@1.100.0)(srvx@0.11.22)(terser@5.49.0)(typescript@5.9.3)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(yaml@2.9.0)
nypm: 0.6.9
pathe: 2.0.3
pkg-types: 2.3.1
@@ -6297,23 +6312,19 @@ snapshots:
std-env: 4.2.0
ufo: 1.6.4
unenv: 2.0.0-rc.24
unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))
vite: 8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)
vite-node: 6.0.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)
vite-plugin-checker: 0.14.5(typescript@5.9.3)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))
vue: 3.5.40(typescript@5.9.3)
vue-bundle-renderer: 2.3.1
optionalDependencies:
'@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7(supports-color@10.0.0))
'@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7)
rolldown: 1.2.1
rollup-plugin-visualizer: 7.0.1(rolldown@1.2.1)(rollup@4.62.4)
transitivePeerDependencies:
- '@biomejs/biome'
- '@farmfe/core'
- '@rspack/core'
- '@types/node'
- '@vitejs/devtools'
- bun-types-no-globals
- esbuild
- eslint
- less
@@ -6323,7 +6334,6 @@ snapshots:
- optionator
- oxc-parser
- oxlint
- rollup
- sass
- sass-embedded
- stylelint
@@ -6333,20 +6343,19 @@ snapshots:
- terser
- tsx
- typescript
- unloader
- unplugin
- vue-tsc
- webpack
- yaml
'@nuxtjs/robots@6.1.3(d7dabbf3135c346ebe465b397dbcc84c)':
'@nuxtjs/robots@6.1.3(44499e2290ee408d98cc04f71ac84946)':
dependencies:
'@fingerprintjs/botd': 2.0.0
'@nuxt/kit': 4.5.1(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rolldown@1.2.1)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)))
consola: 3.4.2
defu: 6.1.7
h3: 1.15.11
nuxt-site-config: 4.1.2(d7dabbf3135c346ebe465b397dbcc84c)
nuxtseo-shared: 5.3.6(f316e4244987355c955a735cd74b666d)
nuxt-site-config: 4.1.2(44499e2290ee408d98cc04f71ac84946)
nuxtseo-shared: 5.3.6(f5fc6f43a669b1b0cbad6a30003743a2)
pathe: 2.0.3
pkg-types: 2.3.1
ufo: 1.6.4
@@ -6363,13 +6372,13 @@ snapshots:
- vite
- vue
'@nuxtjs/sitemap@8.3.2(d7dabbf3135c346ebe465b397dbcc84c)':
'@nuxtjs/sitemap@8.3.2(44499e2290ee408d98cc04f71ac84946)':
dependencies:
'@nuxt/kit': 4.5.1(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rolldown@1.2.1)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)))
consola: 3.4.2
defu: 6.1.7
nuxt-site-config: 4.1.2(d7dabbf3135c346ebe465b397dbcc84c)
nuxtseo-shared: 5.3.6(f316e4244987355c955a735cd74b666d)
nuxt-site-config: 4.1.2(44499e2290ee408d98cc04f71ac84946)
nuxtseo-shared: 5.3.6(f5fc6f43a669b1b0cbad6a30003743a2)
ofetch: 1.5.1
pathe: 2.0.3
pkg-types: 2.3.1
@@ -6390,7 +6399,7 @@ snapshots:
- vite
- vue
'@nuxtjs/tailwindcss@6.14.0(magicast@0.5.4)(supports-color@10.0.0)(yaml@2.9.0)':
'@nuxtjs/tailwindcss@6.14.0(magicast@0.5.4)(yaml@2.9.0)':
dependencies:
'@nuxt/kit': 3.21.10(magicast@0.5.4)
autoprefixer: 10.5.4(postcss@8.5.25)
@@ -6404,7 +6413,7 @@ snapshots:
pkg-types: 2.3.1
postcss: 8.5.25
postcss-nesting: 13.0.1(postcss@8.5.25)
tailwind-config-viewer: 2.0.4(supports-color@10.0.0)(tailwindcss@3.4.19(yaml@2.9.0))
tailwind-config-viewer: 2.0.4(tailwindcss@3.4.19(yaml@2.9.0))
tailwindcss: 3.4.19(yaml@2.9.0)
ufo: 1.6.4
unctx: 2.5.0
@@ -6901,12 +6910,18 @@ snapshots:
'@types/estree@1.0.9': {}
'@types/geojson@7946.0.16': {}
'@types/hast@3.0.5':
dependencies:
'@types/unist': 3.0.3
'@types/jsesc@2.5.1': {}
'@types/leaflet@1.9.22':
dependencies:
'@types/geojson': 7946.0.16
'@types/linkify-it@5.0.0': {}
'@types/lodash-es@4.17.12':
@@ -6989,9 +7004,9 @@ snapshots:
dependencies:
valibot: 1.4.2(typescript@5.9.3)
'@vercel/nft@1.10.2(rollup@4.62.4)(supports-color@10.0.0)':
'@vercel/nft@1.10.2(rollup@4.62.4)':
dependencies:
'@mapbox/node-pre-gyp': 2.0.0(supports-color@10.0.0)
'@mapbox/node-pre-gyp': 2.0.0
'@rollup/pluginutils': 5.4.0(rollup@4.62.4)
acorn: 8.18.0
acorn-import-attributes: 1.9.5(acorn@8.18.0)
@@ -7024,13 +7039,13 @@ snapshots:
- srvx
- typescript
'@vitejs/plugin-vue-jsx@5.1.6(supports-color@10.0.0)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3))':
'@vitejs/plugin-vue-jsx@5.1.6(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3))':
dependencies:
'@babel/core': 7.29.7(supports-color@10.0.0)
'@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.0.0))
'@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.0.0))(supports-color@10.0.0)
'@babel/core': 7.29.7
'@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7)
'@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7)
'@rolldown/pluginutils': 1.0.1
'@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.29.7(supports-color@10.0.0))(supports-color@10.0.0)
'@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.29.7)
vite: 8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)
vue: 3.5.40(typescript@5.9.3)
transitivePeerDependencies:
@@ -7054,27 +7069,27 @@ snapshots:
'@vue/babel-helper-vue-transform-on@2.0.1': {}
'@vue/babel-plugin-jsx@2.0.1(@babel/core@7.29.7(supports-color@10.0.0))(supports-color@10.0.0)':
'@vue/babel-plugin-jsx@2.0.1(@babel/core@7.29.7)':
dependencies:
'@babel/helper-module-imports': 7.29.7(supports-color@10.0.0)
'@babel/helper-module-imports': 7.29.7
'@babel/helper-plugin-utils': 7.29.7
'@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7(supports-color@10.0.0))
'@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7)
'@babel/template': 7.29.7
'@babel/traverse': 7.29.8(supports-color@10.0.0)
'@babel/traverse': 7.29.8
'@babel/types': 7.29.8
'@vue/babel-helper-vue-transform-on': 2.0.1
'@vue/babel-plugin-resolve-type': 2.0.1(@babel/core@7.29.7(supports-color@10.0.0))(supports-color@10.0.0)
'@vue/babel-plugin-resolve-type': 2.0.1(@babel/core@7.29.7)
'@vue/shared': 3.5.40
optionalDependencies:
'@babel/core': 7.29.7(supports-color@10.0.0)
'@babel/core': 7.29.7
transitivePeerDependencies:
- supports-color
'@vue/babel-plugin-resolve-type@2.0.1(@babel/core@7.29.7(supports-color@10.0.0))(supports-color@10.0.0)':
'@vue/babel-plugin-resolve-type@2.0.1(@babel/core@7.29.7)':
dependencies:
'@babel/code-frame': 7.29.7
'@babel/core': 7.29.7(supports-color@10.0.0)
'@babel/helper-module-imports': 7.29.7(supports-color@10.0.0)
'@babel/core': 7.29.7
'@babel/helper-module-imports': 7.29.7
'@babel/helper-plugin-utils': 7.29.7
'@babel/parser': 7.29.8
'@vue/compiler-sfc': 3.5.40
@@ -7172,13 +7187,13 @@ snapshots:
'@vueuse/metadata@14.4.0': {}
'@vueuse/nuxt@14.4.0(magic-string@1.1.0)(magicast@0.5.4)(nuxt@4.5.1(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.0.0)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.0.0)))(@oxc-project/types@0.142.0)(@parcel/watcher@2.5.6)(@vue/compiler-sfc@3.5.40)(better-sqlite3@12.11.1)(db0@0.3.4(better-sqlite3@12.11.1))(esbuild@0.28.1)(ioredis@5.11.1(supports-color@10.0.0))(lightningcss@1.33.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.1)(rollup@4.62.4))(rollup@4.62.4)(sass-embedded@1.100.0)(sass@1.100.0)(srvx@0.11.22)(supports-color@10.0.0)(terser@5.49.0)(typescript@5.9.3)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(yaml@2.9.0))(oxc-parser@0.140.0)(rolldown@1.2.1)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)))(vue@3.5.40(typescript@5.9.3))':
'@vueuse/nuxt@14.4.0(magic-string@1.1.0)(magicast@0.5.4)(nuxt@4.5.1(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@oxc-project/types@0.142.0)(@parcel/watcher@2.5.6)(@vue/compiler-sfc@3.5.40)(better-sqlite3@12.11.1)(db0@0.3.4(better-sqlite3@12.11.1))(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.33.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.1)(rollup@4.62.4))(rollup@4.62.4)(sass-embedded@1.100.0)(sass@1.100.0)(srvx@0.11.22)(terser@5.49.0)(typescript@5.9.3)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(yaml@2.9.0))(oxc-parser@0.140.0)(rolldown@1.2.1)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)))(vue@3.5.40(typescript@5.9.3))':
dependencies:
'@nuxt/kit': 4.5.1(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rolldown@1.2.1)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)))
'@vueuse/core': 14.4.0(vue@3.5.40(typescript@5.9.3))
'@vueuse/metadata': 14.4.0
local-pkg: 1.2.1
nuxt: 4.5.1(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.0.0)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.0.0)))(@oxc-project/types@0.142.0)(@parcel/watcher@2.5.6)(@vue/compiler-sfc@3.5.40)(better-sqlite3@12.11.1)(db0@0.3.4(better-sqlite3@12.11.1))(esbuild@0.28.1)(ioredis@5.11.1(supports-color@10.0.0))(lightningcss@1.33.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.1)(rollup@4.62.4))(rollup@4.62.4)(sass-embedded@1.100.0)(sass@1.100.0)(srvx@0.11.22)(supports-color@10.0.0)(terser@5.49.0)(typescript@5.9.3)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(yaml@2.9.0)
nuxt: 4.5.1(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@oxc-project/types@0.142.0)(@parcel/watcher@2.5.6)(@vue/compiler-sfc@3.5.40)(better-sqlite3@12.11.1)(db0@0.3.4(better-sqlite3@12.11.1))(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.33.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.1)(rollup@4.62.4))(rollup@4.62.4)(sass-embedded@1.100.0)(sass@1.100.0)(srvx@0.11.22)(terser@5.49.0)(typescript@5.9.3)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(yaml@2.9.0)
vue: 3.5.40(typescript@5.9.3)
transitivePeerDependencies:
- magic-string
@@ -7212,9 +7227,9 @@ snapshots:
acorn@8.18.0: {}
agent-base@7.0.2(supports-color@10.0.0):
agent-base@7.0.2:
dependencies:
debug: 4.4.3(supports-color@10.0.0)
debug: 4.4.3
transitivePeerDependencies:
- supports-color
@@ -7688,17 +7703,13 @@ snapshots:
optionalDependencies:
better-sqlite3: 12.11.1
debug@3.2.7(supports-color@10.0.0):
debug@3.2.7:
dependencies:
ms: 2.1.3
optionalDependencies:
supports-color: 10.0.0
debug@4.4.3(supports-color@10.0.0):
debug@4.4.3:
dependencies:
ms: 2.1.3
optionalDependencies:
supports-color: 10.0.0
decamelize@1.2.0: {}
@@ -8212,10 +8223,10 @@ snapshots:
http-shutdown@1.2.2: {}
https-proxy-agent@7.0.5(supports-color@10.0.0):
https-proxy-agent@7.0.5:
dependencies:
agent-base: 7.0.2(supports-color@10.0.0)
debug: 4.4.3(supports-color@10.0.0)
agent-base: 7.0.2
debug: 4.4.3
transitivePeerDependencies:
- supports-color
@@ -8263,11 +8274,11 @@ snapshots:
ini@4.1.1: {}
ioredis@5.11.1(supports-color@10.0.0):
ioredis@5.11.1:
dependencies:
'@ioredis/commands': 1.10.0
cluster-key-slot: 1.1.1
debug: 4.4.3(supports-color@10.0.0)
debug: 4.4.3
denque: 2.1.0
redis-errors: 1.2.0
redis-parser: 3.0.0
@@ -8403,29 +8414,29 @@ snapshots:
co: 4.6.0
koa-compose: 4.1.0
koa-send@5.0.1(supports-color@10.0.0):
koa-send@5.0.1:
dependencies:
debug: 4.4.3(supports-color@10.0.0)
debug: 4.4.3
http-errors: 1.8.1
resolve-path: 1.4.0
transitivePeerDependencies:
- supports-color
koa-static@5.0.0(supports-color@10.0.0):
koa-static@5.0.0:
dependencies:
debug: 3.2.7(supports-color@10.0.0)
koa-send: 5.0.1(supports-color@10.0.0)
debug: 3.2.7
koa-send: 5.0.1
transitivePeerDependencies:
- supports-color
koa@2.16.4(supports-color@10.0.0):
koa@2.16.4:
dependencies:
accepts: 1.3.8
cache-content-type: 1.0.1
content-disposition: 0.5.2
content-type: 1.0.4
cookies: 0.9.1
debug: 4.4.3(supports-color@10.0.0)
debug: 4.4.3
delegates: 1.0.0
depd: 2.0.0
destroy: 1.2.0
@@ -8455,6 +8466,8 @@ snapshots:
dependencies:
readable-stream: 2.3.8
leaflet@1.9.4: {}
lightningcss-android-arm64@1.32.0:
optional: true
@@ -8811,7 +8824,7 @@ snapshots:
mlly: 1.8.2
pkg-types: 2.3.1
nitropack@2.13.4(@parcel/watcher@2.5.6)(better-sqlite3@12.11.1)(oxc-parser@0.140.0)(rolldown@1.2.1)(srvx@0.11.22)(supports-color@10.0.0)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)):
nitropack@2.13.4(@parcel/watcher@2.5.6)(better-sqlite3@12.11.1)(oxc-parser@0.140.0)(rolldown@1.2.1)(srvx@0.11.22)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)):
dependencies:
'@cloudflare/kv-asset-handler': 0.4.2
'@rollup/plugin-alias': 6.0.0(rollup@4.62.4)
@@ -8821,7 +8834,7 @@ snapshots:
'@rollup/plugin-node-resolve': 16.0.3(rollup@4.62.4)
'@rollup/plugin-replace': 6.0.3(rollup@4.62.4)
'@rollup/plugin-terser': 1.0.0(rollup@4.62.4)
'@vercel/nft': 1.10.2(rollup@4.62.4)(supports-color@10.0.0)
'@vercel/nft': 1.10.2(rollup@4.62.4)
archiver: 7.0.1
c12: 3.3.4(magicast@0.5.4)
chokidar: 5.0.0
@@ -8845,7 +8858,7 @@ snapshots:
h3: 1.15.11
hookable: 5.5.3
httpxy: 0.5.5
ioredis: 5.11.1(supports-color@10.0.0)
ioredis: 5.11.1
jiti: 2.7.0
klona: 2.0.6
knitwork: 1.3.0
@@ -8868,7 +8881,7 @@ snapshots:
scule: 1.3.0
semver: 7.8.5
serve-placeholder: 2.0.2
serve-static: 2.2.1(supports-color@10.0.0)
serve-static: 2.2.1
source-map: 0.7.6
std-env: 4.2.0
ufo: 1.6.4
@@ -8878,7 +8891,7 @@ snapshots:
unenv: 2.0.0-rc.24
unimport: 6.4.0(esbuild@0.28.1)(oxc-parser@0.140.0)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))
unplugin-utils: 0.3.2
unstorage: 1.17.5(db0@0.3.4(better-sqlite3@12.11.1))(ioredis@5.11.1(supports-color@10.0.0))
unstorage: 1.17.5(db0@0.3.4(better-sqlite3@12.11.1))(ioredis@5.11.1)
untyped: 2.0.0
unwasm: 0.5.3
youch: 4.1.1
@@ -8980,14 +8993,14 @@ snapshots:
- rolldown
- unplugin
nuxt-security@2.6.0(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rolldown@1.2.1)(rollup@4.62.4)(supports-color@10.0.0)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))):
nuxt-security@2.6.0(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rolldown@1.2.1)(rollup@4.62.4)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))):
dependencies:
'@nuxt/kit': 4.5.1(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rolldown@1.2.1)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)))
basic-auth: 2.0.1
defu: 6.1.7
nuxt-csurf: 1.6.6(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rolldown@1.2.1)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)))
pathe: 2.0.3
unplugin-remove: 1.0.3(rollup@4.62.4)(supports-color@10.0.0)
unplugin-remove: 1.0.3(rollup@4.62.4)
xss: 1.0.15
transitivePeerDependencies:
- magic-string
@@ -9012,13 +9025,13 @@ snapshots:
- unplugin
- vue
nuxt-site-config@4.1.2(d7dabbf3135c346ebe465b397dbcc84c):
nuxt-site-config@4.1.2(44499e2290ee408d98cc04f71ac84946):
dependencies:
'@nuxt/devalue': 2.0.2
'@nuxt/kit': 4.5.1(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rolldown@1.2.1)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)))
h3: 1.15.11
nuxt-site-config-kit: 4.1.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rolldown@1.2.1)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)))(vue@3.5.40(typescript@5.9.3))
nuxtseo-shared: 5.3.6(f316e4244987355c955a735cd74b666d)
nuxtseo-shared: 5.3.6(f5fc6f43a669b1b0cbad6a30003743a2)
pathe: 2.0.3
pkg-types: 2.3.1
site-config-stack: 4.1.2(vue@3.5.40(typescript@5.9.3))
@@ -9035,16 +9048,16 @@ snapshots:
- vue
- zod
nuxt@4.5.1(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.0.0)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.0.0)))(@oxc-project/types@0.142.0)(@parcel/watcher@2.5.6)(@vue/compiler-sfc@3.5.40)(better-sqlite3@12.11.1)(db0@0.3.4(better-sqlite3@12.11.1))(esbuild@0.28.1)(ioredis@5.11.1(supports-color@10.0.0))(lightningcss@1.33.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.1)(rollup@4.62.4))(rollup@4.62.4)(sass-embedded@1.100.0)(sass@1.100.0)(srvx@0.11.22)(supports-color@10.0.0)(terser@5.49.0)(typescript@5.9.3)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(yaml@2.9.0):
nuxt@4.5.1(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@oxc-project/types@0.142.0)(@parcel/watcher@2.5.6)(@vue/compiler-sfc@3.5.40)(better-sqlite3@12.11.1)(db0@0.3.4(better-sqlite3@12.11.1))(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.33.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.1)(rollup@4.62.4))(rollup@4.62.4)(sass-embedded@1.100.0)(sass@1.100.0)(srvx@0.11.22)(terser@5.49.0)(typescript@5.9.3)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(yaml@2.9.0):
dependencies:
'@dxup/nuxt': 0.5.5(esbuild@0.28.1)(magicast@0.5.4)(oxc-parser@0.140.0)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))
'@nuxt/cli': 3.37.0(@nuxt/schema@4.5.1)(@parcel/watcher@2.5.6)(magicast@0.5.4)(supports-color@10.0.0)
'@nuxt/devtools': 3.4.0(db0@0.3.4(better-sqlite3@12.11.1))(ioredis@5.11.1(supports-color@10.0.0))(magic-string@1.1.0)(oxc-parser@0.140.0)(rolldown@1.2.1)(supports-color@10.0.0)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)))(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3))
'@nuxt/cli': 3.37.0(@nuxt/schema@4.5.1)(@parcel/watcher@2.5.6)(magicast@0.5.4)
'@nuxt/devtools': 3.4.0(db0@0.3.4(better-sqlite3@12.11.1))(ioredis@5.11.1)(magic-string@1.1.0)(oxc-parser@0.140.0)(rolldown@1.2.1)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)))(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3))
'@nuxt/kit': 4.5.1(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rolldown@1.2.1)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)))
'@nuxt/nitro-server': 4.5.1(00f251ea06a52b7897ea2534a2e9c338)
'@nuxt/nitro-server': 4.5.1(4be7fb31efaae22ce2a686c8078eaafa)
'@nuxt/schema': 4.5.1
'@nuxt/telemetry': 2.8.0(@nuxt/kit@4.5.1(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rolldown@1.2.1)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))))
'@nuxt/vite-builder': 4.5.1(90e47d9be2a5bdbf0faa18fc6b372899)
'@nuxt/vite-builder': 4.5.1(d3f4e6fef6236f395bf4699fde3f7ca6)
'@unhead/vue': 3.2.3(@oxc-project/types@0.142.0)(esbuild@0.28.1)(lightningcss@1.33.0)(rolldown@1.2.1)(rollup@4.62.4)(srvx@0.11.22)(typescript@5.9.3)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3))
'@vue/shared': 3.5.40
chokidar: 5.0.0
@@ -9175,7 +9188,7 @@ snapshots:
- xml2js
- yaml
nuxtseo-shared@5.3.6(f316e4244987355c955a735cd74b666d):
nuxtseo-shared@5.3.6(f5fc6f43a669b1b0cbad6a30003743a2):
dependencies:
'@clack/prompts': 1.7.0
'@nuxt/devtools-kit': 4.0.0-alpha.7(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rolldown@1.2.1)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.62.4)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0)))(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))
@@ -9184,7 +9197,7 @@ snapshots:
birpc: 4.0.0
consola: 3.4.2
defu: 6.1.7
nuxt: 4.5.1(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.0.0)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.0.0)))(@oxc-project/types@0.142.0)(@parcel/watcher@2.5.6)(@vue/compiler-sfc@3.5.40)(better-sqlite3@12.11.1)(db0@0.3.4(better-sqlite3@12.11.1))(esbuild@0.28.1)(ioredis@5.11.1(supports-color@10.0.0))(lightningcss@1.33.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.1)(rollup@4.62.4))(rollup@4.62.4)(sass-embedded@1.100.0)(sass@1.100.0)(srvx@0.11.22)(supports-color@10.0.0)(terser@5.49.0)(typescript@5.9.3)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(yaml@2.9.0)
nuxt: 4.5.1(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@oxc-project/types@0.142.0)(@parcel/watcher@2.5.6)(@vue/compiler-sfc@3.5.40)(better-sqlite3@12.11.1)(db0@0.3.4(better-sqlite3@12.11.1))(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.33.0)(magicast@0.5.4)(oxc-parser@0.140.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.1)(rollup@4.62.4))(rollup@4.62.4)(sass-embedded@1.100.0)(sass@1.100.0)(srvx@0.11.22)(terser@5.49.0)(typescript@5.9.3)(vite@8.2.0(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(yaml@2.9.0))(yaml@2.9.0)
nypm: 0.6.9
ofetch: 1.5.1
pathe: 2.0.3
@@ -9195,7 +9208,7 @@ snapshots:
ufo: 1.6.4
vue: 3.5.40(typescript@5.9.3)
optionalDependencies:
nuxt-site-config: 4.1.2(d7dabbf3135c346ebe465b397dbcc84c)
nuxt-site-config: 4.1.2(44499e2290ee408d98cc04f71ac84946)
zod: 4.4.3
transitivePeerDependencies:
- magic-string
@@ -9364,10 +9377,10 @@ snapshots:
exsolve: 1.1.1
pathe: 2.0.3
portfinder@1.0.38(supports-color@10.0.0):
portfinder@1.0.38:
dependencies:
async: 3.2.6
debug: 4.4.3(supports-color@10.0.0)
debug: 4.4.3
transitivePeerDependencies:
- supports-color
@@ -9911,9 +9924,9 @@ snapshots:
semver@7.8.5: {}
send@1.2.1(supports-color@10.0.0):
send@1.2.1:
dependencies:
debug: 4.4.3(supports-color@10.0.0)
debug: 4.4.3
encodeurl: 2.0.0
escape-html: 1.0.3
etag: 1.8.1
@@ -9935,12 +9948,12 @@ snapshots:
dependencies:
defu: 6.1.7
serve-static@2.2.1(supports-color@10.0.0):
serve-static@2.2.1:
dependencies:
encodeurl: 2.0.0
escape-html: 1.0.3
parseurl: 1.3.3
send: 1.2.1(supports-color@10.0.0)
send: 1.2.1
transitivePeerDependencies:
- supports-color
@@ -10015,13 +10028,13 @@ snapshots:
simple-concat: 1.0.1
optional: true
simple-git@3.36.0(supports-color@10.0.0):
simple-git@3.36.0:
dependencies:
'@kwsites/file-exists': 1.1.1(supports-color@10.0.0)
'@kwsites/file-exists': 1.1.1
'@kwsites/promise-deferred': 1.1.1
'@simple-git/args-pathspec': 1.0.3
'@simple-git/argv-parser': 1.1.1
debug: 4.4.3(supports-color@10.0.0)
debug: 4.4.3
transitivePeerDependencies:
- supports-color
@@ -10211,15 +10224,15 @@ snapshots:
tagged-tag@1.0.0: {}
tailwind-config-viewer@2.0.4(supports-color@10.0.0)(tailwindcss@3.4.19(yaml@2.9.0)):
tailwind-config-viewer@2.0.4(tailwindcss@3.4.19(yaml@2.9.0)):
dependencies:
'@koa/router': 12.0.1(supports-color@10.0.0)
'@koa/router': 12.0.1
commander: 6.0.0
fs-extra: 9.0.1
koa: 2.16.4(supports-color@10.0.0)
koa-static: 5.0.0(supports-color@10.0.0)
koa: 2.16.4
koa-static: 5.0.0
open: 7.4.2
portfinder: 1.0.38(supports-color@10.0.0)
portfinder: 1.0.38
replace-in-file: 6.1.0
tailwindcss: 3.4.19(yaml@2.9.0)
transitivePeerDependencies:
@@ -10488,12 +10501,12 @@ snapshots:
universalify@2.0.1: {}
unplugin-remove@1.0.3(rollup@4.62.4)(supports-color@10.0.0):
unplugin-remove@1.0.3(rollup@4.62.4):
dependencies:
'@babel/core': 7.29.7(supports-color@10.0.0)
'@babel/core': 7.29.7
'@babel/generator': 7.29.8
'@babel/parser': 7.29.8
'@babel/traverse': 7.29.8(supports-color@10.0.0)
'@babel/traverse': 7.29.8
'@rollup/pluginutils': 5.4.0(rollup@4.62.4)
magic-string: 0.30.11
unplugin: 1.12.0
@@ -10536,7 +10549,7 @@ snapshots:
escape-string-regexp: 5.0.0
ufo: 1.6.4
unstorage@1.17.5(db0@0.3.4(better-sqlite3@12.11.1))(ioredis@5.11.1(supports-color@10.0.0)):
unstorage@1.17.5(db0@0.3.4(better-sqlite3@12.11.1))(ioredis@5.11.1):
dependencies:
anymatch: 3.1.3
chokidar: 5.0.0
@@ -10548,7 +10561,7 @@ snapshots:
ufo: 1.6.4
optionalDependencies:
db0: 0.3.4(better-sqlite3@12.11.1)
ioredis: 5.11.1(supports-color@10.0.0)
ioredis: 5.11.1
untun@0.2.2: {}
@@ -59,6 +59,8 @@
| 字段 | 类型 | 描述 |
| --- | --- | --- |
| `ip` | String | 请求方公网 IP(本节点视角) |
| `lemondb` | Object | `country`/`country_code`/`administrative_area`/`city`/`district`/`division_code`/`isp`/`type`/`latitude`/`longitude`/`coord_system`(自家 LemonDB 月更库:全球打底 + 中国省市县高精度层,坐标统一 WGS-84,上高德底图需自转 GCJ-02) |
| `lemondb_asn` | Object | `asn`/`org`(GeoLite2-ASN 打底 + APNIC CN 精修) |
| `ip2region` | Object | `country`/`administrative_area`/`city`(部分记录另带 `isp`) |
| `ip2location` | Object | `country`/`country_code`/`administrative_area`/`city`/`district`/`zipcode`/`latitude`/`longitude`/`timezone`/`isp`/`asn`/`usagetype` |
| `ip2location_asn` | Object | `asn`/`as` |
+43
View File
@@ -0,0 +1,43 @@
// WGS84 → GCJ-02(火星坐标)互转:国内地图底图(高德/腾讯)均为 GCJ-02 坐标系,
// IP 库返回的经纬度是 WGS84,直接叠点会偏移 100~700 米,须先转换再打点。
// 境外坐标原样返回(GCJ-02 仅在中国境内有偏移)。
const PI = Math.PI
const A = 6378245 // 长半轴
const EE = 0.00669342162296594323 // 偏心率平方
function outOfChina(lat: number, lng: number): boolean {
return lng < 72.004 || lng > 137.8347 || lat < 0.8293 || lat > 55.8271
}
function transformLat(x: number, y: number): number {
let ret =
-100 + 2 * x + 3 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x))
ret += ((20 * Math.sin(6 * x * PI) + 20 * Math.sin(2 * x * PI)) * 2) / 3
ret += ((20 * Math.sin(y * PI) + 40 * Math.sin((y / 3) * PI)) * 2) / 3
ret += ((160 * Math.sin((y / 12) * PI) + 320 * Math.sin((y * PI) / 30)) * 2) / 3
return ret
}
function transformLng(x: number, y: number): number {
let ret =
300 + x + 2 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x))
ret += ((20 * Math.sin(6 * x * PI) + 20 * Math.sin(2 * x * PI)) * 2) / 3
ret += ((20 * Math.sin(x * PI) + 40 * Math.sin((x / 3) * PI)) * 2) / 3
ret += ((150 * Math.sin((x / 12) * PI) + 300 * Math.sin((x / 30) * PI)) * 2) / 3
return ret
}
/** WGS84 → GCJ-02;境外坐标原样返回 */
export function wgs84ToGcj02(lat: number, lng: number): { lat: number; lng: number } {
if (outOfChina(lat, lng)) return { lat, lng }
let dLat = transformLat(lng - 105, lat - 35)
let dLng = transformLng(lng - 105, lat - 35)
const radLat = (lat / 180) * PI
let magic = Math.sin(radLat)
magic = 1 - EE * magic * magic
const sqrtMagic = Math.sqrt(magic)
dLat = (dLat * 180) / ((A * (1 - EE)) / sqrtMagic * PI)
dLng = (dLng * 180) / (A / sqrtMagic * Math.cos(radLat) * PI)
return { lat: lat + dLat, lng: lng + dLng }
}
+1
View File
@@ -0,0 +1 @@
{"110101":[116.416334,39.928359],"110102":[116.36585,39.9126],"110105":[116.443136,39.921444],"110106":[116.286726,39.858538],"110107":[116.223015,39.906304],"110108":[116.2977,39.959893],"110109":[116.101668,39.940842],"110111":[116.143426,39.748889],"110112":[116.72923,39.916403],"110113":[116.661474,40.149891],"110114":[116.231034,40.220952],"110115":[116.341483,39.726917],"110116":[116.631974,40.317003],"110117":[117.121589,40.140805],"110118":[116.843351,40.377058],"110119":[115.974609,40.457033],"120101":[117.214713,39.116884],"120102":[117.251584,39.128294],"120103":[117.223379,39.109679],"120104":[117.150638,39.138551],"120105":[117.196874,39.148018],"120106":[117.151566,39.167349],"120110":[117.313567,39.086789],"120111":[117.008994,39.141811],"120112":[117.356683,38.936971],"120113":[117.135614,39.224638],"120114":[117.04456,39.384108],"120115":[117.309748,39.717054],"120116":[117.69641,39.017809],"120117":[117.826674,39.329749],"120118":[116.975474,38.947772],"120119":[117.408432,40.046544],"130102":[114.538955,38.03682],"130104":[114.45004,38.025245],"130105":[114.463904,38.050749],"130107":[114.062258,38.065446],"130108":[114.531599,38.007002],"130109":[114.846562,38.022177],"130110":[114.313559,38.086536],"130111":[114.647922,37.900915],"130121":[114.145669,38.032366],"130123":[114.57043,38.14699],"130125":[114.553044,38.437535],"130126":[114.383013,38.307908],"130127":[114.611659,37.615905],"130128":[115.20089,38.184572],"130129":[114.386114,37.666549],"130130":[114.976256,38.178852],"130131":[114.186007,38.260288],"130132":[114.525508,37.767332],"130133":[114.775914,37.756935],"130181":[115.217626,37.943239],"130183":[115.044141,38.033937],"130184":[114.683745,38.343952],"130202":[118.154348,39.624988],"130203":[118.201085,39.625079],"130204":[118.447134,39.73392],"130205":[118.262246,39.671634],"130207":[118.085169,39.576031],"130208":[118.162426,39.832919],"130209":[118.460197,39.273528],"130224":[118.68363,39.520272],"130225":[118.91245,39.425748],"130227":[118.31467,40.141486],"130229":[117.738196,39.901827],"130281":[117.965878,40.189119],"130283":[118.701021,39.998861],"130284":[118.70301,39.740963],"130302":[119.564962,39.94756],"130303":[119.775187,39.978882],"130304":[119.48449,39.834912],"130306":[119.244847,39.876253],"130321":[118.94985,40.407473],"130322":[119.199846,39.699677],"130324":[118.891931,39.892564],"130402":[114.531062,36.59457],"130403":[114.492875,36.636434],"130404":[114.462581,36.638879],"130406":[114.212571,36.419298],"130407":[114.800199,36.548545],"130408":[114.536626,36.74188],"130423":[114.585514,36.322504],"130424":[114.670094,36.445331],"130425":[115.147985,36.286406],"130426":[113.692157,36.586073],"130427":[114.373965,36.37507],"130430":[115.200049,36.811783],"130431":[114.889951,36.911525],"130432":[114.921185,36.476727],"130433":[115.281818,36.548295],"130434":[114.939217,36.35926],"130435":[114.957549,36.766347],"130481":[114.203685,36.69759],"130502":[114.507443,37.071314],"130503":[114.468229,37.093798],"130505":[114.671339,37.121958],"130506":[114.683683,37.005626],"130522":[114.498651,37.444512],"130523":[114.512226,37.287612],"130524":[114.693447,37.48288],"130525":[114.770509,37.351232],"130528":[114.940006,37.624524],"130529":[115.037884,37.221293],"130530":[115.251005,37.520891],"130531":[115.142766,37.074795],"130532":[115.03008,37.063771],"130533":[115.266829,36.975164],"130534":[115.665081,37.0451],"130535":[115.501258,36.871312],"130581":[115.40866,37.358907],"130582":[114.503023,36.855548],"130602":[115.458671,38.877318],"130606":[115.497153,38.883528],"130607":[115.322246,38.949732],"130608":[115.48988,38.76527],"130609":[115.655772,39.018781],"130623":[115.713651,39.394305],"130624":[114.195118,38.849221],"130626":[115.808183,39.263219],"130627":[114.982968,38.748477],"130628":[115.779149,38.700846],"130629":[115.861635,39.043321],"130630":[114.694416,39.360622],"130631":[115.15542,38.696221],"130632":[115.935688,38.936102],"130633":[115.497487,39.350219],"130634":[114.740476,38.614409],"130635":[115.583701,38.488064],"130636":[115.135133,38.837988],"130637":[115.464295,38.458048],"130638":[116.108624,38.994825],"130681":[115.97444,39.485684],"130682":[114.990321,38.516746],"130683":[115.327088,38.418985],"130684":[115.873612,39.327233],"130702":[114.894114,40.788472],"130703":[114.868604,40.819553],"130705":[115.099515,40.609444],"130706":[115.287127,40.502628],"130708":[114.740584,40.767377],"130709":[115.282345,40.974741],"130722":[114.719927,41.159039],"130723":[114.60018,41.853016],"130724":[115.688544,41.670497],"130725":[113.968763,41.076588],"130726":[114.589136,39.840154],"130727":[114.150267,40.104303],"130728":[114.386488,40.67527],"130730":[115.517868,40.415625],"130731":[115.196835,40.382681],"130732":[115.831256,40.913348],"130802":[117.943121,40.974679],"130803":[117.799588,40.959426],"130804":[117.659341,40.546424],"130821":[118.174166,40.768082],"130822":[117.500558,40.417358],"130824":[117.332652,40.941644],"130825":[1Line truncated
+16
View File
@@ -0,0 +1,16 @@
package ipdb
import "testing"
func TestDivisionCoords(t *testing.T) {
if len(divisionCoords) < 2800 {
t.Fatalf("divisionCoords 区划数不足: %d", len(divisionCoords))
}
c, ok := divisionCoords["110101"]
if !ok {
t.Fatal("缺少 110101(东城区)")
}
if c[0] < 73 || c[0] > 136 || c[1] < 3 || c[1] > 54 {
t.Fatalf("110101 坐标超出中国范围: %v", c)
}
}
+84 -52
View File
@@ -18,7 +18,10 @@ import (
)
type downloadTask struct {
url string
// urls 镜像列表,按优先级依次尝试。full.txt/short.txt 原来只走
// cdn.jsdelivr.net——国内移动网络下 v4/v6 都被 RST(其余走 ghproxy 的
// 文件全通),GeoCN 的行政区划数据随之永久缺失,故补齐镜像。
urls []string
name string
}
@@ -80,18 +83,38 @@ func PullDatabase(ghproxy string) error {
defer pullMu.Unlock()
tasks := []downloadTask{
{"https://cdn.jsdelivr.net/gh/ljxi/GeoCN@main/data/full.txt", "full.txt"},
{"https://cdn.jsdelivr.net/gh/ljxi/GeoCN@main/data/short.txt", "short.txt"},
{ghproxy + "https://raw.githubusercontent.com/lionsoul2014/ip2region/master/data/ip2region_v4.xdb", "ip2region_v4.xdb"},
{ghproxy + "https://raw.githubusercontent.com/lionsoul2014/ip2region/master/data/ip2region_v6.xdb", "ip2region_v6.xdb"},
{ghproxy + "https://raw.githubusercontent.com/nmgliangwei/qqwry.ipdb/main/qqwry.ipdb", "qqwry.ipdb"},
{ghproxy + "https://github.com/P3TERX/GeoLite.mmdb/raw/download/GeoLite2-City.mmdb", "GeoLite2-City.mmdb"},
{ghproxy + "https://github.com/P3TERX/GeoLite.mmdb/raw/download/GeoLite2-ASN.mmdb", "GeoLite2-ASN.mmdb"},
{ghproxy + "https://github.com/ljxi/GeoCN/releases/latest/download/GeoCN.mmdb", "GeoCN.mmdb"},
{ghproxy + "https://raw.githubusercontent.com/wp-statistics/DbIP-City-lite/master/dbip-city-lite.mmdb.gz", "dbip-city-lite.mmdb.gz"},
{ghproxy + "https://github.com/jcjc-dev/mmdb-latest/releases/download/dbip-latest/dbip-asn-lite.mmdb", "dbip-asn-lite.mmdb"},
{ghproxy + "https://github.com/nomdn/ip2location/releases/latest/download/IP2LOCATION-LITE-DB11.IPV6.BIN.ZIP", "IP2LOCATION-LITE-DB11.IPV6.BIN.ZIP"},
{ghproxy + "https://github.com/nomdn/ip2location/releases/latest/download/IP2LOCATION-LITE-ASN.IPV6.BIN.zip", "IP2LOCATION-LITE-ASN.IPV6.BIN.zip"},
// full/short.txt(GeoCN 行政区划):主通道与其他文件统一走 ghproxy+raw
// 加速(注意 raw 的分支是路径段,不带 @;jsdelivr 的 @main 写法在 raw 上是 404)。
// jsdelivr 系(含 fastly/gcore 镜像)降为兜底——国内移动网络下会被 RST,
// 但海外节点可达。实测主通道 65KB 秒回,兜底仅在主通道故障时启用。
{[]string{
ghproxy + "https://raw.githubusercontent.com/ljxi/GeoCN/main/data/full.txt",
"https://cdn.jsdelivr.net/gh/ljxi/GeoCN@main/data/full.txt",
"https://fastly.jsdelivr.net/gh/ljxi/GeoCN@main/data/full.txt",
"https://gcore.jsdelivr.net/gh/ljxi/GeoCN@main/data/full.txt",
}, "full.txt"},
{[]string{
ghproxy + "https://raw.githubusercontent.com/ljxi/GeoCN/main/data/short.txt",
"https://cdn.jsdelivr.net/gh/ljxi/GeoCN@main/data/short.txt",
"https://fastly.jsdelivr.net/gh/ljxi/GeoCN@main/data/short.txt",
"https://gcore.jsdelivr.net/gh/ljxi/GeoCN@main/data/short.txt",
}, "short.txt"},
{[]string{ghproxy + "https://raw.githubusercontent.com/lionsoul2014/ip2region/master/data/ip2region_v4.xdb"}, "ip2region_v4.xdb"},
{[]string{ghproxy + "https://raw.githubusercontent.com/lionsoul2014/ip2region/master/data/ip2region_v6.xdb"}, "ip2region_v6.xdb"},
{[]string{ghproxy + "https://raw.githubusercontent.com/nmgliangwei/qqwry.ipdb/main/qqwry.ipdb"}, "qqwry.ipdb"},
{[]string{ghproxy + "https://github.com/P3TERX/GeoLite.mmdb/raw/download/GeoLite2-City.mmdb"}, "GeoLite2-City.mmdb"},
{[]string{ghproxy + "https://github.com/P3TERX/GeoLite.mmdb/raw/download/GeoLite2-ASN.mmdb"}, "GeoLite2-ASN.mmdb"},
{[]string{ghproxy + "https://github.com/ljxi/GeoCN/releases/latest/download/GeoCN.mmdb"}, "GeoCN.mmdb"},
{[]string{ghproxy + "https://raw.githubusercontent.com/wp-statistics/DbIP-City-lite/master/dbip-city-lite.mmdb.gz"}, "dbip-city-lite.mmdb.gz"},
{[]string{ghproxy + "https://github.com/jcjc-dev/mmdb-latest/releases/download/dbip-latest/dbip-asn-lite.mmdb"}, "dbip-asn-lite.mmdb"},
{[]string{ghproxy + "https://github.com/nomdn/ip2location/releases/latest/download/IP2LOCATION-LITE-DB11.IPV6.BIN.ZIP"}, "IP2LOCATION-LITE-DB11.IPV6.BIN.ZIP"},
{[]string{ghproxy + "https://github.com/nomdn/ip2location/releases/latest/download/IP2LOCATION-LITE-ASN.IPV6.BIN.zip"}, "IP2LOCATION-LITE-ASN.IPV6.BIN.zip"},
// 自家的 LemonDB(nomdn/LemonDB 月度构建):全球打底 + 中国省市县高精度层,
// CN 段精度远高于上面所有免费源,放在最后当压轴。
// 拉取节奏与其余库一致:启动缺文件即拉 + 每 24h 条件请求检查一次——
// release 月更,ETag 变了才真正下载,节点无论中断多久,恢复后一天内追平最新版。
{[]string{ghproxy + "https://github.com/nomdn/LemonDB/releases/latest/download/LEMON-LOCATION.mmdb"}, "LEMON-LOCATION.mmdb"},
{[]string{ghproxy + "https://github.com/nomdn/LemonDB/releases/latest/download/LEMON-ASN.mmdb"}, "LEMON-ASN.mmdb"},
}
tmpDir := "./tmp"
@@ -144,8 +167,13 @@ func PullDatabase(ghproxy string) error {
slog.Info("Copying downloaded files to working directory...")
for i, t := range tasks {
if errs[i] != nil {
// 下载失败(含传输中断留下的残缺文件)不覆盖现有数据库
slog.Warn("Skipping failed download, keep existing file", "file", t.name)
// 下载失败(含传输中断留下的残缺文件)不覆盖现有数据库。
// 本地确实有旧文件 → 继续用它;连旧文件都没有 → 明确说数据库会降级
if fileExists("./" + finalFileName(t.name)) {
slog.Warn("Download failed, keep existing local copy", "file", t.name)
} else {
slog.Warn("Download failed and no local copy exists, related DB stays degraded until next sync", "file", t.name)
}
continue
}
if notModified[i] {
@@ -196,55 +224,59 @@ func PullDatabase(ghproxy string) error {
return nil
}
// downloadWithRetry 带重试的条件下载。返回 (新凭证, 是否 304 跳过, 错误)。
// downloadWithRetry 带镜像轮试与重试的条件下载。返回 (新凭证, 是否 304 跳过, 错误)。
//
// 镜像轮试:urls 按优先级依次尝试,每个镜像最多 2 次(首次 + 一次快速重试),
// 全部镜像失败后整轮再来一轮——单个镜像被墙/抖动不再拖死整个文件。
// 超时 10 分钟:这是**整个请求**(含响应体下载)的超时,GeoLite2-City 这类几十 MB
// 的大文件需要持续 ~1Mbps 以上才能在 10 分钟内完成;原实现的 60s 在慢链路上
// 会让大文件 3 次重试全部超时失败。
// 会让重试全部超时失败。
func downloadWithRetry(t downloadTask, val dbValidator) (dbValidator, bool, error) {
const maxRetries = 3
const attemptsPerMirror = 2
client := resty.New().SetTimeout(10 * time.Minute).SetOutputDirectory("./tmp")
defer client.Close()
var lastErr error
for attempt := 1; attempt <= maxRetries; attempt++ {
slog.Info("Downloading...", "file", t.name, "attempt", attempt)
for _, u := range t.urls {
for attempt := 1; attempt <= attemptsPerMirror; attempt++ {
slog.Info("Downloading...", "file", t.name, "url", u, "attempt", attempt)
req := client.R().SetOutputFileName(t.name).SetSaveResponse(true)
if val.ETag != "" {
req.SetHeader("If-None-Match", val.ETag)
}
if val.LastModified != "" {
req.SetHeader("If-Modified-Since", val.LastModified)
}
req := client.R().SetOutputFileName(t.name).SetSaveResponse(true)
if val.ETag != "" {
req.SetHeader("If-None-Match", val.ETag)
}
if val.LastModified != "" {
req.SetHeader("If-Modified-Since", val.LastModified)
}
resp, err := req.Get(t.url)
if err != nil {
lastErr = fmt.Errorf("request failed: %w", err)
slog.Warn("Download attempt failed", "file", t.name, "attempt", attempt, "error", err)
// 传输中断会在输出文件里留下残缺内容,删掉避免被当作下载成功覆盖现有数据库
os.Remove(filepath.Join("./tmp", t.name))
time.Sleep(time.Duration(attempt) * time.Second)
continue
}
if resp.StatusCode() == http.StatusNotModified {
slog.Info("Not modified since last sync", "file", t.name)
return val, true, nil
}
if resp.IsError() {
lastErr = fmt.Errorf("HTTP %d", resp.StatusCode())
slog.Warn("Download attempt failed", "file", t.name, "attempt", attempt, "status", resp.StatusCode())
os.Remove(filepath.Join("./tmp", t.name))
time.Sleep(time.Duration(attempt) * time.Second)
continue
}
resp, err := req.Get(u)
if err != nil {
lastErr = fmt.Errorf("request failed: %w", err)
slog.Warn("Download attempt failed", "file", t.name, "url", u, "attempt", attempt, "error", err)
// 传输中断会在输出文件里留下残缺内容,删掉避免被当作下载成功覆盖现有数据库
os.Remove(filepath.Join("./tmp", t.name))
time.Sleep(time.Duration(attempt) * time.Second)
continue
}
if resp.StatusCode() == http.StatusNotModified {
slog.Info("Not modified since last sync", "file", t.name)
return val, true, nil
}
if resp.IsError() {
lastErr = fmt.Errorf("HTTP %d", resp.StatusCode())
slog.Warn("Download attempt failed", "file", t.name, "url", u, "attempt", attempt, "status", resp.StatusCode())
os.Remove(filepath.Join("./tmp", t.name))
time.Sleep(time.Duration(attempt) * time.Second)
continue
}
nv := dbValidator{
ETag: resp.Header().Get("ETag"),
LastModified: resp.Header().Get("Last-Modified"),
nv := dbValidator{
ETag: resp.Header().Get("ETag"),
LastModified: resp.Header().Get("Last-Modified"),
}
slog.Info("Downloaded", "file", t.name, "url", u)
return nv, false, nil
}
slog.Info("Downloaded", "file", t.name)
return nv, false, nil
}
return val, false, lastErr
}
+152 -1
View File
@@ -2,6 +2,8 @@ package ipdb
import (
"bufio"
_ "embed"
"encoding/json"
"fmt"
"log/slog"
"math/rand"
@@ -21,6 +23,23 @@ import (
"resty.dev/v3"
)
// division-coords.json:GB/T 2260 六位区划代码 → 政府驻地 [lng, lat](GCJ-02),
// 数据来自 AreaCity-JsSpider-StatsGov(高德采集,随版本更新可重新生成)。
// 供 GeoCN 按区划代码打出"政府驻地"坐标点。
//
//go:embed division-coords.json
var divisionCoordsRaw string
// divisionCoords 编译期内建,避免运行时读盘
var divisionCoords = func() map[string][2]float64 {
m := make(map[string][2]float64)
if err := json.Unmarshal([]byte(divisionCoordsRaw), &m); err != nil {
slog.Error("failed to parse division-coords.json", "error", err)
return make(map[string][2]float64)
}
return m
}()
var (
ip2region *service.Ip2Region
ip2regionMu sync.RWMutex
@@ -88,6 +107,30 @@ type GeoCNResult struct {
Region string `json:"administrative_area,omitempty"`
City string `json:"city,omitempty"`
District string `json:"district,omitempty"`
// 政府驻地坐标(GCJ-02,按 division_code 查内置对照表);
// 缺表/新设区划查不到时不输出坐标字段
Latitude float64 `json:"latitude,omitempty"`
Longitude float64 `json:"longitude,omitempty"`
}
// LemonDBResult LEMON-LOCATION.mmdb 的查询结果。记录是 GeoLite2 兼容骨架
// (country/subdivisions/city/location)加中国层扩展(district / division_code /
// isp / type),境外段无中国层字段时输出 omitempty 省略。
// 坐标全库统一 WGS-84(国际标准),coord_system 恒为 wgs84;上高德等 GCJ 底图
// 由读方自行转换(前端已有 wgs84ToGcj02)。
type LemonDBResult struct {
Country string `json:"country"`
CountryCode string `json:"country_code,omitempty"`
Region string `json:"administrative_area,omitempty"`
RegionISO string `json:"administrative_area_iso,omitempty"`
City string `json:"city,omitempty"`
District string `json:"district,omitempty"`
ISP string `json:"isp,omitempty"`
Type string `json:"type,omitempty"`
DivisionCode string `json:"division_code,omitempty"`
Latitude float64 `json:"latitude,omitempty"`
Longitude float64 `json:"longitude,omitempty"`
CoordSystem string `json:"coord_system,omitempty"`
}
type BilibiliIPQueryResponse struct {
@@ -285,6 +328,9 @@ var mmdbFiles = map[string]string{
"geocn": "GeoCN.mmdb",
"dbip_city": "dbip-city-lite.mmdb",
"dbip_asn": "dbip-asn-lite.mmdb",
// LemonDB:全球定位 + 全球 ASN(月更,来自 nomdn/LemonDB release)
"lemondb_location": "LEMON-LOCATION.mmdb",
"lemondb_asn": "LEMON-ASN.mmdb",
}
// loadMMDB 重建全部 mmdb 并原子换指针。个别文件加载失败时沿用旧库(数据旧一点
@@ -477,11 +523,100 @@ func searchGeoCN(ip string) (*GeoCNResult, error) {
result.Region = province
result.City = city
result.District = district
// 政府驻地打点:按区划代码查内置对照表(GCJ-02),供前端地图与 GeoCN 打点
if c, ok := divisionCoords[strconv.Itoa(code)]; ok {
result.Longitude = c[0]
result.Latitude = c[1]
}
}
return result, nil
}
// searchLemonDB 查 LemonDB 全球定位库(记录结构见 LemonDBResult 注释)。
// 坐标统一 WGS-84,coord_system 仍透出(恒为 wgs84),读方按需自转 GCJ。
func searchLemonDB(ip string) (*LemonDBResult, error) {
mmdbMu.RLock()
db, ok := mmdbDBs["lemondb_location"]
mmdbMu.RUnlock()
if !ok {
return nil, fmt.Errorf("lemondb_location not loaded")
}
addr, err := netip.ParseAddr(ip)
if err != nil {
return nil, err
}
var record struct {
Country struct {
ISOCode string `maxminddb:"iso_code"`
Names map[string]string `maxminddb:"names"`
} `maxminddb:"country"`
Subdivisions []struct {
ISOCode string `maxminddb:"iso_code"`
Names map[string]string `maxminddb:"names"`
} `maxminddb:"subdivisions"`
City struct {
Names map[string]string `maxminddb:"names"`
} `maxminddb:"city"`
District struct {
Names map[string]string `maxminddb:"names"`
} `maxminddb:"district"`
DivisionCode uint32 `maxminddb:"division_code"`
ISP string `maxminddb:"isp"`
Type string `maxminddb:"type"`
Location struct {
Latitude float64 `maxminddb:"latitude"`
Longitude float64 `maxminddb:"longitude"`
CoordSystem string `maxminddb:"coord_system"`
} `maxminddb:"location"`
}
if err := db.Lookup(addr).Decode(&record); err != nil {
return nil, err
}
// 取名统一 zh-CN 优先、en 兜底
zh := func(m map[string]string) string {
if v, ok := m["zh-CN"]; ok && v != "" {
return v
}
return m["en"]
}
result := &LemonDBResult{
CountryCode: record.Country.ISOCode,
ISP: record.ISP,
Type: record.Type,
DivisionCode: fmt.Sprintf("%d", record.DivisionCode),
CoordSystem: record.Location.CoordSystem,
Latitude: record.Location.Latitude,
Longitude: record.Location.Longitude,
}
result.Country = zh(record.Country.Names)
if len(record.Subdivisions) > 0 {
result.Region = zh(record.Subdivisions[0].Names)
result.RegionISO = record.Subdivisions[0].ISOCode
}
result.City = zh(record.City.Names)
result.District = zh(record.District.Names)
if result.DivisionCode == "0" {
result.DivisionCode = ""
}
return result, nil
}
// searchLemonDBASN 查 LemonDB 全球 ASN 库(记录键与 GeoLite2-ASN 相同,直接复用解析)
func searchLemonDBASN(ip string) (*MMDBASNResult, error) {
mmdbMu.RLock()
db, ok := mmdbDBs["lemondb_asn"]
mmdbMu.RUnlock()
if !ok {
return nil, fmt.Errorf("lemondb_asn not loaded")
}
return searchMMDBASNIn(db, ip)
}
// loadBilibiliEntry 查缓存(按正/负各自的 TTL 判新鲜),过期顺手删除
func loadBilibiliEntry(ip string) (bilibiliCacheEntry, bool) {
if cached, ok := bilibiliCache.Load(ip); ok {
@@ -638,7 +773,7 @@ func Init(ghproxy string) {
}()
}
var allDatabases = []string{"ip2region", "ip2location", "ip2location_asn", "qqwry", "maxmind_city", "maxmind_asn", "dbip_asn", "geocn", "dbip_city", "bilibili"}
var allDatabases = []string{"lemondb", "lemondb_asn", "ip2region", "ip2location", "ip2location_asn", "qqwry", "maxmind_city", "maxmind_asn", "dbip_asn", "geocn", "dbip_city", "bilibili"}
func SearchIP(ip string, databases ...string) map[string]interface{} {
if len(databases) == 0 {
@@ -803,6 +938,22 @@ func SearchIP(ip string, databases ...string) map[string]interface{} {
result["dbip_city"] = dbip
}
case "lemondb":
lemon, err := searchLemonDB(ip)
if err != nil {
result["lemondb"] = "error: " + err.Error()
} else {
result["lemondb"] = lemon
}
case "lemondb_asn":
asn, err := searchLemonDBASN(ip)
if err != nil {
result["lemondb_asn"] = "error: " + err.Error()
} else {
result["lemondb_asn"] = asn
}
default:
result[name] = "unknown database"
}
+16 -4
View File
@@ -738,12 +738,13 @@ func normalizeASN(s string) string {
return "AS" + s
}
// resolveASN 从 SearchIP 结果中按优先级提取可用 ASN(maxmind → dbip → ip2location),返回 "ASxxxx"。
// resolveASN 从 SearchIP 结果中按优先级提取可用 ASN(lemondb → maxmind → dbip → ip2location),返回 "ASxxxx"。
//
// 之所以要兜底:此前 whois 段只认 maxmind_asn,某个 IP 一旦未命中该库,整段 ASN WHOIS 就凭空消失,
// 哪怕 dbip_asn / ip2location_asn 明明有数据。
// 哪怕 dbip_asn / ip2location_asn 明明有数据。lemondb_asn 打头:GeoLite2 底子 + APNIC CN 精修,
// 覆盖面与 maxmind 相同、CN 组织名更准。
func resolveASN(result map[string]interface{}) string {
for _, key := range []string{"maxmind_asn", "dbip_asn"} {
for _, key := range []string{"lemondb_asn", "maxmind_asn", "dbip_asn"} {
if r, ok := result[key].(*ipdb.MMDBASNResult); ok {
if asn := normalizeASN(r.ASN); asn != "" {
return asn
@@ -801,7 +802,7 @@ func asnLookupHandler(c fiber.Ctx) error {
})
}
result := ipdb.SearchIP(ip, "maxmind_asn", "dbip_asn", "ip2location_asn")
result := ipdb.SearchIP(ip, "lemondb_asn", "maxmind_asn", "dbip_asn", "ip2location_asn")
asnResult := map[string]interface{}{
"ip": ip,
@@ -818,6 +819,17 @@ func asnLookupHandler(c fiber.Ctx) error {
}
}
if lemonASN, ok := result["lemondb_asn"].(*ipdb.MMDBASNResult); ok {
asnResult["lemondb_asn"] = map[string]string{
"asn": lemonASN.ASN,
"org": lemonASN.Org,
}
} else if errStr, ok := result["lemondb_asn"].(string); ok {
asnResult["lemondb_asn"] = map[string]string{
"error": errStr,
}
}
if maxmindASN, ok := result["maxmind_asn"].(*ipdb.MMDBASNResult); ok {
asnResult["geolite2_asn"] = map[string]string{
"asn": maxmindASN.ASN,
+6 -1
View File
@@ -249,13 +249,18 @@ func wsProbe(apiType, raw string, query map[string]string) (int, any) {
if raw == "" {
return 400, map[string]string{"error": "IP parameter is required"}
}
result := ipdb.SearchIP(raw, "maxmind_asn", "dbip_asn", "ip2location_asn")
result := ipdb.SearchIP(raw, "lemondb_asn", "maxmind_asn", "dbip_asn", "ip2location_asn")
asnResult := map[string]any{"ip": raw}
if ip2locASN, ok := result["ip2location_asn"].(map[string]string); ok && ip2locASN["asn"] != "" {
asnResult["ip2location_asn"] = map[string]string{"asn": ip2locASN["asn"], "as": ip2locASN["as"]}
} else if errStr, ok := result["ip2location_asn"].(string); ok {
asnResult["ip2location_asn"] = map[string]string{"error": errStr}
}
if lemonASN, ok := result["lemondb_asn"].(*ipdb.MMDBASNResult); ok {
asnResult["lemondb_asn"] = map[string]string{"asn": lemonASN.ASN, "org": lemonASN.Org}
} else if errStr, ok := result["lemondb_asn"].(string); ok {
asnResult["lemondb_asn"] = map[string]string{"error": errStr}
}
if maxmindASN, ok := result["maxmind_asn"].(*ipdb.MMDBASNResult); ok {
asnResult["geolite2_asn"] = map[string]string{"asn": maxmindASN.ASN, "org": maxmindASN.Org}
} else if errStr, ok := result["maxmind_asn"].(string); ok {