@moontra/moonui-pro 2.36.7 → 2.37.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moontra/moonui-pro",
3
- "version": "2.36.7",
3
+ "version": "2.37.1",
4
4
  "description": "Premium React components for MoonUI - Advanced UI library with 50+ pro components including performance, interactive, and gesture components",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
@@ -12,6 +12,7 @@
12
12
  "dist",
13
13
  "scripts",
14
14
  "plugin",
15
+ "templates",
15
16
  "README.md",
16
17
  "LICENSE"
17
18
  ],
@@ -30,11 +31,17 @@
30
31
  },
31
32
  "./styles.css": "./dist/index.css",
32
33
  "./css": "./dist/index.css",
34
+ "./server": {
35
+ "types": "./dist/server.d.ts",
36
+ "import": "./dist/server.mjs",
37
+ "require": "./dist/server.mjs"
38
+ },
33
39
  "./plugin": {
34
40
  "types": "./plugin/index.d.ts",
35
41
  "import": "./plugin/index.js",
36
42
  "require": "./plugin/index.js"
37
- }
43
+ },
44
+ "./templates/validate-pro-route": "./templates/validate-pro-route.ts"
38
45
  },
39
46
  "scripts": {
40
47
  "build": "tsup && node scripts/postbuild.js",
@@ -0,0 +1,157 @@
1
+ /**
2
+ * MoonUI Pro - Server Validation API Route Template
3
+ *
4
+ * INSTALLATION:
5
+ * 1. Copy this file to your project: app/api/moonui/validate-pro/route.ts
6
+ * 2. Set environment variable: MOONUI_LICENSE_KEY=your-license-key
7
+ * 3. (Optional) Set encryption key: MOONUI_ENCRYPTION_KEY=your-secret-key
8
+ *
9
+ * This route handles server-side validation to prevent browser API calls.
10
+ */
11
+
12
+ import { NextRequest, NextResponse } from "next/server";
13
+ import {
14
+ performServerValidation,
15
+ clearValidationCookies,
16
+ generateServerDeviceFingerprint,
17
+ setValidationInCookies,
18
+ } from "@moontra/moonui-pro/server";
19
+
20
+ // In-memory cache for rate limiting (use Redis in production)
21
+ const validationCache = new Map<
22
+ string,
23
+ {
24
+ count: number;
25
+ resetAt: number;
26
+ }
27
+ >();
28
+
29
+ // Rate limiting helper
30
+ function checkRateLimit(identifier: string): boolean {
31
+ const now = Date.now();
32
+ const limit = validationCache.get(identifier);
33
+
34
+ if (!limit || limit.resetAt < now) {
35
+ // Reset or create new limit
36
+ validationCache.set(identifier, {
37
+ count: 1,
38
+ resetAt: now + 60 * 1000, // 1 minute window
39
+ });
40
+ return true;
41
+ }
42
+
43
+ if (limit.count >= 10) {
44
+ // Max 10 requests per minute
45
+ return false;
46
+ }
47
+
48
+ limit.count++;
49
+ return true;
50
+ }
51
+
52
+ /**
53
+ * GET /api/moonui/validate-pro
54
+ * Validates the current session
55
+ */
56
+ export async function GET(request: NextRequest) {
57
+ try {
58
+ // Generate device fingerprint for rate limiting
59
+ const deviceId = await generateServerDeviceFingerprint();
60
+
61
+ // Check rate limit
62
+ if (!checkRateLimit(deviceId)) {
63
+ return NextResponse.json(
64
+ { error: "Rate limit exceeded", valid: false },
65
+ { status: 429 }
66
+ );
67
+ }
68
+
69
+ // Perform server-side validation
70
+ const validation = await performServerValidation();
71
+
72
+ // Return validation result
73
+ // Client will read from cookies, not from this response
74
+ return NextResponse.json({
75
+ valid: validation.valid,
76
+ hasProAccess: validation.hasProAccess,
77
+ cached: validation.cached,
78
+ });
79
+ } catch (error) {
80
+ console.error("[API] Validation error:", error);
81
+ return NextResponse.json(
82
+ { error: "Internal server error", valid: false },
83
+ { status: 500 }
84
+ );
85
+ }
86
+ }
87
+
88
+ /**
89
+ * POST /api/moonui/validate-pro
90
+ * Validates a provided token
91
+ */
92
+ export async function POST(request: NextRequest) {
93
+ try {
94
+ const body = await request.json();
95
+ const { token } = body;
96
+
97
+ if (!token) {
98
+ return NextResponse.json(
99
+ { error: "Token required", valid: false },
100
+ { status: 400 }
101
+ );
102
+ }
103
+
104
+ // Generate device fingerprint
105
+ const deviceId = await generateServerDeviceFingerprint();
106
+
107
+ // Check rate limit
108
+ if (!checkRateLimit(deviceId)) {
109
+ return NextResponse.json(
110
+ { error: "Rate limit exceeded", valid: false },
111
+ { status: 429 }
112
+ );
113
+ }
114
+
115
+ // Import server validation utilities
116
+ const { validateWithMoonUIServer } = await import(
117
+ "@moontra/moonui-pro/server"
118
+ );
119
+
120
+ // Validate token with MoonUI server
121
+ const result = await validateWithMoonUIServer(token, deviceId);
122
+
123
+ // If valid, set cookies for future requests
124
+ if (result.valid) {
125
+ await setValidationInCookies(result);
126
+ }
127
+
128
+ return NextResponse.json(result);
129
+ } catch (error) {
130
+ console.error("[API] Validation error:", error);
131
+ return NextResponse.json(
132
+ { error: "Internal server error", valid: false },
133
+ { status: 500 }
134
+ );
135
+ }
136
+ }
137
+
138
+ /**
139
+ * DELETE /api/moonui/validate-pro
140
+ * Clears validation session
141
+ */
142
+ export async function DELETE(request: NextRequest) {
143
+ try {
144
+ await clearValidationCookies();
145
+
146
+ return NextResponse.json({
147
+ success: true,
148
+ message: "Validation cleared",
149
+ });
150
+ } catch (error) {
151
+ console.error("[API] Clear validation error:", error);
152
+ return NextResponse.json(
153
+ { error: "Failed to clear validation" },
154
+ { status: 500 }
155
+ );
156
+ }
157
+ }
@@ -0,0 +1,336 @@
1
+ /**
2
+ * MoonUI Pro - Server-side License Validation Route with Enhanced Security
3
+ *
4
+ * This route handles license validation on the server to prevent browser API calls.
5
+ * Copy this file to: app/api/moonui/validate-pro/route.ts (App Router)
6
+ * Or: pages/api/moonui/validate-pro.ts (Pages Router)
7
+ *
8
+ * Required environment variables:
9
+ * - MOONUI_LICENSE_KEY: Your MoonUI Pro license key
10
+ * - MOONUI_ENCRYPTION_KEY (optional): Custom encryption key for cookies
11
+ * - MOONUI_SECURITY_HASH (auto-generated): Security checksum for validation
12
+ *
13
+ * SECURITY WARNING: Do not modify the validation logic.
14
+ * Any tampering will be detected and reported.
15
+ */
16
+
17
+ import { NextRequest, NextResponse } from 'next/server';
18
+ import { cookies, headers } from 'next/headers';
19
+ import crypto from 'crypto';
20
+
21
+ // Security checksum - DO NOT MODIFY
22
+ const SECURITY_CHECKSUM = process.env.MOONUI_SECURITY_HASH ||
23
+ crypto.createHash('sha256')
24
+ .update(`moonui-pro-${process.env.MOONUI_LICENSE_KEY || 'default'}-validation`)
25
+ .digest('hex');
26
+
27
+ // Cache configuration
28
+ const CACHE_DURATION = process.env.NODE_ENV === 'production'
29
+ ? 24 * 60 * 60 * 1000 // 24 hours in production
30
+ : 60 * 60 * 1000; // 1 hour in development
31
+
32
+ // Rate limiting
33
+ const rateLimitCache = new Map<string, { count: number; resetAt: number }>();
34
+
35
+ // In-memory validation cache
36
+ const validationCache = new Map<string, {
37
+ valid: boolean;
38
+ hasProAccess: boolean;
39
+ timestamp: number;
40
+ expiresAt: number;
41
+ }>();
42
+
43
+ /**
44
+ * Generate a device fingerprint from request headers
45
+ */
46
+ async function getDeviceFingerprint(request: NextRequest): Promise<string> {
47
+ const headersList = await headers();
48
+ const ua = headersList.get('user-agent') || 'unknown';
49
+ const lang = headersList.get('accept-language') || 'unknown';
50
+ const ip = headersList.get('x-forwarded-for') ||
51
+ headersList.get('x-real-ip') ||
52
+ 'unknown';
53
+
54
+ return crypto
55
+ .createHash('sha256')
56
+ .update(`${ua}|${lang}|${ip}`)
57
+ .digest('hex');
58
+ }
59
+
60
+ /**
61
+ * Check rate limiting
62
+ */
63
+ function checkRateLimit(identifier: string): boolean {
64
+ const now = Date.now();
65
+ const limit = rateLimitCache.get(identifier);
66
+
67
+ if (!limit || limit.resetAt < now) {
68
+ rateLimitCache.set(identifier, {
69
+ count: 1,
70
+ resetAt: now + 60 * 1000, // Reset after 1 minute
71
+ });
72
+ return true;
73
+ }
74
+
75
+ if (limit.count >= 10) { // Max 10 requests per minute
76
+ return false;
77
+ }
78
+
79
+ limit.count++;
80
+ return true;
81
+ }
82
+
83
+ /**
84
+ * Validate license with MoonUI server (server-to-server)
85
+ */
86
+ async function validateWithMoonUIServer(
87
+ token: string,
88
+ deviceId: string
89
+ ): Promise<{ valid: boolean; hasProAccess: boolean; plan?: string }> {
90
+ // Development mode with CLI authentication
91
+ if (process.env.NODE_ENV === 'development') {
92
+ // Check for CLI dev tokens
93
+ const devToken = process.env.NEXT_PUBLIC_MOONUI_DEV_TOKEN;
94
+ const devDeviceId = process.env.NEXT_PUBLIC_MOONUI_DEVICE_ID;
95
+
96
+ if (devToken && devDeviceId === deviceId) {
97
+ try {
98
+ // Decode the dev token
99
+ const decoded = JSON.parse(Buffer.from(devToken, 'base64').toString());
100
+ if (decoded.user?.plan === 'pro_lifetime' || decoded.user?.hasProAccess) {
101
+ return {
102
+ valid: true,
103
+ hasProAccess: true,
104
+ plan: 'lifetime'
105
+ };
106
+ }
107
+ } catch (e) {
108
+ console.error('[MoonUI] Error parsing dev token:', e);
109
+ }
110
+ }
111
+ }
112
+
113
+ // Production validation with license key
114
+ const licenseKey = process.env.MOONUI_LICENSE_KEY;
115
+
116
+ if (!licenseKey) {
117
+ console.error('[MoonUI] No license key configured');
118
+ return { valid: false, hasProAccess: false };
119
+ }
120
+
121
+ try {
122
+ const response = await fetch('https://api.moonui.dev/v1/license/validate', {
123
+ method: 'POST',
124
+ headers: {
125
+ 'Content-Type': 'application/json',
126
+ 'Authorization': `Bearer ${licenseKey}`,
127
+ 'X-Device-ID': deviceId,
128
+ },
129
+ body: JSON.stringify({
130
+ token: licenseKey,
131
+ deviceId,
132
+ domain: process.env.NEXT_PUBLIC_DOMAIN || 'localhost',
133
+ timestamp: Date.now(),
134
+ }),
135
+ signal: AbortSignal.timeout(10000), // 10 second timeout
136
+ });
137
+
138
+ if (!response.ok) {
139
+ console.error('[MoonUI] License validation failed:', response.status);
140
+ return { valid: false, hasProAccess: false };
141
+ }
142
+
143
+ const data = await response.json();
144
+
145
+ return {
146
+ valid: data.valid,
147
+ hasProAccess: data.valid && (
148
+ data.user?.hasLifetimeAccess ||
149
+ data.user?.features?.includes('pro_components') ||
150
+ data.plan === 'pro_lifetime' ||
151
+ false
152
+ ),
153
+ plan: data.user?.plan || data.plan,
154
+ };
155
+
156
+ } catch (error) {
157
+ console.error('[MoonUI] Error validating license:', error);
158
+ return { valid: false, hasProAccess: false };
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Verify request integrity
164
+ */
165
+ function verifyRequestIntegrity(request: NextRequest): boolean {
166
+ // Check for suspicious patterns
167
+ const url = new URL(request.url);
168
+
169
+ // Reject if trying to bypass with query params
170
+ if (url.searchParams.has('bypass') ||
171
+ url.searchParams.has('force') ||
172
+ url.searchParams.has('admin')) {
173
+ console.warn('[MoonUI Security] Suspicious query params detected');
174
+ return false;
175
+ }
176
+
177
+ // Check request headers for tampering
178
+ const suspiciousHeaders = [
179
+ 'x-moonui-bypass',
180
+ 'x-force-pro',
181
+ 'x-admin-override'
182
+ ];
183
+
184
+ for (const header of suspiciousHeaders) {
185
+ if (request.headers.has(header)) {
186
+ console.warn(`[MoonUI Security] Suspicious header detected: ${header}`);
187
+ return false;
188
+ }
189
+ }
190
+
191
+ return true;
192
+ }
193
+
194
+ /**
195
+ * Generate validation signature for response
196
+ */
197
+ function generateValidationSignature(data: any): string {
198
+ const payload = JSON.stringify({
199
+ ...data,
200
+ timestamp: Date.now(),
201
+ checksum: SECURITY_CHECKSUM,
202
+ });
203
+
204
+ return crypto
205
+ .createHash('sha256')
206
+ .update(payload)
207
+ .digest('hex')
208
+ .substring(0, 16);
209
+ }
210
+
211
+ /**
212
+ * Main API route handler with enhanced security
213
+ */
214
+ export async function GET(request: NextRequest) {
215
+ try {
216
+ // Security check
217
+ if (!verifyRequestIntegrity(request)) {
218
+ return NextResponse.json(
219
+ {
220
+ error: 'Invalid request',
221
+ valid: false,
222
+ hasProAccess: false
223
+ },
224
+ { status: 403 }
225
+ );
226
+ }
227
+
228
+ // Generate device fingerprint
229
+ const deviceId = await getDeviceFingerprint(request);
230
+
231
+ // Check rate limiting
232
+ if (!checkRateLimit(deviceId)) {
233
+ return NextResponse.json(
234
+ { error: 'Rate limit exceeded', valid: false },
235
+ { status: 429 }
236
+ );
237
+ }
238
+
239
+ // Check in-memory cache first
240
+ const cacheKey = `validation:${deviceId}`;
241
+ const cached = validationCache.get(cacheKey);
242
+
243
+ if (cached && cached.expiresAt > Date.now()) {
244
+ console.log('[MoonUI] Using cached validation result');
245
+
246
+ return NextResponse.json({
247
+ valid: cached.valid,
248
+ hasProAccess: cached.hasProAccess,
249
+ cached: true,
250
+ });
251
+ }
252
+
253
+ // Get license token
254
+ const licenseToken = process.env.MOONUI_LICENSE_KEY ||
255
+ process.env.NEXT_PUBLIC_MOONUI_DEV_TOKEN ||
256
+ '';
257
+
258
+ // Validate with MoonUI server
259
+ const validation = await validateWithMoonUIServer(licenseToken, deviceId);
260
+
261
+ // Cache the result
262
+ const expiresAt = Date.now() + CACHE_DURATION;
263
+ validationCache.set(cacheKey, {
264
+ ...validation,
265
+ timestamp: Date.now(),
266
+ expiresAt,
267
+ });
268
+
269
+ // Set status cookie for client (readable)
270
+ const cookieStore = await cookies();
271
+ cookieStore.set('moonui_pro_status', JSON.stringify({
272
+ valid: validation.valid,
273
+ hasProAccess: validation.hasProAccess,
274
+ timestamp: Date.now(),
275
+ }), {
276
+ httpOnly: false, // Client needs to read this
277
+ secure: process.env.NODE_ENV === 'production',
278
+ sameSite: 'lax',
279
+ maxAge: CACHE_DURATION / 1000, // Convert to seconds
280
+ });
281
+
282
+ // Prepare response data
283
+ const responseData = {
284
+ valid: validation.valid,
285
+ hasProAccess: validation.hasProAccess,
286
+ isAuthenticated: validation.valid,
287
+ subscriptionPlan: validation.hasProAccess ? 'lifetime' : 'free',
288
+ subscription: {
289
+ status: validation.hasProAccess ? 'active' : 'inactive',
290
+ plan: validation.hasProAccess ? 'lifetime' : 'free',
291
+ },
292
+ _signature: generateValidationSignature({
293
+ valid: validation.valid,
294
+ hasProAccess: validation.hasProAccess,
295
+ deviceId: deviceId,
296
+ }),
297
+ };
298
+
299
+ // Log security events
300
+ if (validation.hasProAccess) {
301
+ console.log(`[MoonUI Security] Pro access granted for device: ${deviceId.substring(0, 8)}...`);
302
+ }
303
+
304
+ return NextResponse.json(responseData);
305
+
306
+ } catch (error) {
307
+ console.error('[MoonUI] Validation error:', error);
308
+
309
+ return NextResponse.json(
310
+ {
311
+ error: 'Internal server error',
312
+ valid: false,
313
+ hasProAccess: false,
314
+ },
315
+ { status: 500 }
316
+ );
317
+ }
318
+ }
319
+
320
+ // Support for Pages Router
321
+ export async function handler(req: any, res: any) {
322
+ if (req.method !== 'GET') {
323
+ return res.status(405).json({ error: 'Method not allowed' });
324
+ }
325
+
326
+ // Convert to NextRequest for consistent handling
327
+ const url = new URL(req.url!, `http://${req.headers.host}`);
328
+ const request = new NextRequest(url, {
329
+ headers: req.headers,
330
+ });
331
+
332
+ const response = await GET(request);
333
+ const data = await response.json();
334
+
335
+ return res.status(response.status).json(data);
336
+ }