1
1
import { NextResponse } from "next/server"
2
2
3
+ import { IS_PREVIEW_DEPLOY } from "@/lib/utils/env"
4
+
3
5
type MatomoExperiment = {
4
6
idexperiment : string
5
7
name : string
@@ -21,31 +23,37 @@ type ABTestConfig = {
21
23
} >
22
24
}
23
25
24
- function isExperimentActive ( experiment : MatomoExperiment ) : boolean {
26
+ const isExperimentActive = ( experiment : MatomoExperiment ) : boolean => {
25
27
const now = new Date ( )
26
28
27
29
// Check start date - if scheduled for future, not active yet
28
30
if ( experiment . start_date ) {
29
31
const startDate = new Date ( experiment . start_date )
30
- if ( now < startDate ) {
31
- return false // Not started yet
32
- }
32
+ if ( now < startDate ) return false
33
33
}
34
34
35
35
// Check end date - if past end date, not active anymore
36
36
if ( experiment . end_date ) {
37
37
const endDate = new Date ( experiment . end_date )
38
- if ( now > endDate ) {
39
- return false // Already ended
40
- }
38
+ if ( now > endDate ) return false
41
39
}
42
40
43
41
// If no scheduling constraints, enabled if created or running
44
- // If within time window, enabled if running
45
42
return [ "created" , "running" ] . includes ( experiment . status )
46
43
}
47
44
45
+ const getPreviewConfig = ( ) => ( {
46
+ AppTest : {
47
+ id : "preview" ,
48
+ enabled : true ,
49
+ variants : [ { name : "Original" , weight : 100 } ] ,
50
+ } ,
51
+ } )
52
+
48
53
export async function GET ( ) {
54
+ // Preview mode: Show menu with original default
55
+ if ( IS_PREVIEW_DEPLOY ) return NextResponse . json ( getPreviewConfig ( ) )
56
+
49
57
try {
50
58
const matomoUrl = process . env . NEXT_PUBLIC_MATOMO_URL
51
59
const apiToken = process . env . MATOMO_API_TOKEN
@@ -57,95 +65,65 @@ export async function GET() {
57
65
)
58
66
}
59
67
60
- // Get the site ID from environment
61
- const siteId = process . env . NEXT_PUBLIC_MATOMO_SITE_ID || "1"
62
-
63
- // Try different API methods for A/B testing
64
- const apiMethods = [
65
- "ExperimentsPlatform.getExperiments" ,
66
- "AbTesting.getExperiments" ,
67
- "Experiments.getExperiments" ,
68
- `AbTesting.getAllExperiments&idSite=${ siteId } ` ,
69
- ]
70
-
71
- let experiments : MatomoExperiment [ ] = [ ]
72
- let apiError = null
73
-
74
- for ( const method of apiMethods ) {
75
- // Add cache busting for development
76
- const cacheBuster =
77
- process . env . NODE_ENV === "development" ? `&cb=${ Date . now ( ) } ` : ""
78
- const matomoApiUrl = `${ matomoUrl } /index.php?module=API&method=${ method } &format=json&token_auth=${ apiToken } ${ cacheBuster } `
79
-
80
- try {
81
- const response = await fetch ( matomoApiUrl , {
82
- next : {
83
- revalidate : process . env . NODE_ENV === "development" ? 0 : 3600 ,
84
- } , // No cache in dev
85
- headers : { "User-Agent" : "ethereum.org-ab-testing/1.0" } ,
86
- } )
87
-
88
- const data = await response . json ( )
89
-
90
- if ( data . result !== "error" && Array . isArray ( data ) ) {
91
- experiments = data
92
- break
93
- } else if ( data . result === "error" ) {
94
- apiError = data . message
95
- }
96
- } catch ( error ) {
97
- // Continue to next method
98
- }
99
- }
68
+ const siteId = process . env . NEXT_PUBLIC_MATOMO_SITE_ID || "4"
100
69
101
- // If no API method worked, use fallback
102
- if ( experiments . length === 0 ) {
103
- console . warn (
104
- `[AB Config] All API methods failed. Last error: ${ apiError } `
105
- )
70
+ // Add cache busting for development
71
+ const cacheBuster =
72
+ process . env . NODE_ENV === "development" ? `&cb=${ Date . now ( ) } ` : ""
73
+ const matomoApiUrl = `${ matomoUrl } /index.php?module=API&method=AbTesting.getAllExperiments&idSite=${ siteId } &format=json&token_auth=${ apiToken } ${ cacheBuster } `
106
74
107
- const fallbackConfig = { }
75
+ const response = await fetch ( matomoApiUrl , {
76
+ next : { revalidate : process . env . NODE_ENV === "development" ? 0 : 3600 } ,
77
+ headers : { "User-Agent" : "ethereum.org-ab-testing/1.0" } ,
78
+ } )
108
79
109
- return NextResponse . json ( fallbackConfig , {
110
- headers : {
111
- "Cache-Control" : "s-max-age=300, stale-while-revalidate=600" ,
112
- } ,
113
- } )
80
+ const data = await response . json ( )
81
+
82
+ if ( data . result === "error" || ! Array . isArray ( data ) ) {
83
+ console . error (
84
+ "[AB Config] Matomo API error:" ,
85
+ data . message || "Invalid response"
86
+ )
87
+ return NextResponse . json (
88
+ { } ,
89
+ {
90
+ headers : {
91
+ "Cache-Control" : "s-max-age=300, stale-while-revalidate=600" ,
92
+ } ,
93
+ }
94
+ )
114
95
}
115
96
97
+ const experiments : MatomoExperiment [ ] = data
98
+
116
99
// Transform Matomo experiments to our config format
117
100
const config : Record < string , ABTestConfig > = { }
118
101
119
- experiments . forEach ( ( exp ) => {
120
- // Include all experiments with variations (let scheduling handle timing )
121
- if ( exp . variations && exp . variations . length > 0 ) {
102
+ experiments
103
+ . filter ( ( exp ) => exp . variations && exp . variations . length > 0 )
104
+ . forEach ( ( exp ) => {
122
105
// Calculate Original variant weight (100% - sum of all variations)
123
106
const variationsTotalWeight = exp . variations . reduce (
124
- ( sum , variation ) => {
125
- return sum + ( variation . percentage || 0 )
126
- } ,
107
+ ( sum , variation ) => sum + ( variation . percentage || 0 ) ,
127
108
0
128
109
)
129
110
const originalWeight = 100 - variationsTotalWeight
130
111
131
112
// Build variants array starting with "Original"
132
- const variants = [ { name : "Original" , weight : originalWeight } ]
133
-
134
- // Add variations from Matomo (use actual percentages)
135
- exp . variations . forEach ( ( variation ) => {
136
- variants . push ( {
113
+ const variants = [
114
+ { name : "Original" , weight : originalWeight } ,
115
+ ...exp . variations . map ( ( variation ) => ( {
137
116
name : variation . name ,
138
117
weight : variation . percentage || 0 ,
139
- } )
140
- } )
118
+ } ) ) ,
119
+ ]
141
120
142
121
config [ exp . name ] = {
143
122
id : exp . idexperiment ,
144
123
enabled : isExperimentActive ( exp ) ,
145
- variants : variants ,
124
+ variants,
146
125
}
147
- }
148
- } )
126
+ } )
149
127
150
128
return NextResponse . json ( config , {
151
129
headers : {
0 commit comments