Skip to content

Commit ec3d975

Browse files
committed
lib/maxmind
1 parent 7a057fc commit ec3d975

13 files changed

+403
-1
lines changed

.github/workflows/on_commit.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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+
run: |
25+
bun install
26+
make 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+
lib/**/*.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

assets/fonts/index.css

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,7 @@
22
font-family: 'Pacifico';
33
font-style: normal;
44
font-weight: normal;
5-
src: local('Pacifico Regular'), url('Pacifico-Regular.woff') format('woff');
5+
src:
6+
local('Pacifico Regular'),
7+
url('Pacifico-Regular.woff') format('woff');
68
}

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

src/service_worker.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import * as maxmind from '../lib/maxmind/index.mjs';

0 commit comments

Comments
 (0)