Skip to content

Commit 99250bc

Browse files
committed
lib/maxmind
1 parent 97750c6 commit 99250bc

File tree

10 files changed

+373
-0
lines changed

10 files changed

+373
-0
lines changed

.github/workflows/on_commit.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
name: CI
3+
4+
on:
5+
workflow_dispatch:
6+
7+
pull_request:
8+
push:
9+
branches: [main]
10+
11+
jobs:
12+
build:
13+
name: Build
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- name: 🛎️ Checkout
18+
uses: actions/checkout@v4
19+
20+
- name: ⚙️ Setup Bun
21+
uses: oven-sh/setup-bun@v1
22+
23+
- name: 📦 Build
24+
env:
25+
NODE_ENV: production
26+
FORCE_COLOR: 1
27+
run: |
28+
bun install
29+
make --output-sync --jobs fmt test

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules/
2+
**/*.mmdb
3+
lib/**/*.d.*ts
4+
lib/**/index.mjs

Makefile

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
.PHONY : all test
2+
all :
3+
4+
.PHONY : lib/maxmind
5+
lib/maxmind :
6+
@ $(MAKE) --directory=lib/maxmind
7+
8+
.PHONY : fmt
9+
fmt :
10+
bun x prettier --write .
11+
@ git diff-index --quiet HEAD
12+
13+
.PHONY : test
14+
test :
15+
@ $(MAKE) --directory=lib/maxmind test
16+
17+
.PHONY : distclean
18+
distclean :
19+
-rm -r node_modules
20+
-@ $(MAKE) --directory=lib/maxmind clean

lib/maxmind/Makefile

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
.PHONY : all test clean
2+
all : test/GeoLite2-Country-Test.mmdb index.mjs
3+
4+
index.mjs : index.mts
5+
bun x tsc --pretty
6+
bun x webpack
7+
8+
test/GeoLite2-Country-Test.mmdb :
9+
cd test && wget 'https://cdn.jsdelivr.net/gh/maxmind/MaxMind-DB/test-data/GeoLite2-Country-Test.mmdb'
10+
11+
test :
12+
cd test && bun x tsc
13+
cd test && bun test
14+
15+
clean :
16+
- rm test/*.mmdb *.d.mts index.*js

lib/maxmind/index.mts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import type { CountryResponse } from 'mmdb-lib';
2+
import { Reader } from 'mmdb-lib';
3+
4+
export async function init(response: Pick<Response, 'arrayBuffer'>) {
5+
const db = Buffer.from(await response.arrayBuffer());
6+
7+
return new Reader<CountryResponse>(db);
8+
}

lib/maxmind/test/maxmind.spec.ts

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
import { describe, expect, it } from 'bun:test';
2+
import fs from 'node:fs/promises';
3+
import { fileURLToPath } from 'node:url';
4+
5+
import * as maxmind from '../index.mjs';
6+
7+
describe('lib / maxmind', () => {
8+
const arrayBuffer = fs
9+
.readFile(
10+
fileURLToPath(import.meta.resolve('./GeoLite2-Country-Test.mmdb')),
11+
)
12+
.then(({ buffer }) => buffer as ArrayBuffer);
13+
const mockDatabase = { arrayBuffer: () => arrayBuffer };
14+
15+
it('should resolve a country for IPv4', async () => {
16+
const reader = await maxmind.init(mockDatabase);
17+
18+
expect(reader.get('89.160.20.122')).toStrictEqual({
19+
continent: {
20+
code: 'EU',
21+
geoname_id: 6255148,
22+
names: {
23+
de: 'Europa',
24+
en: 'Europe',
25+
es: 'Europa',
26+
fr: 'Europe',
27+
ja: 'ヨーロッパ',
28+
'pt-BR': 'Europa',
29+
ru: 'Европа',
30+
'zh-CN': '欧洲',
31+
},
32+
},
33+
country: {
34+
geoname_id: 2661886,
35+
is_in_european_union: true,
36+
iso_code: 'SE',
37+
names: {
38+
de: 'Schweden',
39+
en: 'Sweden',
40+
es: 'Suecia',
41+
fr: 'Suède',
42+
ja: 'スウェーデン王国',
43+
'pt-BR': 'Suécia',
44+
ru: 'Швеция',
45+
'zh-CN': '瑞典',
46+
},
47+
},
48+
registered_country: {
49+
geoname_id: 2921044,
50+
is_in_european_union: true,
51+
iso_code: 'DE',
52+
names: {
53+
de: 'Deutschland',
54+
en: 'Germany',
55+
es: 'Alemania',
56+
fr: 'Allemagne',
57+
ja: 'ドイツ連邦共和国',
58+
'pt-BR': 'Alemanha',
59+
ru: 'Германия',
60+
'zh-CN': '德国',
61+
},
62+
},
63+
});
64+
65+
expect(reader.get('67.43.156.156')).toStrictEqual({
66+
continent: {
67+
code: 'AS',
68+
geoname_id: 6255147,
69+
names: {
70+
de: 'Asien',
71+
en: 'Asia',
72+
es: 'Asia',
73+
fr: 'Asie',
74+
ja: 'アジア',
75+
'pt-BR': 'Ásia',
76+
ru: 'Азия',
77+
'zh-CN': '亚洲',
78+
},
79+
},
80+
country: {
81+
geoname_id: 1252634,
82+
iso_code: 'BT',
83+
names: {
84+
de: 'Bhutan',
85+
en: 'Bhutan',
86+
es: 'Bután',
87+
fr: 'Bhutan',
88+
ja: 'ブータン王国',
89+
'pt-BR': 'Butão',
90+
ru: 'Бутан',
91+
'zh-CN': '不丹',
92+
},
93+
},
94+
registered_country: {
95+
geoname_id: 798549,
96+
is_in_european_union: true,
97+
iso_code: 'RO',
98+
names: {
99+
de: 'Rumänien',
100+
en: 'Romania',
101+
es: 'Rumanía',
102+
fr: 'Roumanie',
103+
ja: 'ルーマニア',
104+
'pt-BR': 'Romênia',
105+
ru: 'Румыния',
106+
'zh-CN': '罗马尼亚',
107+
},
108+
},
109+
traits: {
110+
is_anonymous_proxy: true,
111+
},
112+
});
113+
});
114+
115+
it('should resolve a country for IPv6', async () => {
116+
const reader = await maxmind.init(mockDatabase);
117+
118+
expect(reader.get('2a02:fc40::1')).toStrictEqual({
119+
continent: {
120+
code: 'EU',
121+
geoname_id: 6255148,
122+
names: {
123+
de: 'Europa',
124+
en: 'Europe',
125+
es: 'Europa',
126+
fr: 'Europe',
127+
ja: 'ヨーロッパ',
128+
'pt-BR': 'Europa',
129+
ru: 'Европа',
130+
'zh-CN': '欧洲',
131+
},
132+
},
133+
country: {
134+
geoname_id: 2623032,
135+
is_in_european_union: true,
136+
iso_code: 'DK',
137+
names: {
138+
de: 'Dänemark',
139+
en: 'Denmark',
140+
es: 'Dinamarca',
141+
fr: 'Danemark',
142+
ja: 'デンマーク王国',
143+
'pt-BR': 'Dinamarca',
144+
ru: 'Дания',
145+
'zh-CN': '丹麦',
146+
},
147+
},
148+
registered_country: {
149+
geoname_id: 2623032,
150+
is_in_european_union: true,
151+
iso_code: 'DK',
152+
names: {
153+
de: 'Dänemark',
154+
en: 'Denmark',
155+
es: 'Dinamarca',
156+
fr: 'Danemark',
157+
ja: 'デンマーク王国',
158+
'pt-BR': 'Dinamarca',
159+
ru: 'Дания',
160+
'zh-CN': '丹麦',
161+
},
162+
},
163+
});
164+
165+
expect(reader.get('2a02:d300::1')).toStrictEqual({
166+
continent: {
167+
code: 'EU',
168+
geoname_id: 6255148,
169+
names: {
170+
de: 'Europa',
171+
en: 'Europe',
172+
es: 'Europa',
173+
fr: 'Europe',
174+
ja: 'ヨーロッパ',
175+
'pt-BR': 'Europa',
176+
ru: 'Европа',
177+
'zh-CN': '欧洲',
178+
},
179+
},
180+
country: {
181+
geoname_id: 690791,
182+
iso_code: 'UA',
183+
names: {
184+
de: 'Ukraine',
185+
en: 'Ukraine',
186+
es: 'Ucrania',
187+
fr: 'Ukraine',
188+
ja: 'ウクライナ共和国',
189+
'pt-BR': 'Ucrânia',
190+
ru: 'Украина',
191+
'zh-CN': '乌克兰',
192+
},
193+
},
194+
registered_country: {
195+
geoname_id: 690791,
196+
iso_code: 'UA',
197+
names: {
198+
de: 'Ukraine',
199+
en: 'Ukraine',
200+
es: 'Ucrania',
201+
fr: 'Ukraine',
202+
ja: 'ウクライナ共和国',
203+
'pt-BR': 'Ucrânia',
204+
ru: 'Украина',
205+
'zh-CN': '乌克兰',
206+
},
207+
},
208+
});
209+
});
210+
});

lib/maxmind/test/tsconfig.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"$schema": "https://json.schemastore.org/tsconfig",
3+
4+
"include": ["*.ts"],
5+
6+
"compilerOptions": {
7+
"target": "ESNext",
8+
"module": "Preserve",
9+
"skipLibCheck": true,
10+
"noEmit": true,
11+
12+
"strict": true,
13+
"forceConsistentCasingInFileNames": true
14+
}
15+
}

lib/maxmind/tsconfig.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"$schema": "https://json.schemastore.org/tsconfig",
3+
4+
"include": ["*.mts", "webpack.config.mjs"],
5+
6+
"compilerOptions": {
7+
"target": "ESNext",
8+
"module": "Preserve",
9+
"types": [],
10+
"declaration": true,
11+
12+
"strict": true,
13+
"forceConsistentCasingInFileNames": true
14+
}
15+
}

lib/maxmind/webpack.config.mjs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// @ts-check
2+
3+
import { fileURLToPath } from 'node:url';
4+
5+
import webpack from 'webpack';
6+
7+
/** @type webpack.Configuration */
8+
const config = {
9+
mode: 'none',
10+
experiments: { outputModule: true },
11+
12+
entry: { index: fileURLToPath(import.meta.resolve('./index.mjs')) },
13+
output: {
14+
path: fileURLToPath(import.meta.resolve('.')),
15+
libraryTarget: 'module',
16+
},
17+
18+
resolve: {
19+
fallback: {
20+
net: false,
21+
},
22+
},
23+
plugins: [
24+
new webpack.ProvidePlugin({
25+
Buffer: ['buffer', 'Buffer'],
26+
}),
27+
],
28+
};
29+
30+
export default config;

package.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"private": true,
3+
"name": "ctf",
4+
"version": "0.1.0",
5+
"author": "nilfalse.com",
6+
"license": "MPL-2.0",
7+
"type": "module",
8+
"scripts": {
9+
"postinstall": "make lib/maxmind"
10+
},
11+
"dependencies": {
12+
"buffer": "^6.0.3",
13+
"mmdb-lib": "^2.1.1"
14+
},
15+
"devDependencies": {
16+
"@types/bun": "latest",
17+
"prettier": "^3.3.1",
18+
"webpack-cli": "^5.1.4"
19+
},
20+
"peerDependencies": {
21+
"typescript": "*"
22+
},
23+
"prettier": {
24+
"singleQuote": true
25+
}
26+
}

0 commit comments

Comments
 (0)