ts-ip2region2 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +120 -0
- package/README_CN.md +119 -0
- package/addon.cpp +258 -0
- package/binding.gyp +30 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +48 -0
- package/dist/index.js.map +1 -0
- package/ip2region/Makefile +27 -0
- package/ip2region/ReadMe.md +341 -0
- package/ip2region/main.c +428 -0
- package/ip2region/test_util +0 -0
- package/ip2region/test_util.c +213 -0
- package/ip2region/xdb_api.h +272 -0
- package/ip2region/xdb_searcher.c +242 -0
- package/ip2region/xdb_util.c +496 -0
- package/package.json +78 -0
package/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# ip2region Node.js Addon (TypeScript)
|
|
2
|
+
|
|
3
|
+
[English](README.md) | [中文](README_CN.md)
|
|
4
|
+
|
|
5
|
+
High-performance Node.js native addon for ip2region xdb query with IPv4/IPv6 support. Written in TypeScript with full type definitions.
|
|
6
|
+
|
|
7
|
+
> **Note**: This project is adapted from the official [ip2region C binding](https://github.com/lionsoul2014/ip2region/tree/master/binding/c) and enhanced with TypeScript support.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- 🚀 **High Performance** - Native C++ implementation with microsecond-level response
|
|
12
|
+
- 🌐 **IPv4 & IPv6** - Full support for both IPv4 and IPv6 address queries
|
|
13
|
+
- 💾 **Multiple Cache Strategies** - file/vectorIndex/content caching options
|
|
14
|
+
- 🔒 **Memory Safe** - Automatic resource management prevents memory leaks
|
|
15
|
+
- 📝 **TypeScript** - Complete TypeScript type definitions included
|
|
16
|
+
- 🔧 **Cross Platform** - Works on Windows, Linux, and macOS
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install
|
|
22
|
+
npm run build:all
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
> **Database Required**: Download the xdb database files from [ip2region data](https://github.com/lionsoul2014/ip2region/tree/master/data) before using.
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import Ip2Region from 'ip2region-nodejs';
|
|
31
|
+
|
|
32
|
+
// Create searcher instance
|
|
33
|
+
const searcher = new Ip2Region('./data/ip2region_v4.xdb', {
|
|
34
|
+
cachePolicy: 'vectorIndex', // file/vectorIndex/content
|
|
35
|
+
ipVersion: 'v4' // v4/v6
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Query IP address
|
|
39
|
+
const result = searcher.search('8.8.8.8');
|
|
40
|
+
console.log(result);
|
|
41
|
+
// Output: { region: 'United States|0|0|Level3', ioCount: 3, took: 1000 }
|
|
42
|
+
|
|
43
|
+
// Clean up
|
|
44
|
+
searcher.close();
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## API Reference
|
|
48
|
+
|
|
49
|
+
### Constructor
|
|
50
|
+
```typescript
|
|
51
|
+
new Ip2Region(dbPath: string, options?: Ip2RegionOptions)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Methods
|
|
55
|
+
- `search(ip: string): SearchResult` - Query IP address location
|
|
56
|
+
- `close(): void` - Release resources
|
|
57
|
+
|
|
58
|
+
### Static Methods
|
|
59
|
+
- `Ip2Region.verify(dbPath: string): boolean` - Verify xdb file
|
|
60
|
+
- `Ip2Region.verifyDetailed(dbPath: string): VerifyResult` - Verify with detailed info
|
|
61
|
+
|
|
62
|
+
### Types
|
|
63
|
+
```typescript
|
|
64
|
+
interface SearchResult {
|
|
65
|
+
region: string; // Geographic location
|
|
66
|
+
ioCount: number; // IO operation count
|
|
67
|
+
took: number; // Query time in microseconds
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface Ip2RegionOptions {
|
|
71
|
+
cachePolicy?: 'file' | 'vectorIndex' | 'content';
|
|
72
|
+
ipVersion?: 'v4' | 'v6';
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Cache Strategies
|
|
77
|
+
|
|
78
|
+
| Strategy | Memory | Performance | Use Case |
|
|
79
|
+
|----------|--------|-------------|----------|
|
|
80
|
+
| `file` | Minimal | Good | Memory-constrained |
|
|
81
|
+
| `vectorIndex` | Medium | Better | General use (recommended) |
|
|
82
|
+
| `content` | High | Best | High-concurrency |
|
|
83
|
+
|
|
84
|
+
## Examples
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# Run TypeScript example
|
|
88
|
+
npm run example
|
|
89
|
+
|
|
90
|
+
# Run TypeScript tests
|
|
91
|
+
npm test
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Project Structure
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
ip2region-nodejs/
|
|
98
|
+
├── src/ # TypeScript source code
|
|
99
|
+
│ ├── index.ts # Main TypeScript API
|
|
100
|
+
│ ├── example.ts # TypeScript example
|
|
101
|
+
│ └── test.ts # TypeScript tests
|
|
102
|
+
├── dist/ # Compiled JavaScript output
|
|
103
|
+
│ ├── index.js
|
|
104
|
+
│ ├── index.d.ts
|
|
105
|
+
│ └── ...
|
|
106
|
+
├── ip2region/ # Original ip2region C source code
|
|
107
|
+
│ ├── xdb_api.h
|
|
108
|
+
│ ├── xdb_util.c
|
|
109
|
+
│ └── xdb_searcher.c
|
|
110
|
+
├── addon.cpp # Node.js addon implementation
|
|
111
|
+
├── binding.gyp # Build configuration
|
|
112
|
+
├── tsconfig.json # TypeScript configuration
|
|
113
|
+
├── package.json
|
|
114
|
+
└── README.md
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## License
|
|
118
|
+
|
|
119
|
+
Apache-2.0
|
|
120
|
+
|
package/README_CN.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# ip2region Node.js Addon (TypeScript)
|
|
2
|
+
|
|
3
|
+
[English](README.md) | [中文](README_CN.md)
|
|
4
|
+
|
|
5
|
+
高性能的 ip2region xdb 查询 Node.js 原生扩展,支持 IPv4/IPv6。使用 TypeScript 编写,包含完整的类型定义。
|
|
6
|
+
|
|
7
|
+
> **说明**: 本项目基于官方 [ip2region C语言客户端](https://github.com/lionsoul2014/ip2region/tree/master/binding/c) 改造,并增强了 TypeScript 支持。
|
|
8
|
+
|
|
9
|
+
## 特性
|
|
10
|
+
|
|
11
|
+
- 🚀 **高性能** - 原生 C++ 实现,微秒级响应
|
|
12
|
+
- 🌐 **IPv4 & IPv6** - 完全支持 IPv4 和 IPv6 地址查询
|
|
13
|
+
- 💾 **多种缓存策略** - file/vectorIndex/content 缓存选项
|
|
14
|
+
- 🔒 **内存安全** - 自动资源管理,防止内存泄漏
|
|
15
|
+
- 📝 **TypeScript** - 包含完整的 TypeScript 类型定义
|
|
16
|
+
- 🔧 **跨平台** - 支持 Windows、Linux 和 macOS
|
|
17
|
+
|
|
18
|
+
## 安装
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install
|
|
22
|
+
npm run build:all
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
> **需要数据库**: 使用前请从 [ip2region data](https://github.com/lionsoul2014/ip2region/tree/master/data) 下载 xdb 数据库文件。
|
|
26
|
+
|
|
27
|
+
## 快速开始
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import Ip2Region from 'ip2region-nodejs';
|
|
31
|
+
|
|
32
|
+
// 创建查询器实例
|
|
33
|
+
const searcher = new Ip2Region('./data/ip2region_v4.xdb', {
|
|
34
|
+
cachePolicy: 'vectorIndex', // file/vectorIndex/content
|
|
35
|
+
ipVersion: 'v4' // v4/v6
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// 查询IP地址
|
|
39
|
+
const result = searcher.search('8.8.8.8');
|
|
40
|
+
console.log(result);
|
|
41
|
+
// 输出: { region: 'United States|0|0|Level3', ioCount: 3, took: 1000 }
|
|
42
|
+
|
|
43
|
+
// 清理资源
|
|
44
|
+
searcher.close();
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## API 参考
|
|
48
|
+
|
|
49
|
+
### 构造函数
|
|
50
|
+
```typescript
|
|
51
|
+
new Ip2Region(dbPath: string, options?: Ip2RegionOptions)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 方法
|
|
55
|
+
- `search(ip: string): SearchResult` - 查询IP地址位置
|
|
56
|
+
- `close(): void` - 释放资源
|
|
57
|
+
|
|
58
|
+
### 静态方法
|
|
59
|
+
- `Ip2Region.verify(dbPath: string): boolean` - 验证xdb文件
|
|
60
|
+
- `Ip2Region.verifyDetailed(dbPath: string): VerifyResult` - 验证并返回详细信息
|
|
61
|
+
|
|
62
|
+
### 类型定义
|
|
63
|
+
```typescript
|
|
64
|
+
interface SearchResult {
|
|
65
|
+
region: string; // 地理位置
|
|
66
|
+
ioCount: number; // IO操作次数
|
|
67
|
+
took: number; // 查询耗时(微秒)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface Ip2RegionOptions {
|
|
71
|
+
cachePolicy?: 'file' | 'vectorIndex' | 'content';
|
|
72
|
+
ipVersion?: 'v4' | 'v6';
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## 缓存策略
|
|
77
|
+
|
|
78
|
+
| 策略 | 内存占用 | 性能 | 使用场景 |
|
|
79
|
+
|------|----------|------|----------|
|
|
80
|
+
| `file` | 最小 | 良好 | 内存受限环境 |
|
|
81
|
+
| `vectorIndex` | 中等 | 更好 | 一般使用(推荐) |
|
|
82
|
+
| `content` | 较高 | 最佳 | 高并发场景 |
|
|
83
|
+
|
|
84
|
+
## 示例
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# 运行 TypeScript 示例
|
|
88
|
+
npm run example
|
|
89
|
+
|
|
90
|
+
# 运行 TypeScript 测试
|
|
91
|
+
npm test
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## 项目结构
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
ip2region-nodejs/
|
|
98
|
+
├── src/ # TypeScript 源代码
|
|
99
|
+
│ ├── index.ts # 主要 TypeScript API
|
|
100
|
+
│ ├── example.ts # TypeScript 示例
|
|
101
|
+
│ └── test.ts # TypeScript 测试
|
|
102
|
+
├── dist/ # 编译后的 JavaScript 输出
|
|
103
|
+
│ ├── index.js
|
|
104
|
+
│ ├── index.d.ts
|
|
105
|
+
│ └── ...
|
|
106
|
+
├── ip2region/ # 原始 ip2region C 源代码
|
|
107
|
+
│ ├── xdb_api.h
|
|
108
|
+
│ ├── xdb_util.c
|
|
109
|
+
│ └── xdb_searcher.c
|
|
110
|
+
├── addon.cpp # Node.js 扩展实现
|
|
111
|
+
├── binding.gyp # 构建配置
|
|
112
|
+
├── tsconfig.json # TypeScript 配置
|
|
113
|
+
├── package.json
|
|
114
|
+
└── README.md
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## 许可证
|
|
118
|
+
|
|
119
|
+
Apache-2.0
|
package/addon.cpp
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
#ifndef WIN32_LEAN_AND_MEAN
|
|
2
|
+
#define WIN32_LEAN_AND_MEAN
|
|
3
|
+
#endif
|
|
4
|
+
|
|
5
|
+
#include <napi.h>
|
|
6
|
+
|
|
7
|
+
extern "C" {
|
|
8
|
+
#include "ip2region/xdb_api.h"
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
#include <memory>
|
|
12
|
+
|
|
13
|
+
class Ip2RegionSearcher : public Napi::ObjectWrap<Ip2RegionSearcher> {
|
|
14
|
+
public:
|
|
15
|
+
static Napi::Object Init(Napi::Env env, Napi::Object exports);
|
|
16
|
+
Ip2RegionSearcher(const Napi::CallbackInfo& info);
|
|
17
|
+
~Ip2RegionSearcher();
|
|
18
|
+
|
|
19
|
+
private:
|
|
20
|
+
static Napi::FunctionReference constructor;
|
|
21
|
+
|
|
22
|
+
Napi::Value Search(const Napi::CallbackInfo& info);
|
|
23
|
+
Napi::Value Close(const Napi::CallbackInfo& info);
|
|
24
|
+
|
|
25
|
+
xdb_searcher_t searcher;
|
|
26
|
+
xdb_vector_index_t* v_index;
|
|
27
|
+
xdb_content_t* c_buffer;
|
|
28
|
+
bool is_initialized;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
Napi::FunctionReference Ip2RegionSearcher::constructor;
|
|
32
|
+
|
|
33
|
+
Napi::Object Ip2RegionSearcher::Init(Napi::Env env, Napi::Object exports) {
|
|
34
|
+
Napi::HandleScope scope(env);
|
|
35
|
+
|
|
36
|
+
Napi::Function func = DefineClass(env, "Ip2RegionSearcher", {
|
|
37
|
+
InstanceMethod("search", &Ip2RegionSearcher::Search),
|
|
38
|
+
InstanceMethod("close", &Ip2RegionSearcher::Close)
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
constructor = Napi::Persistent(func);
|
|
42
|
+
constructor.SuppressDestruct();
|
|
43
|
+
|
|
44
|
+
exports.Set("Ip2RegionSearcher", func);
|
|
45
|
+
return exports;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
Ip2RegionSearcher::Ip2RegionSearcher(const Napi::CallbackInfo& info)
|
|
49
|
+
: Napi::ObjectWrap<Ip2RegionSearcher>(info), v_index(nullptr), c_buffer(nullptr), is_initialized(false) {
|
|
50
|
+
|
|
51
|
+
Napi::Env env = info.Env();
|
|
52
|
+
Napi::HandleScope scope(env);
|
|
53
|
+
|
|
54
|
+
if (info.Length() < 1) {
|
|
55
|
+
Napi::TypeError::New(env, "Expected at least 1 argument").ThrowAsJavaScriptException();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!info[0].IsString()) {
|
|
60
|
+
Napi::TypeError::New(env, "Expected string for db_path").ThrowAsJavaScriptException();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
std::string db_path = info[0].As<Napi::String>().Utf8Value();
|
|
65
|
+
std::string cache_policy = "vectorIndex";
|
|
66
|
+
std::string ip_version = "v4";
|
|
67
|
+
|
|
68
|
+
if (info.Length() > 1 && info[1].IsObject()) {
|
|
69
|
+
Napi::Object options = info[1].As<Napi::Object>();
|
|
70
|
+
|
|
71
|
+
if (options.Has("cachePolicy")) {
|
|
72
|
+
cache_policy = options.Get("cachePolicy").As<Napi::String>().Utf8Value();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (options.Has("ipVersion")) {
|
|
76
|
+
ip_version = options.Get("ipVersion").As<Napi::String>().Utf8Value();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 初始化winsock (Windows)
|
|
81
|
+
int err = xdb_init_winsock();
|
|
82
|
+
if (err != 0) {
|
|
83
|
+
Napi::Error::New(env, "Failed to init winsock").ThrowAsJavaScriptException();
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 验证xdb文件 (跳过严格验证,因为某些xdb文件可能有兼容性问题)
|
|
88
|
+
// err = xdb_verify_from_file(db_path.c_str());
|
|
89
|
+
// if (err != 0) {
|
|
90
|
+
// std::string error_msg = "Failed to verify xdb file, error code: " + std::to_string(err);
|
|
91
|
+
// Napi::Error::New(env, error_msg).ThrowAsJavaScriptException();
|
|
92
|
+
// return;
|
|
93
|
+
// }
|
|
94
|
+
|
|
95
|
+
// 确定IP版本
|
|
96
|
+
xdb_version_t* version;
|
|
97
|
+
if (ip_version == "v4" || ip_version == "ipv4") {
|
|
98
|
+
version = XDB_IPv4;
|
|
99
|
+
} else if (ip_version == "v6" || ip_version == "ipv6") {
|
|
100
|
+
version = XDB_IPv6;
|
|
101
|
+
} else {
|
|
102
|
+
Napi::Error::New(env, "Invalid IP version").ThrowAsJavaScriptException();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 根据缓存策略初始化searcher
|
|
107
|
+
if (cache_policy == "file") {
|
|
108
|
+
err = xdb_new_with_file_only(version, &searcher, db_path.c_str());
|
|
109
|
+
} else if (cache_policy == "vectorIndex") {
|
|
110
|
+
v_index = xdb_load_vector_index_from_file(db_path.c_str());
|
|
111
|
+
if (v_index == nullptr) {
|
|
112
|
+
Napi::Error::New(env, "Failed to load vector index").ThrowAsJavaScriptException();
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
err = xdb_new_with_vector_index(version, &searcher, db_path.c_str(), v_index);
|
|
116
|
+
} else if (cache_policy == "content") {
|
|
117
|
+
c_buffer = xdb_load_content_from_file(db_path.c_str());
|
|
118
|
+
if (c_buffer == nullptr) {
|
|
119
|
+
Napi::Error::New(env, "Failed to load content").ThrowAsJavaScriptException();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
err = xdb_new_with_buffer(version, &searcher, c_buffer);
|
|
123
|
+
} else {
|
|
124
|
+
Napi::Error::New(env, "Invalid cache policy").ThrowAsJavaScriptException();
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (err != 0) {
|
|
129
|
+
Napi::Error::New(env, "Failed to create searcher").ThrowAsJavaScriptException();
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
is_initialized = true;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
Ip2RegionSearcher::~Ip2RegionSearcher() {
|
|
137
|
+
if (is_initialized) {
|
|
138
|
+
xdb_close(&searcher);
|
|
139
|
+
|
|
140
|
+
if (v_index != nullptr) {
|
|
141
|
+
xdb_free_vector_index(v_index);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (c_buffer != nullptr) {
|
|
145
|
+
xdb_free_content(c_buffer);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
xdb_clean_winsock();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
Napi::Value Ip2RegionSearcher::Search(const Napi::CallbackInfo& info) {
|
|
153
|
+
Napi::Env env = info.Env();
|
|
154
|
+
Napi::HandleScope scope(env);
|
|
155
|
+
|
|
156
|
+
if (!is_initialized) {
|
|
157
|
+
Napi::Error::New(env, "Searcher not initialized").ThrowAsJavaScriptException();
|
|
158
|
+
return env.Null();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (info.Length() < 1 || !info[0].IsString()) {
|
|
162
|
+
Napi::TypeError::New(env, "Expected string for IP address").ThrowAsJavaScriptException();
|
|
163
|
+
return env.Null();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
std::string ip_string = info[0].As<Napi::String>().Utf8Value();
|
|
167
|
+
|
|
168
|
+
xdb_region_buffer_t region;
|
|
169
|
+
int err = xdb_region_buffer_init(®ion, nullptr, 0);
|
|
170
|
+
if (err != 0) {
|
|
171
|
+
Napi::Error::New(env, "Failed to init region buffer").ThrowAsJavaScriptException();
|
|
172
|
+
return env.Null();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
long start_time = xdb_now();
|
|
176
|
+
err = xdb_search_by_string(&searcher, ip_string.c_str(), ®ion);
|
|
177
|
+
long cost_time = xdb_now() - start_time;
|
|
178
|
+
|
|
179
|
+
if (err != 0) {
|
|
180
|
+
xdb_region_buffer_free(®ion);
|
|
181
|
+
Napi::Error::New(env, "Search failed").ThrowAsJavaScriptException();
|
|
182
|
+
return env.Null();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
Napi::Object result = Napi::Object::New(env);
|
|
186
|
+
result.Set("region", Napi::String::New(env, region.value ? region.value : ""));
|
|
187
|
+
result.Set("ioCount", Napi::Number::New(env, xdb_get_io_count(&searcher)));
|
|
188
|
+
result.Set("took", Napi::Number::New(env, cost_time));
|
|
189
|
+
|
|
190
|
+
xdb_region_buffer_free(®ion);
|
|
191
|
+
return result;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
Napi::Value Ip2RegionSearcher::Close(const Napi::CallbackInfo& info) {
|
|
195
|
+
Napi::Env env = info.Env();
|
|
196
|
+
|
|
197
|
+
if (is_initialized) {
|
|
198
|
+
xdb_close(&searcher);
|
|
199
|
+
|
|
200
|
+
if (v_index != nullptr) {
|
|
201
|
+
xdb_free_vector_index(v_index);
|
|
202
|
+
v_index = nullptr;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (c_buffer != nullptr) {
|
|
206
|
+
xdb_free_content(c_buffer);
|
|
207
|
+
c_buffer = nullptr;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
xdb_clean_winsock();
|
|
211
|
+
is_initialized = false;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return env.Undefined();
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// 静态函数用于验证xdb文件
|
|
218
|
+
Napi::Value VerifyXdb(const Napi::CallbackInfo& info) {
|
|
219
|
+
Napi::Env env = info.Env();
|
|
220
|
+
|
|
221
|
+
if (info.Length() < 1 || !info[0].IsString()) {
|
|
222
|
+
Napi::TypeError::New(env, "Expected string for db_path").ThrowAsJavaScriptException();
|
|
223
|
+
return env.Null();
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
std::string db_path = info[0].As<Napi::String>().Utf8Value();
|
|
227
|
+
|
|
228
|
+
int err = 0;
|
|
229
|
+
FILE *handle = fopen(db_path.c_str(), "rb");
|
|
230
|
+
if (handle == NULL) {
|
|
231
|
+
err = -1;
|
|
232
|
+
} else {
|
|
233
|
+
// 加载header
|
|
234
|
+
xdb_header_t *header = xdb_load_header(handle);
|
|
235
|
+
if (header == NULL) {
|
|
236
|
+
err = 1;
|
|
237
|
+
} else {
|
|
238
|
+
// =只检查header是否正常加载
|
|
239
|
+
err = 0; // header加载成功即认为文件有效
|
|
240
|
+
xdb_free_header(header);
|
|
241
|
+
}
|
|
242
|
+
fclose(handle);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
Napi::Object result = Napi::Object::New(env);
|
|
246
|
+
result.Set("valid", Napi::Boolean::New(env, err == 0));
|
|
247
|
+
result.Set("errorCode", Napi::Number::New(env, err));
|
|
248
|
+
|
|
249
|
+
return result;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
Napi::Object Init(Napi::Env env, Napi::Object exports) {
|
|
253
|
+
Ip2RegionSearcher::Init(env, exports);
|
|
254
|
+
exports.Set("verifyXdb", Napi::Function::New(env, VerifyXdb));
|
|
255
|
+
return exports;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
NODE_API_MODULE(ip2region, Init)
|
package/binding.gyp
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"targets": [
|
|
3
|
+
{
|
|
4
|
+
"target_name": "ip2region",
|
|
5
|
+
"sources": [
|
|
6
|
+
"addon.cpp",
|
|
7
|
+
"ip2region/xdb_util.c",
|
|
8
|
+
"ip2region/xdb_searcher.c"
|
|
9
|
+
],
|
|
10
|
+
"include_dirs": [
|
|
11
|
+
"<!@(node -p \"require('node-addon-api').include\")",
|
|
12
|
+
".",
|
|
13
|
+
"ip2region"
|
|
14
|
+
],
|
|
15
|
+
"dependencies": [
|
|
16
|
+
"<!(node -p \"require('node-addon-api').gyp\")"
|
|
17
|
+
],
|
|
18
|
+
"defines": [
|
|
19
|
+
"NAPI_DISABLE_CPP_EXCEPTIONS"
|
|
20
|
+
],
|
|
21
|
+
"conditions": [
|
|
22
|
+
["OS=='win'", {
|
|
23
|
+
"libraries": [
|
|
24
|
+
"ws2_32.lib"
|
|
25
|
+
]
|
|
26
|
+
}]
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export interface SearchResult {
|
|
2
|
+
/** 地理位置信息字符串 */
|
|
3
|
+
region: string;
|
|
4
|
+
/** IO操作次数 */
|
|
5
|
+
ioCount: number;
|
|
6
|
+
/** 查询耗时(微秒) */
|
|
7
|
+
took: number;
|
|
8
|
+
}
|
|
9
|
+
export interface Ip2RegionOptions {
|
|
10
|
+
/** 缓存策略: 'file' | 'vectorIndex' | 'content' */
|
|
11
|
+
cachePolicy?: 'file' | 'vectorIndex' | 'content';
|
|
12
|
+
/** IP版本: 'v4' | 'v6' */
|
|
13
|
+
ipVersion?: 'v4' | 'v6';
|
|
14
|
+
}
|
|
15
|
+
export interface VerifyResult {
|
|
16
|
+
/** 验证结果 */
|
|
17
|
+
valid: boolean;
|
|
18
|
+
/** 错误代码 */
|
|
19
|
+
errorCode: number;
|
|
20
|
+
}
|
|
21
|
+
export default class Ip2Region {
|
|
22
|
+
private searcher;
|
|
23
|
+
/**
|
|
24
|
+
* 创建ip2region查询器实例
|
|
25
|
+
* @param dbPath xdb数据库文件路径
|
|
26
|
+
* @param options 配置选项
|
|
27
|
+
*/
|
|
28
|
+
constructor(dbPath: string, options?: Ip2RegionOptions);
|
|
29
|
+
/**
|
|
30
|
+
* 查询IP地址对应的地理位置信息
|
|
31
|
+
* @param ip IP地址字符串(支持IPv4和IPv6)
|
|
32
|
+
* @returns 查询结果
|
|
33
|
+
*/
|
|
34
|
+
search(ip: string): SearchResult;
|
|
35
|
+
/**
|
|
36
|
+
* 关闭查询器并释放资源
|
|
37
|
+
*/
|
|
38
|
+
close(): void;
|
|
39
|
+
/**
|
|
40
|
+
* 验证xdb文件的有效性
|
|
41
|
+
* @param dbPath xdb文件路径
|
|
42
|
+
* @returns 验证结果
|
|
43
|
+
*/
|
|
44
|
+
static verify(dbPath: string): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* 验证xdb文件并返回详细信息
|
|
47
|
+
* @param dbPath xdb文件路径
|
|
48
|
+
* @returns 包含 valid 和 errorCode 的对象
|
|
49
|
+
*/
|
|
50
|
+
static verifyDetailed(dbPath: string): VerifyResult;
|
|
51
|
+
}
|
|
52
|
+
export { Ip2Region };
|
|
53
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IACzB,gBAAgB;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe;IACf,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC7B,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAAC;IACjD,wBAAwB;IACxB,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IACzB,WAAW;IACX,KAAK,EAAE,OAAO,CAAC;IACf,WAAW;IACX,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,CAAC,OAAO,OAAO,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAM;IAEtB;;;;OAIG;gBACS,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB;IAI1D;;;;OAIG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY;IAIhC;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;;;OAIG;IACH,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAKtC;;;;OAIG;IACH,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY;CAGtD;AAED,OAAO,EAAE,SAAS,EAAE,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Ip2Region = void 0;
|
|
4
|
+
const addon = require('../build/Release/ip2region.node');
|
|
5
|
+
class Ip2Region {
|
|
6
|
+
/**
|
|
7
|
+
* 创建ip2region查询器实例
|
|
8
|
+
* @param dbPath xdb数据库文件路径
|
|
9
|
+
* @param options 配置选项
|
|
10
|
+
*/
|
|
11
|
+
constructor(dbPath, options = {}) {
|
|
12
|
+
this.searcher = new addon.Ip2RegionSearcher(dbPath, options);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 查询IP地址对应的地理位置信息
|
|
16
|
+
* @param ip IP地址字符串(支持IPv4和IPv6)
|
|
17
|
+
* @returns 查询结果
|
|
18
|
+
*/
|
|
19
|
+
search(ip) {
|
|
20
|
+
return this.searcher.search(ip);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* 关闭查询器并释放资源
|
|
24
|
+
*/
|
|
25
|
+
close() {
|
|
26
|
+
this.searcher.close();
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 验证xdb文件的有效性
|
|
30
|
+
* @param dbPath xdb文件路径
|
|
31
|
+
* @returns 验证结果
|
|
32
|
+
*/
|
|
33
|
+
static verify(dbPath) {
|
|
34
|
+
const result = addon.verifyXdb(dbPath);
|
|
35
|
+
return result.valid;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 验证xdb文件并返回详细信息
|
|
39
|
+
* @param dbPath xdb文件路径
|
|
40
|
+
* @returns 包含 valid 和 errorCode 的对象
|
|
41
|
+
*/
|
|
42
|
+
static verifyDetailed(dbPath) {
|
|
43
|
+
return addon.verifyXdb(dbPath);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
exports.default = Ip2Region;
|
|
47
|
+
exports.Ip2Region = Ip2Region;
|
|
48
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,MAAM,KAAK,GAAG,OAAO,CAAC,iCAAiC,CAAC,CAAC;AAyBzD,MAAqB,SAAS;IAG1B;;;;OAIG;IACH,YAAY,MAAc,EAAE,UAA4B,EAAE;QACtD,IAAI,CAAC,QAAQ,GAAG,IAAI,KAAK,CAAC,iBAAiB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjE,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,EAAU;QACb,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACpC,CAAC;IAED;;OAEG;IACH,KAAK;QACD,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IAC1B,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,MAAM,CAAC,MAAc;QACxB,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACvC,OAAO,MAAM,CAAC,KAAK,CAAC;IACxB,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,cAAc,CAAC,MAAc;QAChC,OAAO,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;CACJ;AA9CD,4BA8CC;AAEQ,8BAAS"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
all: xdb_searcher test_util
|
|
2
|
+
|
|
3
|
+
xdb_searcher: xdb_api.h xdb_util.c xdb_searcher.c main.c
|
|
4
|
+
gcc -Wall -O2 -I./ xdb_util.c xdb_searcher.c main.c -o xdb_searcher
|
|
5
|
+
|
|
6
|
+
test_util: xdb_api.h xdb_util.c test_util.c
|
|
7
|
+
gcc -Wall -O2 -I./ xdb_util.c test_util.c -o test_util
|
|
8
|
+
|
|
9
|
+
xdb_searcher.o: xdb_searcher.c
|
|
10
|
+
gcc -Wall -c xdb_searcher.c
|
|
11
|
+
|
|
12
|
+
xdb_util.o: xdb_util.c
|
|
13
|
+
gcc -Wall -c xdb_util.c
|
|
14
|
+
|
|
15
|
+
xdb_searcher_lib: xdb_util.o xdb_searcher.o
|
|
16
|
+
mkdir -p build/lib
|
|
17
|
+
mkdir -p build/include
|
|
18
|
+
ar -rc build/lib/libxdb.a `find . -name *.o`
|
|
19
|
+
cp xdb_api.h build/include
|
|
20
|
+
|
|
21
|
+
clean:
|
|
22
|
+
find ./ -name \*.o | xargs rm -f
|
|
23
|
+
find ./ -name test_util | xargs rm -f
|
|
24
|
+
find ./ -name xdb_searcher | xargs rm -f
|
|
25
|
+
rm -rf build
|
|
26
|
+
|
|
27
|
+
.PHONY: all clean xdb_searcher test_util
|