@owlmeans/api-config-server 0.1.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/LICENSE +21 -0
- package/README.md +367 -0
- package/build/.gitkeep +0 -0
- package/build/actions/config.d.ts +4 -0
- package/build/actions/config.d.ts.map +1 -0
- package/build/actions/config.js +34 -0
- package/build/actions/config.js.map +1 -0
- package/build/actions/index.d.ts +2 -0
- package/build/actions/index.d.ts.map +1 -0
- package/build/actions/index.js +2 -0
- package/build/actions/index.js.map +1 -0
- package/build/index.d.ts +2 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +2 -0
- package/build/index.js.map +1 -0
- package/build/modules.d.ts +3 -0
- package/build/modules.d.ts.map +1 -0
- package/build/modules.js +8 -0
- package/build/modules.js.map +1 -0
- package/package.json +37 -0
- package/src/actions/config.ts +50 -0
- package/src/actions/index.ts +2 -0
- package/src/index.ts +2 -0
- package/src/modules.ts +11 -0
- package/tsconfig.json +14 -0
- package/tsconfig.tsbuildinfo +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 OwlMeans Common — Fullstack typescript framework
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
# @owlmeans/api-config-server
|
|
2
|
+
|
|
3
|
+
The **@owlmeans/api-config-server** package provides server-side functionality for advertising API configuration to clients in OwlMeans Common Libraries, designed for fullstack microservices and microclients development with focus on security and proper authentication and authorization.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
This package serves as the server-side component of the OwlMeans configuration system that:
|
|
8
|
+
|
|
9
|
+
- **Advertises service configuration** to clients through API endpoints
|
|
10
|
+
- **Filters sensitive information** before sending configuration to clients
|
|
11
|
+
- **Supports multi-service architecture** by exposing service endpoint information
|
|
12
|
+
- **Integrates with server modules** for seamless API endpoint creation
|
|
13
|
+
- **Provides secure configuration sharing** with proper filtering of secrets and internal data
|
|
14
|
+
|
|
15
|
+
## Core Concepts
|
|
16
|
+
|
|
17
|
+
### Configuration Advertisement
|
|
18
|
+
The server advertises its configuration to clients through a REST API endpoint, allowing clients to discover available services, their endpoints, and relevant configuration data.
|
|
19
|
+
|
|
20
|
+
### Information Filtering
|
|
21
|
+
The package carefully filters configuration data to ensure sensitive information (like secrets, API keys, and internal configurations) is not exposed to clients.
|
|
22
|
+
|
|
23
|
+
### Service Discovery
|
|
24
|
+
Clients can discover all available services, their hosts, ports, and base paths through the configuration API, enabling dynamic service discovery.
|
|
25
|
+
|
|
26
|
+
### Plugin and Record Sharing
|
|
27
|
+
The server can share frontend-compatible plugins and allowed configuration records with clients.
|
|
28
|
+
|
|
29
|
+
## API Reference
|
|
30
|
+
|
|
31
|
+
### Modules
|
|
32
|
+
|
|
33
|
+
#### `modules`
|
|
34
|
+
|
|
35
|
+
Exported server modules array containing the elevated API config module with server-side handler.
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { modules } from '@owlmeans/api-config-server'
|
|
39
|
+
|
|
40
|
+
// Register all server API config modules
|
|
41
|
+
context.registerModules(modules)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Actions
|
|
45
|
+
|
|
46
|
+
#### `advertise`
|
|
47
|
+
|
|
48
|
+
The main action handler that processes API configuration requests and returns filtered configuration.
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import { advertise } from '@owlmeans/api-config-server'
|
|
52
|
+
|
|
53
|
+
// This handler is automatically attached to the API config module
|
|
54
|
+
// It processes requests and returns filtered configuration
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Returns:** `ApiConfig` object containing:
|
|
58
|
+
- `debug`: Debug configuration (filtered)
|
|
59
|
+
- `brand`: Branding information
|
|
60
|
+
- `services`: Service endpoint information (host, port, base, service name, type)
|
|
61
|
+
- `plugins`: Frontend-compatible plugins
|
|
62
|
+
- `[CONFIG_RECORD]`: Allowed configuration records
|
|
63
|
+
- `oidc`: OIDC configuration (filtered, no secrets)
|
|
64
|
+
- Additional safe configuration fields
|
|
65
|
+
|
|
66
|
+
**Filtering Behavior:**
|
|
67
|
+
- Removes sensitive keys defined in `notAdvertizedConfigKeys`
|
|
68
|
+
- Filters service configurations to include only public information
|
|
69
|
+
- Removes secrets from OIDC configuration
|
|
70
|
+
- Only includes frontend-compatible plugins
|
|
71
|
+
- Only includes allowed configuration record types
|
|
72
|
+
|
|
73
|
+
## Configuration Filtering
|
|
74
|
+
|
|
75
|
+
The package implements strict filtering to protect sensitive information:
|
|
76
|
+
|
|
77
|
+
### Service Information
|
|
78
|
+
From each service configuration, only these fields are shared:
|
|
79
|
+
- `service`: Service name
|
|
80
|
+
- `type`: Service type (Frontend/Backend)
|
|
81
|
+
- `host`: Service host
|
|
82
|
+
- `port`: Service port
|
|
83
|
+
- `base`: Base path
|
|
84
|
+
|
|
85
|
+
### OIDC Configuration
|
|
86
|
+
For OIDC configuration, sensitive fields are filtered out:
|
|
87
|
+
- **Removed**: `secret`, `apiClientId`
|
|
88
|
+
- **Kept**: `clientCookie`, public provider information
|
|
89
|
+
- **Internal providers**: Completely filtered out (where `internal: true`)
|
|
90
|
+
|
|
91
|
+
### Plugin Filtering
|
|
92
|
+
Only plugins marked as `AppType.Frontend` are included in the advertised configuration.
|
|
93
|
+
|
|
94
|
+
### Configuration Records
|
|
95
|
+
Only configuration records with types listed in `allowedConfigRecords` are shared with clients.
|
|
96
|
+
|
|
97
|
+
## Usage Examples
|
|
98
|
+
|
|
99
|
+
### Basic Server Setup
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
import { makeServerContext, makeServerConfig } from '@owlmeans/server-context'
|
|
103
|
+
import { modules } from '@owlmeans/api-config-server'
|
|
104
|
+
import { AppType } from '@owlmeans/context'
|
|
105
|
+
|
|
106
|
+
// Create server configuration
|
|
107
|
+
const config = makeServerConfig(AppType.Backend, 'config-server', {
|
|
108
|
+
host: 'localhost',
|
|
109
|
+
port: 3000,
|
|
110
|
+
services: {
|
|
111
|
+
'user-service': {
|
|
112
|
+
service: 'user-service',
|
|
113
|
+
type: AppType.Backend,
|
|
114
|
+
host: 'users.api.com',
|
|
115
|
+
port: 8080,
|
|
116
|
+
base: '/api/v1'
|
|
117
|
+
},
|
|
118
|
+
'auth-service': {
|
|
119
|
+
service: 'auth-service',
|
|
120
|
+
type: AppType.Backend,
|
|
121
|
+
host: 'auth.api.com',
|
|
122
|
+
port: 8081,
|
|
123
|
+
secret: 'very-secret-key' // This will be filtered out
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
debug: { all: true }
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
// Create and configure context
|
|
130
|
+
const context = makeServerContext(config)
|
|
131
|
+
|
|
132
|
+
// Register API config modules
|
|
133
|
+
context.registerModules(modules)
|
|
134
|
+
|
|
135
|
+
// Configure and initialize
|
|
136
|
+
context.configure()
|
|
137
|
+
await context.init()
|
|
138
|
+
|
|
139
|
+
// API config endpoint is now available at the configured route
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Integration with Express
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
import express from 'express'
|
|
146
|
+
import { modules } from '@owlmeans/api-config-server'
|
|
147
|
+
import { API_CONFIG } from '@owlmeans/api-config'
|
|
148
|
+
|
|
149
|
+
const app = express()
|
|
150
|
+
|
|
151
|
+
// After context initialization
|
|
152
|
+
const configModule = context.module(API_CONFIG)
|
|
153
|
+
|
|
154
|
+
// The module has a handler that returns filtered configuration
|
|
155
|
+
app.get('/api/config', async (req, res) => {
|
|
156
|
+
try {
|
|
157
|
+
// The advertise handler is automatically attached
|
|
158
|
+
const request = adaptExpressRequest(req)
|
|
159
|
+
const response = provideResponse()
|
|
160
|
+
|
|
161
|
+
await configModule.handle(request, response)
|
|
162
|
+
|
|
163
|
+
if (response.error) {
|
|
164
|
+
res.status(500).json({ error: response.error.message })
|
|
165
|
+
} else {
|
|
166
|
+
res.json(response.value)
|
|
167
|
+
}
|
|
168
|
+
} catch (error) {
|
|
169
|
+
res.status(500).json({ error: 'Configuration unavailable' })
|
|
170
|
+
}
|
|
171
|
+
})
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Custom Configuration Filtering
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
import { makeServerConfig } from '@owlmeans/server-context'
|
|
178
|
+
import { modules } from '@owlmeans/api-config-server'
|
|
179
|
+
|
|
180
|
+
const config = makeServerConfig(AppType.Backend, 'api-server', {
|
|
181
|
+
// Public configuration (will be shared)
|
|
182
|
+
brand: {
|
|
183
|
+
name: 'My Application',
|
|
184
|
+
version: '1.0.0'
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
// Service configuration (filtered)
|
|
188
|
+
services: {
|
|
189
|
+
'payment-service': {
|
|
190
|
+
service: 'payment-service',
|
|
191
|
+
type: AppType.Backend,
|
|
192
|
+
host: 'payments.api.com',
|
|
193
|
+
port: 443,
|
|
194
|
+
apiKey: 'secret-api-key', // Will be filtered out
|
|
195
|
+
publicEndpoint: '/api/v1' // Will be included as 'base'
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
// OIDC configuration (partially filtered)
|
|
200
|
+
oidc: {
|
|
201
|
+
clientCookie: 'session-cookie', // Will be shared
|
|
202
|
+
providers: [
|
|
203
|
+
{
|
|
204
|
+
name: 'google',
|
|
205
|
+
clientId: 'public-client-id', // Will be shared
|
|
206
|
+
secret: 'client-secret', // Will be filtered out
|
|
207
|
+
issuer: 'https://accounts.google.com'
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
name: 'internal-auth',
|
|
211
|
+
internal: true, // Entire provider will be filtered out
|
|
212
|
+
clientId: 'internal-client',
|
|
213
|
+
secret: 'internal-secret'
|
|
214
|
+
}
|
|
215
|
+
]
|
|
216
|
+
},
|
|
217
|
+
|
|
218
|
+
// Plugins (only frontend plugins shared)
|
|
219
|
+
plugins: [
|
|
220
|
+
{
|
|
221
|
+
name: 'frontend-plugin',
|
|
222
|
+
type: AppType.Frontend,
|
|
223
|
+
config: { theme: 'dark' }
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
name: 'backend-plugin',
|
|
227
|
+
type: AppType.Backend,
|
|
228
|
+
config: { database: 'secret-connection' } // Will be filtered out
|
|
229
|
+
}
|
|
230
|
+
]
|
|
231
|
+
})
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Multi-Service Architecture
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
const config = makeServerConfig(AppType.Backend, 'gateway-server', {
|
|
238
|
+
services: {
|
|
239
|
+
'user-service': {
|
|
240
|
+
service: 'user-service',
|
|
241
|
+
type: AppType.Backend,
|
|
242
|
+
host: 'users.internal.com',
|
|
243
|
+
port: 8080,
|
|
244
|
+
base: '/users'
|
|
245
|
+
},
|
|
246
|
+
'order-service': {
|
|
247
|
+
service: 'order-service',
|
|
248
|
+
type: AppType.Backend,
|
|
249
|
+
host: 'orders.internal.com',
|
|
250
|
+
port: 8081,
|
|
251
|
+
base: '/orders'
|
|
252
|
+
},
|
|
253
|
+
'notification-service': {
|
|
254
|
+
service: 'notification-service',
|
|
255
|
+
type: AppType.Backend,
|
|
256
|
+
host: 'notifications.internal.com',
|
|
257
|
+
port: 8082,
|
|
258
|
+
base: '/notifications'
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
const context = makeServerContext(config)
|
|
264
|
+
context.registerModules(modules)
|
|
265
|
+
await context.configure().init()
|
|
266
|
+
|
|
267
|
+
// Clients can now discover all services through the config API
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## Security Considerations
|
|
271
|
+
|
|
272
|
+
### Information Filtering
|
|
273
|
+
The package implements multiple layers of security:
|
|
274
|
+
|
|
275
|
+
1. **Automatic Secret Filtering** - Known secret fields are automatically removed
|
|
276
|
+
2. **Service Information Limiting** - Only essential service discovery information is shared
|
|
277
|
+
3. **OIDC Security** - Client secrets and internal configurations are filtered
|
|
278
|
+
4. **Plugin Type Filtering** - Only frontend-compatible plugins are shared
|
|
279
|
+
5. **Configuration Record Filtering** - Only explicitly allowed record types are shared
|
|
280
|
+
|
|
281
|
+
### Best Practices
|
|
282
|
+
|
|
283
|
+
1. **Minimize exposed information** - Only include necessary configuration in server config
|
|
284
|
+
2. **Use environment variables** - Store secrets in environment variables, not configuration
|
|
285
|
+
3. **Implement authentication** - Protect the config endpoint with appropriate authentication
|
|
286
|
+
4. **Monitor access** - Log and monitor who accesses configuration endpoints
|
|
287
|
+
5. **Regular security audits** - Review what information is being exposed
|
|
288
|
+
|
|
289
|
+
### Example Security Setup
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
import { modules } from '@owlmeans/api-config-server'
|
|
293
|
+
import { authenticatedGuard } from '@owlmeans/server-auth'
|
|
294
|
+
|
|
295
|
+
// Protect configuration endpoint with authentication
|
|
296
|
+
const configModule = context.module(API_CONFIG)
|
|
297
|
+
configModule.guards = ['authenticated']
|
|
298
|
+
|
|
299
|
+
// Or use custom middleware for additional security
|
|
300
|
+
app.get('/api/config', authenticateRequest, async (req, res) => {
|
|
301
|
+
// Only authenticated clients can access configuration
|
|
302
|
+
})
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## Integration Patterns
|
|
306
|
+
|
|
307
|
+
### Microservices Gateway
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
// Gateway server that advertises all microservice endpoints
|
|
311
|
+
const gatewayConfig = makeServerConfig(AppType.Backend, 'api-gateway', {
|
|
312
|
+
services: {
|
|
313
|
+
...userServiceConfig,
|
|
314
|
+
...orderServiceConfig,
|
|
315
|
+
...paymentServiceConfig,
|
|
316
|
+
...notificationServiceConfig
|
|
317
|
+
}
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
// Clients connect to gateway and get all service information
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Development vs Production
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
const isDevelopment = process.env.NODE_ENV === 'development'
|
|
327
|
+
|
|
328
|
+
const config = makeServerConfig(AppType.Backend, 'api-server', {
|
|
329
|
+
debug: isDevelopment ? { all: true } : {},
|
|
330
|
+
services: {
|
|
331
|
+
'user-service': {
|
|
332
|
+
service: 'user-service',
|
|
333
|
+
type: AppType.Backend,
|
|
334
|
+
host: isDevelopment ? 'localhost' : 'users.prod.com',
|
|
335
|
+
port: isDevelopment ? 3001 : 443,
|
|
336
|
+
base: '/api/v1'
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
})
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Error Handling
|
|
343
|
+
|
|
344
|
+
The package handles configuration errors gracefully:
|
|
345
|
+
|
|
346
|
+
- **Missing configuration** - Returns empty or default values
|
|
347
|
+
- **Invalid service configs** - Filters out malformed service entries
|
|
348
|
+
- **Plugin errors** - Continues processing even if plugin filtering fails
|
|
349
|
+
- **OIDC parsing errors** - Falls back to basic configuration
|
|
350
|
+
|
|
351
|
+
## Related Packages
|
|
352
|
+
|
|
353
|
+
- [`@owlmeans/api-config`](../api-config) - Common API config module definitions
|
|
354
|
+
- [`@owlmeans/api-config-client`](../api-config-client) - Client-side configuration fetching
|
|
355
|
+
- [`@owlmeans/server-context`](../server-context) - Server-side context management
|
|
356
|
+
- [`@owlmeans/server-module`](../server-module) - Server-side module system
|
|
357
|
+
- [`@owlmeans/config`](../config) - Configuration management utilities
|
|
358
|
+
|
|
359
|
+
## Dependencies
|
|
360
|
+
|
|
361
|
+
This package depends on:
|
|
362
|
+
- `@owlmeans/api-config` - Common API config definitions and constants
|
|
363
|
+
- `@owlmeans/server-module` - Server-side module elevation and handling
|
|
364
|
+
- `@owlmeans/server-context` - Server context types and configuration
|
|
365
|
+
- `@owlmeans/server-api` - Request handling utilities
|
|
366
|
+
- `@owlmeans/config` - Configuration management and plugin types
|
|
367
|
+
- `@owlmeans/context` - Core context functionality and types
|
package/build/.gitkeep
ADDED
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/actions/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AACjE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AASrD,eAAO,MAAM,SAAS,EAAE,kBAAkB,CAAC,SAAS,CAuClD,CAAA"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { notAdvertizedConfigKeys, allowedConfigRecords } from '@owlmeans/api-config';
|
|
2
|
+
import { handleRequest } from '@owlmeans/server-api';
|
|
3
|
+
import { PLUGINS } from '@owlmeans/config';
|
|
4
|
+
import { AppType, CONFIG_RECORD } from '@owlmeans/context';
|
|
5
|
+
export const advertise = handleRequest(async (_, ctx) => {
|
|
6
|
+
const apiConfig = {
|
|
7
|
+
debug: ctx.cfg.debug ?? {},
|
|
8
|
+
brand: {},
|
|
9
|
+
services: Object.fromEntries(Object.entries(ctx.cfg.services ?? {}).map(([service, config]) => [
|
|
10
|
+
service, {
|
|
11
|
+
service: config.service,
|
|
12
|
+
type: config.type,
|
|
13
|
+
host: config.host,
|
|
14
|
+
port: config.port,
|
|
15
|
+
base: config.base
|
|
16
|
+
}
|
|
17
|
+
])),
|
|
18
|
+
plugins: (ctx.cfg[PLUGINS] ?? [])
|
|
19
|
+
.filter((plugin) => plugin.type === AppType.Frontend),
|
|
20
|
+
[CONFIG_RECORD]: (ctx.cfg[CONFIG_RECORD] ?? []).filter((record) => record.recordType != null && allowedConfigRecords.includes(record.recordType)),
|
|
21
|
+
...(Object.fromEntries(Object.entries(ctx.cfg).filter(([key]) => ![
|
|
22
|
+
'debug', 'services', PLUGINS, ...notAdvertizedConfigKeys
|
|
23
|
+
].includes(key)))),
|
|
24
|
+
...("oidc" in ctx.cfg ? {
|
|
25
|
+
oidc: {
|
|
26
|
+
clientCookie: ctx.cfg.oidc.clientCookie,
|
|
27
|
+
providers: ctx.cfg.oidc.providers?.map(provider => Object.fromEntries(Object.entries(provider)
|
|
28
|
+
.filter(([key]) => !['secret', 'apiClientId'].includes(key)))).filter(provider => provider.internal !== true),
|
|
29
|
+
}
|
|
30
|
+
} : {})
|
|
31
|
+
};
|
|
32
|
+
return apiConfig;
|
|
33
|
+
});
|
|
34
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/actions/config.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AACpF,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAG1D,MAAM,CAAC,MAAM,SAAS,GAAkC,aAAa,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE;IACrF,MAAM,SAAS,GAAc;QAC3B,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE;QAC1B,KAAK,EAAE,EAAE;QACT,QAAQ,EAAE,MAAM,CAAC,WAAW,CAC1B,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,IAAI,EAAkB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC;YAChF,OAAO,EAAE;gBACP,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,IAAI,EAAE,MAAM,CAAC,IAAI;aAClB;SACF,CAAC,CACH;QACD,OAAO,EAAE,CAAE,GAAG,CAAC,GAAiD,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;aAC7E,MAAM,CAAC,CAAC,MAAoB,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,OAAO,CAAC,QAAQ,CAAC;QACrE,CAAC,aAAa,CAAC,EAAE,CAAE,GAAG,CAAC,GAAiD,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CACnG,CAAC,MAAoB,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,IAAI,IAAI,IAAI,oBAAoB,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CACxG;QACD,GAAG,CACD,MAAM,CAAC,WAAW,CAChB,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;YACzC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,uBAAuB;SACzD,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CACjB,CACF;QACD,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;YACtB,IAAI,EAAE;gBACJ,YAAY,EAAG,GAAG,CAAC,GAAG,CAAC,IAAkC,CAAC,YAAY;gBACtE,SAAS,EAAG,GAAG,CAAC,GAAG,CAAC,IAAgC,CAAC,SAAS,EAAE,GAAG,CACjE,QAAQ,CAAC,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;qBACpD,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAChE,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,KAAK,IAAI,CAAC;aACjD;SACF,CAAC,CAAC,CAAC,EAAE,CAAC;KACR,CAAA;IAED,OAAO,SAAS,CAAA;AAClB,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/actions/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/actions/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA"}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,cAAc,CAAA"}
|
package/build/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,cAAc,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modules.d.ts","sourceRoot":"","sources":["../src/modules.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAO3D,eAAO,MAAM,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,EAE1C,CAAA"}
|
package/build/modules.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { elevate } from '@owlmeans/server-module';
|
|
2
|
+
import { modules as list, API_CONFIG } from '@owlmeans/api-config';
|
|
3
|
+
import { config } from './actions/index.js';
|
|
4
|
+
elevate(list, API_CONFIG, config.advertise);
|
|
5
|
+
export const modules = [
|
|
6
|
+
...list.filter((module) => module.getAlias() === API_CONFIG)
|
|
7
|
+
];
|
|
8
|
+
//# sourceMappingURL=modules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modules.js","sourceRoot":"","sources":["../src/modules.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAA;AACjD,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAClE,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAE3C,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,SAAS,CAAC,CAAA;AAE3C,MAAM,CAAC,MAAM,OAAO,GAA4B;IAC9C,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,EAAmC,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,KAAK,UAAU,CAAC;CAC9F,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@owlmeans/api-config-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "tsc -b",
|
|
7
|
+
"dev": "sleep 18 && nodemon -e ts,tsx,json --watch src --exec \"tsc -p ./tsconfig.json\"",
|
|
8
|
+
"watch": "tsc -b -w --preserveWatchOutput --pretty"
|
|
9
|
+
},
|
|
10
|
+
"main": "build/index.js",
|
|
11
|
+
"module": "build/index.js",
|
|
12
|
+
"types": "build/index.d.ts",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"import": "./build/index.js",
|
|
16
|
+
"require": "./build/index.js",
|
|
17
|
+
"default": "./build/index.js",
|
|
18
|
+
"module": "./build/index.js",
|
|
19
|
+
"types": "./build/index.d.ts"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@owlmeans/api-config": "^0.1.0",
|
|
24
|
+
"@owlmeans/server-api": "^0.1.0",
|
|
25
|
+
"@owlmeans/server-context": "^0.1.0",
|
|
26
|
+
"@owlmeans/server-module": "^0.1.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^22.7.8",
|
|
30
|
+
"nodemon": "^3.1.7",
|
|
31
|
+
"typescript": "^5.6.3"
|
|
32
|
+
},
|
|
33
|
+
"private": false,
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { RefedModuleHandler } from '@owlmeans/server-module'
|
|
2
|
+
import type { ApiConfig } from '@owlmeans/api-config'
|
|
3
|
+
import type { ServerConfig } from '@owlmeans/server-context'
|
|
4
|
+
import type { PluginConfig } from '@owlmeans/config'
|
|
5
|
+
import { notAdvertizedConfigKeys, allowedConfigRecords } from '@owlmeans/api-config'
|
|
6
|
+
import { handleRequest } from '@owlmeans/server-api'
|
|
7
|
+
import { PLUGINS } from '@owlmeans/config'
|
|
8
|
+
import { AppType, CONFIG_RECORD } from '@owlmeans/context'
|
|
9
|
+
import type { ConfigRecord } from '@owlmeans/context'
|
|
10
|
+
|
|
11
|
+
export const advertise: RefedModuleHandler<ApiConfig> = handleRequest(async (_, ctx) => {
|
|
12
|
+
const apiConfig: ApiConfig = {
|
|
13
|
+
debug: ctx.cfg.debug ?? {},
|
|
14
|
+
brand: {},
|
|
15
|
+
services: Object.fromEntries(
|
|
16
|
+
Object.entries(ctx.cfg.services ?? {} as ServerConfig).map(([service, config]) => [
|
|
17
|
+
service, {
|
|
18
|
+
service: config.service,
|
|
19
|
+
type: config.type,
|
|
20
|
+
host: config.host,
|
|
21
|
+
port: config.port,
|
|
22
|
+
base: config.base
|
|
23
|
+
}
|
|
24
|
+
])
|
|
25
|
+
),
|
|
26
|
+
plugins: ((ctx.cfg as unknown as Record<string, PluginConfig[]>)[PLUGINS] ?? [])
|
|
27
|
+
.filter((plugin: PluginConfig) => plugin.type === AppType.Frontend),
|
|
28
|
+
[CONFIG_RECORD]: ((ctx.cfg as unknown as Record<string, ConfigRecord[]>)[CONFIG_RECORD] ?? []).filter(
|
|
29
|
+
(record: ConfigRecord) => record.recordType != null && allowedConfigRecords.includes(record.recordType)
|
|
30
|
+
),
|
|
31
|
+
...(
|
|
32
|
+
Object.fromEntries(
|
|
33
|
+
Object.entries(ctx.cfg).filter(([key]) => ![
|
|
34
|
+
'debug', 'services', PLUGINS, ...notAdvertizedConfigKeys
|
|
35
|
+
].includes(key))
|
|
36
|
+
)
|
|
37
|
+
),
|
|
38
|
+
...("oidc" in ctx.cfg ? {
|
|
39
|
+
oidc: {
|
|
40
|
+
clientCookie: (ctx.cfg.oidc as { clientCookie: unknown }).clientCookie,
|
|
41
|
+
providers: (ctx.cfg.oidc as { providers: Object[] }).providers?.map(
|
|
42
|
+
provider => Object.fromEntries(Object.entries(provider)
|
|
43
|
+
.filter(([key]) => !['secret', 'apiClientId'].includes(key)))
|
|
44
|
+
).filter(provider => provider.internal !== true),
|
|
45
|
+
}
|
|
46
|
+
} : {})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return apiConfig
|
|
50
|
+
})
|
package/src/index.ts
ADDED
package/src/modules.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
|
|
2
|
+
import type { ServerModule } from '@owlmeans/server-module'
|
|
3
|
+
import { elevate } from '@owlmeans/server-module'
|
|
4
|
+
import { modules as list, API_CONFIG } from '@owlmeans/api-config'
|
|
5
|
+
import { config } from './actions/index.js'
|
|
6
|
+
|
|
7
|
+
elevate(list, API_CONFIG, config.advertise)
|
|
8
|
+
|
|
9
|
+
export const modules: ServerModule<unknown>[] = [
|
|
10
|
+
...list.filter((module): module is ServerModule<unknown> => module.getAlias() === API_CONFIG)
|
|
11
|
+
]
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": [
|
|
3
|
+
"../tsconfig.default.json",
|
|
4
|
+
],
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"rootDir": "./src/", /* Specify the root folder within your source files. */
|
|
7
|
+
"outDir": "./build/", /* Specify an output folder for all emitted files. */
|
|
8
|
+
},
|
|
9
|
+
"exclude": [
|
|
10
|
+
"./dist/**/*",
|
|
11
|
+
"./build/**/*",
|
|
12
|
+
"./*.ts"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["./src/index.ts","./src/modules.ts","./src/actions/config.ts","./src/actions/index.ts"],"version":"5.6.3"}
|