@@ -8,11 +8,9 @@ const TempPasswordSchema = z.object({
8
8
const AuthorisedItemSchema = z . object ( {
9
9
project : z . string ( ) ,
10
10
name : z . string ( ) ,
11
- url : z
12
- . string ( )
13
- . regex ( / ^ (?: h t t p s ? : \/ \/ .+ \. g i t | g i t @ [ ^ : ] + : [ ^ / ] + \/ .+ \. g i t ) $ / , {
14
- message : 'Must be a Git HTTPS URL (https://... .git) or SSH URL (git@...:... .git)' ,
15
- } ) ,
11
+ url : z . string ( ) . regex ( / ^ (?: h t t p s ? : \/ \/ .+ \. g i t | g i t @ [ ^ : ] + : [ ^ / ] + \/ .+ \. g i t ) $ / i, {
12
+ message : 'Must be a Git HTTPS URL (https://... .git) or SSH URL (git@...:... .git)' ,
13
+ } ) ,
16
14
} ) ;
17
15
18
16
const FsSinkSchema = z . object ( {
@@ -87,11 +85,82 @@ const AttestationQuestionSchema = z.object({
87
85
} ) ,
88
86
} ) ;
89
87
88
+ export const RateLimitSchema = z
89
+ . object ( {
90
+ windowMs : z . number ( { description : 'Sliding window in milliseconds' } ) ,
91
+ limit : z . number ( { description : 'Maximum number of requests' } ) ,
92
+ statusCode : z . number ( ) . optional ( ) . default ( 429 ) ,
93
+ message : z . string ( ) . optional ( ) . default ( 'Too many requests' ) ,
94
+ } )
95
+ . strict ( ) ;
96
+
97
+ const FileConfigSourceSchema = z
98
+ . object ( {
99
+ type : z . literal ( 'file' ) ,
100
+ enabled : z . boolean ( ) . default ( false ) ,
101
+ path : z . string ( ) ,
102
+ } )
103
+ . strict ( ) ;
104
+
105
+ const HttpConfigSourceSchema = z
106
+ . object ( {
107
+ type : z . literal ( 'http' ) ,
108
+ enabled : z . boolean ( ) . default ( false ) ,
109
+ url : z . string ( ) . url ( ) ,
110
+ headers : z . record ( z . string ( ) ) . default ( { } ) ,
111
+ auth : z
112
+ . object ( {
113
+ type : z . literal ( 'bearer' ) ,
114
+ token : z . string ( ) . default ( '' ) ,
115
+ } )
116
+ . strict ( )
117
+ . default ( { type : 'bearer' , token : '' } ) ,
118
+ } )
119
+ . strict ( ) ;
120
+
121
+ const GitConfigSourceSchema = z
122
+ . object ( {
123
+ type : z . literal ( 'git' ) ,
124
+ enabled : z . boolean ( ) . default ( false ) ,
125
+ repository : z . string ( ) ,
126
+ branch : z . string ( ) . default ( 'main' ) ,
127
+ path : z . string ( ) ,
128
+ auth : z
129
+ . object ( {
130
+ type : z . literal ( 'ssh' ) ,
131
+ privateKeyPath : z . string ( ) ,
132
+ } )
133
+ . strict ( ) ,
134
+ } )
135
+ . strict ( ) ;
136
+
137
+ const ConfigSourceSchema = z . discriminatedUnion ( 'type' , [
138
+ FileConfigSourceSchema ,
139
+ HttpConfigSourceSchema ,
140
+ GitConfigSourceSchema ,
141
+ ] ) ;
142
+
143
+ export const ConfigurationSourcesSchema = z
144
+ . object ( {
145
+ enabled : z . boolean ( ) ,
146
+ reloadIntervalSeconds : z . number ( ) . optional ( ) . default ( 60 ) ,
147
+ merge : z . boolean ( ) . optional ( ) . default ( false ) ,
148
+ sources : z . array ( ConfigSourceSchema ) . default ( [ ] ) ,
149
+ } )
150
+ . strict ( ) ;
151
+
90
152
export const ConfigSchema = z
91
153
. object ( {
92
154
proxyUrl : z . string ( ) . url ( ) . default ( 'https://github.com' ) ,
93
155
cookieSecret : z . string ( ) . default ( '' ) ,
94
156
sessionMaxAgeHours : z . number ( ) . int ( ) . positive ( ) . default ( 12 ) ,
157
+ rateLimit : RateLimitSchema . default ( { windowMs : 600000 , limit : 150 } ) ,
158
+ configurationSources : ConfigurationSourcesSchema . default ( {
159
+ enabled : false ,
160
+ reloadIntervalSeconds : 60 ,
161
+ merge : false ,
162
+ sources : [ ] ,
163
+ } ) ,
95
164
tempPassword : TempPasswordSchema . default ( { } ) ,
96
165
authorisedList : z . array ( AuthorisedItemSchema ) . default ( [ ] ) ,
97
166
sink : z . array ( SinkSchema ) . default ( [ ] ) ,
0 commit comments