sovrium 0.1.0 → 0.2.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/README.md +65 -185
- package/package.json +6 -6
- package/src/application/use-cases/server/static-content-generators.ts +18 -2
- package/src/domain/models/api/activity.ts +87 -0
- package/src/domain/models/api/comments.ts +131 -0
- package/src/domain/models/api/index.ts +9 -0
- package/src/domain/models/api/params.ts +83 -0
- package/src/infrastructure/server/route-setup/openapi-routes/activity-routes.ts +72 -0
- package/src/infrastructure/server/route-setup/openapi-routes/analytics-routes.ts +176 -0
- package/src/infrastructure/server/route-setup/openapi-routes/batch-routes.ts +215 -0
- package/src/infrastructure/server/route-setup/openapi-routes/health-routes.ts +38 -0
- package/src/infrastructure/server/route-setup/openapi-routes/record-routes.ts +444 -0
- package/src/infrastructure/server/route-setup/openapi-routes/table-routes.ts +100 -0
- package/src/infrastructure/server/route-setup/openapi-routes/view-routes.ts +104 -0
- package/src/infrastructure/server/route-setup/openapi-schema.ts +40 -40
- package/src/presentation/styling/parse-style.ts +17 -9
- package/schemas/0.0.1/app.openapi.json +0 -70
- package/schemas/0.0.1/app.schema.json +0 -7961
- package/schemas/0.0.2/app.openapi.json +0 -70
- package/schemas/0.0.2/app.schema.json +0 -7456
- package/schemas/0.1.0/app.openapi.json +0 -80
- package/schemas/0.1.0/app.schema.json +0 -8829
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 ESSENTIAL SERVICES
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Business Source License 1.1
|
|
5
|
+
* found in the LICENSE.md file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { z } from 'zod'
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// OpenAPI Path Parameter Schemas
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Table ID path parameter
|
|
16
|
+
*/
|
|
17
|
+
export const tableIdParamSchema = z.object({
|
|
18
|
+
tableId: z.string().describe('Table identifier'),
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Record ID path parameters (includes tableId)
|
|
23
|
+
*/
|
|
24
|
+
export const recordIdParamSchema = z.object({
|
|
25
|
+
tableId: z.string().describe('Table identifier'),
|
|
26
|
+
recordId: z.string().describe('Record identifier'),
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Comment ID path parameters (includes tableId and recordId)
|
|
31
|
+
*/
|
|
32
|
+
export const commentIdParamSchema = z.object({
|
|
33
|
+
tableId: z.string().describe('Table identifier'),
|
|
34
|
+
recordId: z.string().describe('Record identifier'),
|
|
35
|
+
commentId: z.string().describe('Comment identifier'),
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* View ID path parameters (includes tableId)
|
|
40
|
+
*/
|
|
41
|
+
export const viewIdParamSchema = z.object({
|
|
42
|
+
tableId: z.string().describe('Table identifier'),
|
|
43
|
+
viewId: z.string().describe('View identifier'),
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Activity ID path parameter
|
|
48
|
+
*/
|
|
49
|
+
export const activityIdParamSchema = z.object({
|
|
50
|
+
activityId: z.string().describe('Activity log identifier'),
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// ============================================================================
|
|
54
|
+
// OpenAPI Query Parameter Schemas
|
|
55
|
+
// ============================================================================
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* List records query parameters
|
|
59
|
+
*/
|
|
60
|
+
export const listRecordsQuerySchema = z.object({
|
|
61
|
+
page: z.string().optional().describe('Page number (1-indexed)'),
|
|
62
|
+
limit: z.string().optional().describe('Items per page'),
|
|
63
|
+
sort: z.string().optional().describe('Sort field'),
|
|
64
|
+
order: z.enum(['asc', 'desc']).optional().describe('Sort order'),
|
|
65
|
+
format: z.enum(['raw', 'display']).optional().describe('Field value format'),
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Activity log query parameters
|
|
70
|
+
*/
|
|
71
|
+
export const activityQuerySchema = z.object({
|
|
72
|
+
page: z.string().optional().describe('Page number'),
|
|
73
|
+
pageSize: z.string().optional().describe('Items per page'),
|
|
74
|
+
tableId: z.string().optional().describe('Filter by table ID'),
|
|
75
|
+
action: z.enum(['create', 'update', 'delete', 'restore']).optional().describe('Filter by action'),
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Create/update comment request body
|
|
80
|
+
*/
|
|
81
|
+
export const commentBodySchema = z.object({
|
|
82
|
+
content: z.string().min(1).max(10_000).describe('Comment text'),
|
|
83
|
+
})
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 ESSENTIAL SERVICES
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Business Source License 1.1
|
|
5
|
+
* found in the LICENSE.md file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { type OpenAPIHono, createRoute } from '@hono/zod-openapi'
|
|
9
|
+
import {
|
|
10
|
+
getActivityLogResponseSchema,
|
|
11
|
+
listActivityLogsResponseSchema,
|
|
12
|
+
} from '@/domain/models/api/activity'
|
|
13
|
+
import { errorResponseSchema } from '@/domain/models/api/error'
|
|
14
|
+
import { activityIdParamSchema, activityQuerySchema } from '@/domain/models/api/params'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Register activity log routes for OpenAPI schema generation
|
|
18
|
+
*/
|
|
19
|
+
export function registerActivityRoutes(app: OpenAPIHono): void {
|
|
20
|
+
// GET /api/activity
|
|
21
|
+
app.openapi(
|
|
22
|
+
createRoute({
|
|
23
|
+
method: 'get',
|
|
24
|
+
path: '/api/activity',
|
|
25
|
+
summary: 'List activity logs',
|
|
26
|
+
description:
|
|
27
|
+
'Returns paginated activity logs with optional filtering by table or action type.',
|
|
28
|
+
operationId: 'listActivityLogs',
|
|
29
|
+
tags: ['activity'],
|
|
30
|
+
request: { query: activityQuerySchema },
|
|
31
|
+
responses: {
|
|
32
|
+
200: {
|
|
33
|
+
content: { 'application/json': { schema: listActivityLogsResponseSchema } },
|
|
34
|
+
description: 'Activity logs',
|
|
35
|
+
},
|
|
36
|
+
401: {
|
|
37
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
38
|
+
description: 'Unauthorized',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
}),
|
|
42
|
+
(c) => c.json({} as never)
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
// GET /api/activity/{activityId}
|
|
46
|
+
app.openapi(
|
|
47
|
+
createRoute({
|
|
48
|
+
method: 'get',
|
|
49
|
+
path: '/api/activity/{activityId}',
|
|
50
|
+
summary: 'Get activity log details',
|
|
51
|
+
description: 'Returns a single activity log entry with full change details.',
|
|
52
|
+
operationId: 'getActivityLog',
|
|
53
|
+
tags: ['activity'],
|
|
54
|
+
request: { params: activityIdParamSchema },
|
|
55
|
+
responses: {
|
|
56
|
+
200: {
|
|
57
|
+
content: { 'application/json': { schema: getActivityLogResponseSchema } },
|
|
58
|
+
description: 'Activity log details',
|
|
59
|
+
},
|
|
60
|
+
401: {
|
|
61
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
62
|
+
description: 'Unauthorized',
|
|
63
|
+
},
|
|
64
|
+
404: {
|
|
65
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
66
|
+
description: 'Activity not found',
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
}),
|
|
70
|
+
(c) => c.json({} as never)
|
|
71
|
+
)
|
|
72
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 ESSENTIAL SERVICES
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Business Source License 1.1
|
|
5
|
+
* found in the LICENSE.md file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { type OpenAPIHono, createRoute } from '@hono/zod-openapi'
|
|
9
|
+
import {
|
|
10
|
+
analyticsCollectSchema,
|
|
11
|
+
analyticsQuerySchema,
|
|
12
|
+
analyticsOverviewResponseSchema,
|
|
13
|
+
analyticsTopPagesResponseSchema,
|
|
14
|
+
analyticsTopReferrersResponseSchema,
|
|
15
|
+
analyticsDevicesResponseSchema,
|
|
16
|
+
analyticsCampaignsResponseSchema,
|
|
17
|
+
} from '@/domain/models/api/analytics'
|
|
18
|
+
import { successResponseSchema } from '@/domain/models/api/common'
|
|
19
|
+
import { errorResponseSchema } from '@/domain/models/api/error'
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Register analytics routes for OpenAPI schema generation
|
|
23
|
+
*/
|
|
24
|
+
export function registerAnalyticsRoutes(app: OpenAPIHono): void {
|
|
25
|
+
// POST /api/analytics/collect
|
|
26
|
+
app.openapi(
|
|
27
|
+
createRoute({
|
|
28
|
+
method: 'post',
|
|
29
|
+
path: '/api/analytics/collect',
|
|
30
|
+
summary: 'Collect analytics event',
|
|
31
|
+
description: 'Records a page view event. Uses single-letter keys to minimize payload size.',
|
|
32
|
+
operationId: 'collectAnalytics',
|
|
33
|
+
tags: ['analytics'],
|
|
34
|
+
request: {
|
|
35
|
+
body: {
|
|
36
|
+
content: { 'application/json': { schema: analyticsCollectSchema } },
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
responses: {
|
|
40
|
+
200: {
|
|
41
|
+
content: { 'application/json': { schema: successResponseSchema } },
|
|
42
|
+
description: 'Event collected',
|
|
43
|
+
},
|
|
44
|
+
400: {
|
|
45
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
46
|
+
description: 'Validation error',
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
}),
|
|
50
|
+
(c) => c.json({} as never)
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
// GET /api/analytics/overview
|
|
54
|
+
app.openapi(
|
|
55
|
+
createRoute({
|
|
56
|
+
method: 'get',
|
|
57
|
+
path: '/api/analytics/overview',
|
|
58
|
+
summary: 'Get analytics overview',
|
|
59
|
+
description: 'Returns summary statistics and time series data for the given date range.',
|
|
60
|
+
operationId: 'getAnalyticsOverview',
|
|
61
|
+
tags: ['analytics'],
|
|
62
|
+
request: { query: analyticsQuerySchema },
|
|
63
|
+
responses: {
|
|
64
|
+
200: {
|
|
65
|
+
content: { 'application/json': { schema: analyticsOverviewResponseSchema } },
|
|
66
|
+
description: 'Analytics overview',
|
|
67
|
+
},
|
|
68
|
+
400: {
|
|
69
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
70
|
+
description: 'Invalid date range',
|
|
71
|
+
},
|
|
72
|
+
401: {
|
|
73
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
74
|
+
description: 'Unauthorized',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
}),
|
|
78
|
+
(c) => c.json({} as never)
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
// GET /api/analytics/pages
|
|
82
|
+
app.openapi(
|
|
83
|
+
createRoute({
|
|
84
|
+
method: 'get',
|
|
85
|
+
path: '/api/analytics/pages',
|
|
86
|
+
summary: 'Get top pages',
|
|
87
|
+
description: 'Returns most visited pages with view counts and unique visitors.',
|
|
88
|
+
operationId: 'getAnalyticsTopPages',
|
|
89
|
+
tags: ['analytics'],
|
|
90
|
+
request: { query: analyticsQuerySchema },
|
|
91
|
+
responses: {
|
|
92
|
+
200: {
|
|
93
|
+
content: { 'application/json': { schema: analyticsTopPagesResponseSchema } },
|
|
94
|
+
description: 'Top pages',
|
|
95
|
+
},
|
|
96
|
+
401: {
|
|
97
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
98
|
+
description: 'Unauthorized',
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
}),
|
|
102
|
+
(c) => c.json({} as never)
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
// GET /api/analytics/referrers
|
|
106
|
+
app.openapi(
|
|
107
|
+
createRoute({
|
|
108
|
+
method: 'get',
|
|
109
|
+
path: '/api/analytics/referrers',
|
|
110
|
+
summary: 'Get top referrers',
|
|
111
|
+
description: 'Returns traffic sources ranked by page views.',
|
|
112
|
+
operationId: 'getAnalyticsTopReferrers',
|
|
113
|
+
tags: ['analytics'],
|
|
114
|
+
request: { query: analyticsQuerySchema },
|
|
115
|
+
responses: {
|
|
116
|
+
200: {
|
|
117
|
+
content: { 'application/json': { schema: analyticsTopReferrersResponseSchema } },
|
|
118
|
+
description: 'Top referrers',
|
|
119
|
+
},
|
|
120
|
+
401: {
|
|
121
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
122
|
+
description: 'Unauthorized',
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
}),
|
|
126
|
+
(c) => c.json({} as never)
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
// GET /api/analytics/devices
|
|
130
|
+
app.openapi(
|
|
131
|
+
createRoute({
|
|
132
|
+
method: 'get',
|
|
133
|
+
path: '/api/analytics/devices',
|
|
134
|
+
summary: 'Get device breakdown',
|
|
135
|
+
description: 'Returns visitor breakdown by device type, browser, and operating system.',
|
|
136
|
+
operationId: 'getAnalyticsDevices',
|
|
137
|
+
tags: ['analytics'],
|
|
138
|
+
request: { query: analyticsQuerySchema },
|
|
139
|
+
responses: {
|
|
140
|
+
200: {
|
|
141
|
+
content: { 'application/json': { schema: analyticsDevicesResponseSchema } },
|
|
142
|
+
description: 'Device breakdown',
|
|
143
|
+
},
|
|
144
|
+
401: {
|
|
145
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
146
|
+
description: 'Unauthorized',
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
}),
|
|
150
|
+
(c) => c.json({} as never)
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
// GET /api/analytics/campaigns
|
|
154
|
+
app.openapi(
|
|
155
|
+
createRoute({
|
|
156
|
+
method: 'get',
|
|
157
|
+
path: '/api/analytics/campaigns',
|
|
158
|
+
summary: 'Get campaign breakdown',
|
|
159
|
+
description: 'Returns UTM campaign performance data.',
|
|
160
|
+
operationId: 'getAnalyticsCampaigns',
|
|
161
|
+
tags: ['analytics'],
|
|
162
|
+
request: { query: analyticsQuerySchema },
|
|
163
|
+
responses: {
|
|
164
|
+
200: {
|
|
165
|
+
content: { 'application/json': { schema: analyticsCampaignsResponseSchema } },
|
|
166
|
+
description: 'Campaign breakdown',
|
|
167
|
+
},
|
|
168
|
+
401: {
|
|
169
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
170
|
+
description: 'Unauthorized',
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
}),
|
|
174
|
+
(c) => c.json({} as never)
|
|
175
|
+
)
|
|
176
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 ESSENTIAL SERVICES
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Business Source License 1.1
|
|
5
|
+
* found in the LICENSE.md file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { type OpenAPIHono, createRoute } from '@hono/zod-openapi'
|
|
9
|
+
import { errorResponseSchema } from '@/domain/models/api/error'
|
|
10
|
+
import { tableIdParamSchema } from '@/domain/models/api/params'
|
|
11
|
+
import {
|
|
12
|
+
batchCreateRecordsRequestSchema,
|
|
13
|
+
batchDeleteRecordsRequestSchema,
|
|
14
|
+
batchRestoreRecordsRequestSchema,
|
|
15
|
+
batchUpdateRecordsRequestSchema,
|
|
16
|
+
upsertRecordsRequestSchema,
|
|
17
|
+
} from '@/domain/models/api/request'
|
|
18
|
+
import {
|
|
19
|
+
batchCreateRecordsResponseSchema,
|
|
20
|
+
batchDeleteRecordsResponseSchema,
|
|
21
|
+
batchRestoreRecordsResponseSchema,
|
|
22
|
+
batchUpdateRecordsResponseSchema,
|
|
23
|
+
upsertRecordsResponseSchema,
|
|
24
|
+
} from '@/domain/models/api/tables'
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Register batch operation routes for OpenAPI schema generation
|
|
28
|
+
*/
|
|
29
|
+
export function registerBatchRoutes(app: OpenAPIHono): void {
|
|
30
|
+
// POST /api/tables/{tableId}/records/batch (create)
|
|
31
|
+
app.openapi(
|
|
32
|
+
createRoute({
|
|
33
|
+
method: 'post',
|
|
34
|
+
path: '/api/tables/{tableId}/records/batch',
|
|
35
|
+
summary: 'Batch create records',
|
|
36
|
+
description: 'Creates multiple records in a single request (up to 1000).',
|
|
37
|
+
operationId: 'batchCreateRecords',
|
|
38
|
+
tags: ['records'],
|
|
39
|
+
request: {
|
|
40
|
+
params: tableIdParamSchema,
|
|
41
|
+
body: {
|
|
42
|
+
content: { 'application/json': { schema: batchCreateRecordsRequestSchema } },
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
responses: {
|
|
46
|
+
201: {
|
|
47
|
+
content: { 'application/json': { schema: batchCreateRecordsResponseSchema } },
|
|
48
|
+
description: 'Records created',
|
|
49
|
+
},
|
|
50
|
+
400: {
|
|
51
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
52
|
+
description: 'Validation error',
|
|
53
|
+
},
|
|
54
|
+
401: {
|
|
55
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
56
|
+
description: 'Unauthorized',
|
|
57
|
+
},
|
|
58
|
+
404: {
|
|
59
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
60
|
+
description: 'Table not found',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
}),
|
|
64
|
+
(c) => c.json({} as never, 201)
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
// PATCH /api/tables/{tableId}/records/batch (update)
|
|
68
|
+
app.openapi(
|
|
69
|
+
createRoute({
|
|
70
|
+
method: 'patch',
|
|
71
|
+
path: '/api/tables/{tableId}/records/batch',
|
|
72
|
+
summary: 'Batch update records',
|
|
73
|
+
description: 'Updates multiple records in a single request (up to 100).',
|
|
74
|
+
operationId: 'batchUpdateRecords',
|
|
75
|
+
tags: ['records'],
|
|
76
|
+
request: {
|
|
77
|
+
params: tableIdParamSchema,
|
|
78
|
+
body: {
|
|
79
|
+
content: { 'application/json': { schema: batchUpdateRecordsRequestSchema } },
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
responses: {
|
|
83
|
+
200: {
|
|
84
|
+
content: { 'application/json': { schema: batchUpdateRecordsResponseSchema } },
|
|
85
|
+
description: 'Records updated',
|
|
86
|
+
},
|
|
87
|
+
400: {
|
|
88
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
89
|
+
description: 'Validation error',
|
|
90
|
+
},
|
|
91
|
+
401: {
|
|
92
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
93
|
+
description: 'Unauthorized',
|
|
94
|
+
},
|
|
95
|
+
404: {
|
|
96
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
97
|
+
description: 'Table not found',
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
}),
|
|
101
|
+
(c) => c.json({} as never)
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
// DELETE /api/tables/{tableId}/records/batch
|
|
105
|
+
app.openapi(
|
|
106
|
+
createRoute({
|
|
107
|
+
method: 'delete',
|
|
108
|
+
path: '/api/tables/{tableId}/records/batch',
|
|
109
|
+
summary: 'Batch delete records',
|
|
110
|
+
description: 'Soft-deletes multiple records in a single request (up to 100).',
|
|
111
|
+
operationId: 'batchDeleteRecords',
|
|
112
|
+
tags: ['records'],
|
|
113
|
+
request: {
|
|
114
|
+
params: tableIdParamSchema,
|
|
115
|
+
body: {
|
|
116
|
+
content: { 'application/json': { schema: batchDeleteRecordsRequestSchema } },
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
responses: {
|
|
120
|
+
200: {
|
|
121
|
+
content: { 'application/json': { schema: batchDeleteRecordsResponseSchema } },
|
|
122
|
+
description: 'Records deleted',
|
|
123
|
+
},
|
|
124
|
+
400: {
|
|
125
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
126
|
+
description: 'Validation error',
|
|
127
|
+
},
|
|
128
|
+
401: {
|
|
129
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
130
|
+
description: 'Unauthorized',
|
|
131
|
+
},
|
|
132
|
+
404: {
|
|
133
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
134
|
+
description: 'Table not found',
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
}),
|
|
138
|
+
(c) => c.json({} as never)
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
// POST /api/tables/{tableId}/records/batch/restore
|
|
142
|
+
app.openapi(
|
|
143
|
+
createRoute({
|
|
144
|
+
method: 'post',
|
|
145
|
+
path: '/api/tables/{tableId}/records/batch/restore',
|
|
146
|
+
summary: 'Batch restore deleted records',
|
|
147
|
+
description: 'Restores multiple soft-deleted records in a single request (up to 100).',
|
|
148
|
+
operationId: 'batchRestoreRecords',
|
|
149
|
+
tags: ['records'],
|
|
150
|
+
request: {
|
|
151
|
+
params: tableIdParamSchema,
|
|
152
|
+
body: {
|
|
153
|
+
content: { 'application/json': { schema: batchRestoreRecordsRequestSchema } },
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
responses: {
|
|
157
|
+
200: {
|
|
158
|
+
content: { 'application/json': { schema: batchRestoreRecordsResponseSchema } },
|
|
159
|
+
description: 'Records restored',
|
|
160
|
+
},
|
|
161
|
+
400: {
|
|
162
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
163
|
+
description: 'Validation error',
|
|
164
|
+
},
|
|
165
|
+
401: {
|
|
166
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
167
|
+
description: 'Unauthorized',
|
|
168
|
+
},
|
|
169
|
+
404: {
|
|
170
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
171
|
+
description: 'Table not found',
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
}),
|
|
175
|
+
(c) => c.json({} as never)
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
// POST /api/tables/{tableId}/records/upsert
|
|
179
|
+
app.openapi(
|
|
180
|
+
createRoute({
|
|
181
|
+
method: 'post',
|
|
182
|
+
path: '/api/tables/{tableId}/records/upsert',
|
|
183
|
+
summary: 'Upsert records',
|
|
184
|
+
description:
|
|
185
|
+
'Creates or updates records based on merge fields. Existing records matching the merge fields are updated; new records are created.',
|
|
186
|
+
operationId: 'upsertRecords',
|
|
187
|
+
tags: ['records'],
|
|
188
|
+
request: {
|
|
189
|
+
params: tableIdParamSchema,
|
|
190
|
+
body: {
|
|
191
|
+
content: { 'application/json': { schema: upsertRecordsRequestSchema } },
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
responses: {
|
|
195
|
+
200: {
|
|
196
|
+
content: { 'application/json': { schema: upsertRecordsResponseSchema } },
|
|
197
|
+
description: 'Records upserted',
|
|
198
|
+
},
|
|
199
|
+
400: {
|
|
200
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
201
|
+
description: 'Validation error',
|
|
202
|
+
},
|
|
203
|
+
401: {
|
|
204
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
205
|
+
description: 'Unauthorized',
|
|
206
|
+
},
|
|
207
|
+
404: {
|
|
208
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
209
|
+
description: 'Table not found',
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
}),
|
|
213
|
+
(c) => c.json({} as never)
|
|
214
|
+
)
|
|
215
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 ESSENTIAL SERVICES
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Business Source License 1.1
|
|
5
|
+
* found in the LICENSE.md file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { type OpenAPIHono, createRoute } from '@hono/zod-openapi'
|
|
9
|
+
import { healthResponseSchema } from '@/domain/models/api/health'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Register health check routes for OpenAPI schema generation
|
|
13
|
+
*/
|
|
14
|
+
export function registerHealthRoutes(app: OpenAPIHono): void {
|
|
15
|
+
const healthRoute = createRoute({
|
|
16
|
+
method: 'get',
|
|
17
|
+
path: '/api/health',
|
|
18
|
+
summary: 'Health check endpoint',
|
|
19
|
+
description:
|
|
20
|
+
'Returns server health status. Used by monitoring tools and E2E tests to verify server is running.',
|
|
21
|
+
operationId: 'healthCheck',
|
|
22
|
+
tags: ['infrastructure'],
|
|
23
|
+
responses: {
|
|
24
|
+
200: {
|
|
25
|
+
content: { 'application/json': { schema: healthResponseSchema } },
|
|
26
|
+
description: 'Server is healthy',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
app.openapi(healthRoute, (c) => {
|
|
32
|
+
return c.json({
|
|
33
|
+
status: 'ok' as const,
|
|
34
|
+
timestamp: new Date().toISOString(),
|
|
35
|
+
app: { name: 'Sovrium' },
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
}
|