maxibase 1.2.1 → 1.3.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/docker/compose.yaml
CHANGED
|
@@ -70,8 +70,8 @@ function formatDay(utcTimestamp: string) {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
function originUrl(origin: string | null) {
|
|
73
|
-
const url = origin?.replace(/ .*/, '')
|
|
74
|
-
return url
|
|
73
|
+
const url = origin?.replace(/^client /, '').replace(/ .*/, '')
|
|
74
|
+
return url && /^\/(?![/\\])/.test(url) ? url : null
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
function toggleExpanded(id: number, isOpen: boolean) {
|
|
@@ -16,5 +16,5 @@ const bodySchema = z.object({
|
|
|
16
16
|
|
|
17
17
|
export default defineEventHandler(async (event) => {
|
|
18
18
|
const { summary, data, origin, type } = await readValidatedBody(event, bodySchema.parse)
|
|
19
|
-
await log(summary, data, origin, type, event)
|
|
19
|
+
await log(summary, data, origin ? `client ${origin}` : 'client', type, event)
|
|
20
20
|
})
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { H3Event } from 'h3'
|
|
2
|
+
import { isIPv6 } from 'node:net'
|
|
2
3
|
|
|
3
4
|
export interface RateLimitRule {
|
|
4
5
|
limit: number // max requests per window
|
|
@@ -24,7 +25,31 @@ setInterval(() => {
|
|
|
24
25
|
async function getId(event: H3Event): Promise<string> {
|
|
25
26
|
const { user } = await getUserSession(event)
|
|
26
27
|
if (user) return user.id
|
|
27
|
-
return getClientIP(event)
|
|
28
|
+
return rateLimitIP(getClientIP(event))
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// An IPv6 client usually holds a whole /64, so counting single addresses would let it rotate
|
|
32
|
+
// through 2^64 of them. Count the /64 instead, and IPv4-mapped addresses as their IPv4.
|
|
33
|
+
export function rateLimitIP(ip: string): string {
|
|
34
|
+
const address = ip.split('%')[0]!.toLowerCase() // drop the zone id
|
|
35
|
+
if (!isIPv6(address)) return ip
|
|
36
|
+
|
|
37
|
+
const mapped = address.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/)
|
|
38
|
+
if (mapped) return mapped[1]!
|
|
39
|
+
|
|
40
|
+
// an embedded IPv4 tail fills the last two groups
|
|
41
|
+
const toGroups = (part: string) =>
|
|
42
|
+
part ? part.split(':').flatMap((group) => (group.includes('.') ? ['0', '0'] : [group])) : []
|
|
43
|
+
const [head = '', tail] = address.split('::')
|
|
44
|
+
let groups = toGroups(head)
|
|
45
|
+
if (tail !== undefined) {
|
|
46
|
+
const tailGroups = toGroups(tail)
|
|
47
|
+
groups = [...groups, ...Array(8 - groups.length - tailGroups.length).fill('0'), ...tailGroups]
|
|
48
|
+
}
|
|
49
|
+
return `${groups
|
|
50
|
+
.slice(0, 4)
|
|
51
|
+
.map((group) => parseInt(group, 16).toString(16))
|
|
52
|
+
.join(':')}::/64`
|
|
28
53
|
}
|
|
29
54
|
|
|
30
55
|
export async function enforceRateLimit(
|
package/nuxt.config.ts
CHANGED
|
@@ -13,10 +13,7 @@ export default defineNuxtConfig({
|
|
|
13
13
|
lang: 'en',
|
|
14
14
|
},
|
|
15
15
|
title: process.env.NUXT_PUBLIC_NAME,
|
|
16
|
-
link: [
|
|
17
|
-
{ rel: 'icon', href: '/favicon.ico', sizes: '32x32' },
|
|
18
|
-
{ rel: 'icon', type: 'image/svg+xml', href: '/logo.svg' },
|
|
19
|
-
],
|
|
16
|
+
link: [{ rel: 'icon', href: '/favicon.ico', sizes: '32x32' }],
|
|
20
17
|
meta: [{ name: 'theme-color', content: '#000000' }],
|
|
21
18
|
},
|
|
22
19
|
},
|
|
@@ -29,7 +26,7 @@ export default defineNuxtConfig({
|
|
|
29
26
|
rateLimit: {
|
|
30
27
|
routes: {
|
|
31
28
|
// limit: max requests per IP, window: in seconds
|
|
32
|
-
'/api/log': { limit:
|
|
29
|
+
'/api/log': { limit: 10, window: 3600 },
|
|
33
30
|
'/api/auth/options': { limit: 10, window: 60 },
|
|
34
31
|
'/api/auth/password': { limit: 5, window: 60 },
|
|
35
32
|
'/api/auth/otp/get': { limit: 5, window: 900 },
|
|
@@ -38,6 +35,7 @@ export default defineNuxtConfig({
|
|
|
38
35
|
'/api/auth/otp/verify': { limit: 10, window: 60 },
|
|
39
36
|
'/api/auth/totp/verify': { limit: 10, window: 60 },
|
|
40
37
|
'/api/auth/webauthn/authenticate': { limit: 10, window: 60 },
|
|
38
|
+
'/api/auth/webauthn/register': { limit: 10, window: 60 },
|
|
41
39
|
'/api/auth/anonymous': { limit: 5, window: 3600 },
|
|
42
40
|
},
|
|
43
41
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "maxibase",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Nuxt layer with auth, files, logs and backups",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nuxt",
|
|
@@ -58,12 +58,12 @@
|
|
|
58
58
|
"prepublishOnly": "pnpm lint && pnpm fmt:check && pnpm typecheck && pnpm test"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@aws-sdk/client-s3": "^3.
|
|
62
|
-
"@aws-sdk/s3-request-presigner": "^3.
|
|
63
|
-
"@iconify-json/lucide": "^1.2.
|
|
64
|
-
"@nuxt/hints": "^1.1.
|
|
61
|
+
"@aws-sdk/client-s3": "^3.1154.0",
|
|
62
|
+
"@aws-sdk/s3-request-presigner": "^3.1154.0",
|
|
63
|
+
"@iconify-json/lucide": "^1.2.147",
|
|
64
|
+
"@nuxt/hints": "^1.1.5",
|
|
65
65
|
"@nuxt/icon": "^2.5.1",
|
|
66
|
-
"@pinia/nuxt": "^1.0.
|
|
66
|
+
"@pinia/nuxt": "^1.0.3",
|
|
67
67
|
"@simplewebauthn/browser": "^11.0.0",
|
|
68
68
|
"@simplewebauthn/server": "^11.0.0",
|
|
69
69
|
"@types/nodemailer": "^8.0.2",
|
|
@@ -71,24 +71,24 @@
|
|
|
71
71
|
"@types/qrcode": "^1.5.6",
|
|
72
72
|
"drizzle-orm": "1.0.0-rc.3",
|
|
73
73
|
"goku-css": "^9.0.8",
|
|
74
|
-
"nodemailer": "^
|
|
75
|
-
"nuxt-auth-utils": "^0.5.
|
|
76
|
-
"nuxt-i18n-micro": "^3.30.
|
|
74
|
+
"nodemailer": "^10.1.7",
|
|
75
|
+
"nuxt-auth-utils": "^0.5.32",
|
|
76
|
+
"nuxt-i18n-micro": "^3.30.1",
|
|
77
77
|
"otplib": "^13.5.0",
|
|
78
78
|
"pg": "^8.23.1",
|
|
79
|
-
"pinia": "^4.0
|
|
79
|
+
"pinia": "^4.1.0",
|
|
80
80
|
"qrcode": "^1.5.4",
|
|
81
|
-
"zod": "^4.6.
|
|
81
|
+
"zod": "^4.6.7"
|
|
82
82
|
},
|
|
83
83
|
"devDependencies": {
|
|
84
84
|
"drizzle-kit": "1.0.0-rc.3",
|
|
85
85
|
"drizzle-seed": "1.0.0-rc.3",
|
|
86
86
|
"maxibase": "link:.",
|
|
87
|
-
"nuxt": "^4.6.
|
|
87
|
+
"nuxt": "^4.6.2",
|
|
88
88
|
"oxfmt": "^0.72.0",
|
|
89
|
-
"oxlint": "^1.
|
|
89
|
+
"oxlint": "^1.89.0",
|
|
90
90
|
"typescript": "^5.9.3",
|
|
91
|
-
"vitest": "^5.0.
|
|
92
|
-
"vue-tsc": "^3.3.
|
|
91
|
+
"vitest": "^5.0.5",
|
|
92
|
+
"vue-tsc": "^3.3.13"
|
|
93
93
|
}
|
|
94
94
|
}
|