sovrium 0.0.2 → 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 +67 -185
- package/package.json +26 -17
- 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/CHANGELOG.md +0 -3497
- 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 -80
- package/schemas/0.0.2/app.schema.json +0 -8829
- package/schemas/development/app.openapi.json +0 -70
- package/schemas/development/app.schema.json +0 -7456
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,444 @@
|
|
|
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
|
+
createCommentResponseSchema,
|
|
11
|
+
getCommentResponseSchema,
|
|
12
|
+
getRecordHistoryResponseSchema,
|
|
13
|
+
listCommentsResponseSchema,
|
|
14
|
+
updateCommentResponseSchema,
|
|
15
|
+
} from '@/domain/models/api/comments'
|
|
16
|
+
import { errorResponseSchema } from '@/domain/models/api/error'
|
|
17
|
+
import {
|
|
18
|
+
commentBodySchema,
|
|
19
|
+
commentIdParamSchema,
|
|
20
|
+
listRecordsQuerySchema,
|
|
21
|
+
recordIdParamSchema,
|
|
22
|
+
tableIdParamSchema,
|
|
23
|
+
} from '@/domain/models/api/params'
|
|
24
|
+
import { createRecordRequestSchema, updateRecordRequestSchema } from '@/domain/models/api/request'
|
|
25
|
+
import {
|
|
26
|
+
createRecordResponseSchema,
|
|
27
|
+
deleteRecordResponseSchema,
|
|
28
|
+
getRecordResponseSchema,
|
|
29
|
+
listRecordsResponseSchema,
|
|
30
|
+
restoreRecordResponseSchema,
|
|
31
|
+
updateRecordResponseSchema,
|
|
32
|
+
} from '@/domain/models/api/tables'
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Register record CRUD routes for OpenAPI schema generation
|
|
36
|
+
*/
|
|
37
|
+
export function registerRecordRoutes(app: OpenAPIHono): void {
|
|
38
|
+
registerCrudRoutes(app)
|
|
39
|
+
registerCommentRoutes(app)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function registerCrudRoutes(app: OpenAPIHono): void {
|
|
43
|
+
// GET /api/tables/{tableId}/records
|
|
44
|
+
app.openapi(
|
|
45
|
+
createRoute({
|
|
46
|
+
method: 'get',
|
|
47
|
+
path: '/api/tables/{tableId}/records',
|
|
48
|
+
summary: 'List records',
|
|
49
|
+
description: 'Returns paginated records for a table with optional sorting and filtering.',
|
|
50
|
+
operationId: 'listRecords',
|
|
51
|
+
tags: ['records'],
|
|
52
|
+
request: { params: tableIdParamSchema, query: listRecordsQuerySchema },
|
|
53
|
+
responses: {
|
|
54
|
+
200: {
|
|
55
|
+
content: { 'application/json': { schema: listRecordsResponseSchema } },
|
|
56
|
+
description: 'List of records',
|
|
57
|
+
},
|
|
58
|
+
401: {
|
|
59
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
60
|
+
description: 'Unauthorized',
|
|
61
|
+
},
|
|
62
|
+
404: {
|
|
63
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
64
|
+
description: 'Table not found',
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
}),
|
|
68
|
+
(c) => c.json({} as never)
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
// POST /api/tables/{tableId}/records
|
|
72
|
+
app.openapi(
|
|
73
|
+
createRoute({
|
|
74
|
+
method: 'post',
|
|
75
|
+
path: '/api/tables/{tableId}/records',
|
|
76
|
+
summary: 'Create a record',
|
|
77
|
+
description: 'Creates a new record in the table with the given field values.',
|
|
78
|
+
operationId: 'createRecord',
|
|
79
|
+
tags: ['records'],
|
|
80
|
+
request: {
|
|
81
|
+
params: tableIdParamSchema,
|
|
82
|
+
body: { content: { 'application/json': { schema: createRecordRequestSchema } } },
|
|
83
|
+
},
|
|
84
|
+
responses: {
|
|
85
|
+
201: {
|
|
86
|
+
content: { 'application/json': { schema: createRecordResponseSchema } },
|
|
87
|
+
description: 'Record created',
|
|
88
|
+
},
|
|
89
|
+
400: {
|
|
90
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
91
|
+
description: 'Validation error',
|
|
92
|
+
},
|
|
93
|
+
401: {
|
|
94
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
95
|
+
description: 'Unauthorized',
|
|
96
|
+
},
|
|
97
|
+
404: {
|
|
98
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
99
|
+
description: 'Table not found',
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
}),
|
|
103
|
+
(c) => c.json({} as never, 201)
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
// GET /api/tables/{tableId}/records/{recordId}
|
|
107
|
+
app.openapi(
|
|
108
|
+
createRoute({
|
|
109
|
+
method: 'get',
|
|
110
|
+
path: '/api/tables/{tableId}/records/{recordId}',
|
|
111
|
+
summary: 'Get a record',
|
|
112
|
+
description: 'Returns a single record by ID with all field values.',
|
|
113
|
+
operationId: 'getRecord',
|
|
114
|
+
tags: ['records'],
|
|
115
|
+
request: { params: recordIdParamSchema },
|
|
116
|
+
responses: {
|
|
117
|
+
200: {
|
|
118
|
+
content: { 'application/json': { schema: getRecordResponseSchema } },
|
|
119
|
+
description: 'Record details',
|
|
120
|
+
},
|
|
121
|
+
401: {
|
|
122
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
123
|
+
description: 'Unauthorized',
|
|
124
|
+
},
|
|
125
|
+
404: {
|
|
126
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
127
|
+
description: 'Record not found',
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
}),
|
|
131
|
+
(c) => c.json({} as never)
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
// PATCH /api/tables/{tableId}/records/{recordId}
|
|
135
|
+
app.openapi(
|
|
136
|
+
createRoute({
|
|
137
|
+
method: 'patch',
|
|
138
|
+
path: '/api/tables/{tableId}/records/{recordId}',
|
|
139
|
+
summary: 'Update a record',
|
|
140
|
+
description: 'Updates field values of an existing record.',
|
|
141
|
+
operationId: 'updateRecord',
|
|
142
|
+
tags: ['records'],
|
|
143
|
+
request: {
|
|
144
|
+
params: recordIdParamSchema,
|
|
145
|
+
body: { content: { 'application/json': { schema: updateRecordRequestSchema } } },
|
|
146
|
+
},
|
|
147
|
+
responses: {
|
|
148
|
+
200: {
|
|
149
|
+
content: { 'application/json': { schema: updateRecordResponseSchema } },
|
|
150
|
+
description: 'Record updated',
|
|
151
|
+
},
|
|
152
|
+
400: {
|
|
153
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
154
|
+
description: 'Validation error',
|
|
155
|
+
},
|
|
156
|
+
401: {
|
|
157
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
158
|
+
description: 'Unauthorized',
|
|
159
|
+
},
|
|
160
|
+
404: {
|
|
161
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
162
|
+
description: 'Record not found',
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
}),
|
|
166
|
+
(c) => c.json({} as never)
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
// DELETE /api/tables/{tableId}/records/{recordId}
|
|
170
|
+
app.openapi(
|
|
171
|
+
createRoute({
|
|
172
|
+
method: 'delete',
|
|
173
|
+
path: '/api/tables/{tableId}/records/{recordId}',
|
|
174
|
+
summary: 'Delete a record',
|
|
175
|
+
description: 'Soft-deletes a record. Use the restore endpoint to undo.',
|
|
176
|
+
operationId: 'deleteRecord',
|
|
177
|
+
tags: ['records'],
|
|
178
|
+
request: { params: recordIdParamSchema },
|
|
179
|
+
responses: {
|
|
180
|
+
200: {
|
|
181
|
+
content: { 'application/json': { schema: deleteRecordResponseSchema } },
|
|
182
|
+
description: 'Record deleted',
|
|
183
|
+
},
|
|
184
|
+
401: {
|
|
185
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
186
|
+
description: 'Unauthorized',
|
|
187
|
+
},
|
|
188
|
+
404: {
|
|
189
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
190
|
+
description: 'Record not found',
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
}),
|
|
194
|
+
(c) => c.json({} as never)
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
// POST /api/tables/{tableId}/records/{recordId}/restore
|
|
198
|
+
app.openapi(
|
|
199
|
+
createRoute({
|
|
200
|
+
method: 'post',
|
|
201
|
+
path: '/api/tables/{tableId}/records/{recordId}/restore',
|
|
202
|
+
summary: 'Restore a deleted record',
|
|
203
|
+
description: 'Restores a soft-deleted record back to active state.',
|
|
204
|
+
operationId: 'restoreRecord',
|
|
205
|
+
tags: ['records'],
|
|
206
|
+
request: { params: recordIdParamSchema },
|
|
207
|
+
responses: {
|
|
208
|
+
200: {
|
|
209
|
+
content: { 'application/json': { schema: restoreRecordResponseSchema } },
|
|
210
|
+
description: 'Record restored',
|
|
211
|
+
},
|
|
212
|
+
401: {
|
|
213
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
214
|
+
description: 'Unauthorized',
|
|
215
|
+
},
|
|
216
|
+
404: {
|
|
217
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
218
|
+
description: 'Record not found',
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
}),
|
|
222
|
+
(c) => c.json({} as never)
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
// GET /api/tables/{tableId}/trash
|
|
226
|
+
app.openapi(
|
|
227
|
+
createRoute({
|
|
228
|
+
method: 'get',
|
|
229
|
+
path: '/api/tables/{tableId}/trash',
|
|
230
|
+
summary: 'List deleted records',
|
|
231
|
+
description: 'Returns paginated list of soft-deleted records in the trash.',
|
|
232
|
+
operationId: 'listTrashRecords',
|
|
233
|
+
tags: ['records'],
|
|
234
|
+
request: { params: tableIdParamSchema, query: listRecordsQuerySchema },
|
|
235
|
+
responses: {
|
|
236
|
+
200: {
|
|
237
|
+
content: { 'application/json': { schema: listRecordsResponseSchema } },
|
|
238
|
+
description: 'List of deleted records',
|
|
239
|
+
},
|
|
240
|
+
401: {
|
|
241
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
242
|
+
description: 'Unauthorized',
|
|
243
|
+
},
|
|
244
|
+
404: {
|
|
245
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
246
|
+
description: 'Table not found',
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
}),
|
|
250
|
+
(c) => c.json({} as never)
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
// GET /api/tables/{tableId}/records/{recordId}/history
|
|
254
|
+
app.openapi(
|
|
255
|
+
createRoute({
|
|
256
|
+
method: 'get',
|
|
257
|
+
path: '/api/tables/{tableId}/records/{recordId}/history',
|
|
258
|
+
summary: 'Get record history',
|
|
259
|
+
description: 'Returns the audit trail of changes for a specific record.',
|
|
260
|
+
operationId: 'getRecordHistory',
|
|
261
|
+
tags: ['records'],
|
|
262
|
+
request: { params: recordIdParamSchema },
|
|
263
|
+
responses: {
|
|
264
|
+
200: {
|
|
265
|
+
content: { 'application/json': { schema: getRecordHistoryResponseSchema } },
|
|
266
|
+
description: 'Record history',
|
|
267
|
+
},
|
|
268
|
+
401: {
|
|
269
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
270
|
+
description: 'Unauthorized',
|
|
271
|
+
},
|
|
272
|
+
404: {
|
|
273
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
274
|
+
description: 'Record not found',
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
}),
|
|
278
|
+
(c) => c.json({} as never)
|
|
279
|
+
)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function registerCommentRoutes(app: OpenAPIHono): void {
|
|
283
|
+
// GET /api/tables/{tableId}/records/{recordId}/comments
|
|
284
|
+
app.openapi(
|
|
285
|
+
createRoute({
|
|
286
|
+
method: 'get',
|
|
287
|
+
path: '/api/tables/{tableId}/records/{recordId}/comments',
|
|
288
|
+
summary: 'List comments on a record',
|
|
289
|
+
description: 'Returns paginated comments for a specific record.',
|
|
290
|
+
operationId: 'listComments',
|
|
291
|
+
tags: ['records'],
|
|
292
|
+
request: { params: recordIdParamSchema },
|
|
293
|
+
responses: {
|
|
294
|
+
200: {
|
|
295
|
+
content: { 'application/json': { schema: listCommentsResponseSchema } },
|
|
296
|
+
description: 'List of comments',
|
|
297
|
+
},
|
|
298
|
+
401: {
|
|
299
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
300
|
+
description: 'Unauthorized',
|
|
301
|
+
},
|
|
302
|
+
404: {
|
|
303
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
304
|
+
description: 'Record not found',
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
}),
|
|
308
|
+
(c) => c.json({} as never)
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
// POST /api/tables/{tableId}/records/{recordId}/comments
|
|
312
|
+
app.openapi(
|
|
313
|
+
createRoute({
|
|
314
|
+
method: 'post',
|
|
315
|
+
path: '/api/tables/{tableId}/records/{recordId}/comments',
|
|
316
|
+
summary: 'Create a comment',
|
|
317
|
+
description: 'Adds a comment to a record.',
|
|
318
|
+
operationId: 'createComment',
|
|
319
|
+
tags: ['records'],
|
|
320
|
+
request: {
|
|
321
|
+
params: recordIdParamSchema,
|
|
322
|
+
body: { content: { 'application/json': { schema: commentBodySchema } } },
|
|
323
|
+
},
|
|
324
|
+
responses: {
|
|
325
|
+
201: {
|
|
326
|
+
content: { 'application/json': { schema: createCommentResponseSchema } },
|
|
327
|
+
description: 'Comment created',
|
|
328
|
+
},
|
|
329
|
+
400: {
|
|
330
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
331
|
+
description: 'Validation error',
|
|
332
|
+
},
|
|
333
|
+
401: {
|
|
334
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
335
|
+
description: 'Unauthorized',
|
|
336
|
+
},
|
|
337
|
+
404: {
|
|
338
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
339
|
+
description: 'Record not found',
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
}),
|
|
343
|
+
(c) => c.json({} as never, 201)
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
// GET /api/tables/{tableId}/records/{recordId}/comments/{commentId}
|
|
347
|
+
app.openapi(
|
|
348
|
+
createRoute({
|
|
349
|
+
method: 'get',
|
|
350
|
+
path: '/api/tables/{tableId}/records/{recordId}/comments/{commentId}',
|
|
351
|
+
summary: 'Get a comment',
|
|
352
|
+
description: 'Returns a single comment by ID.',
|
|
353
|
+
operationId: 'getComment',
|
|
354
|
+
tags: ['records'],
|
|
355
|
+
request: { params: commentIdParamSchema },
|
|
356
|
+
responses: {
|
|
357
|
+
200: {
|
|
358
|
+
content: { 'application/json': { schema: getCommentResponseSchema } },
|
|
359
|
+
description: 'Comment details',
|
|
360
|
+
},
|
|
361
|
+
401: {
|
|
362
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
363
|
+
description: 'Unauthorized',
|
|
364
|
+
},
|
|
365
|
+
404: {
|
|
366
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
367
|
+
description: 'Comment not found',
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
}),
|
|
371
|
+
(c) => c.json({} as never)
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
// PATCH /api/tables/{tableId}/records/{recordId}/comments/{commentId}
|
|
375
|
+
app.openapi(
|
|
376
|
+
createRoute({
|
|
377
|
+
method: 'patch',
|
|
378
|
+
path: '/api/tables/{tableId}/records/{recordId}/comments/{commentId}',
|
|
379
|
+
summary: 'Update a comment',
|
|
380
|
+
description: 'Updates the content of a comment. Only the author can update.',
|
|
381
|
+
operationId: 'updateComment',
|
|
382
|
+
tags: ['records'],
|
|
383
|
+
request: {
|
|
384
|
+
params: commentIdParamSchema,
|
|
385
|
+
body: { content: { 'application/json': { schema: commentBodySchema } } },
|
|
386
|
+
},
|
|
387
|
+
responses: {
|
|
388
|
+
200: {
|
|
389
|
+
content: { 'application/json': { schema: updateCommentResponseSchema } },
|
|
390
|
+
description: 'Comment updated',
|
|
391
|
+
},
|
|
392
|
+
400: {
|
|
393
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
394
|
+
description: 'Validation error',
|
|
395
|
+
},
|
|
396
|
+
401: {
|
|
397
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
398
|
+
description: 'Unauthorized',
|
|
399
|
+
},
|
|
400
|
+
403: {
|
|
401
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
402
|
+
description: 'Forbidden',
|
|
403
|
+
},
|
|
404
|
+
404: {
|
|
405
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
406
|
+
description: 'Comment not found',
|
|
407
|
+
},
|
|
408
|
+
},
|
|
409
|
+
}),
|
|
410
|
+
(c) => c.json({} as never)
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
// DELETE /api/tables/{tableId}/records/{recordId}/comments/{commentId}
|
|
414
|
+
app.openapi(
|
|
415
|
+
createRoute({
|
|
416
|
+
method: 'delete',
|
|
417
|
+
path: '/api/tables/{tableId}/records/{recordId}/comments/{commentId}',
|
|
418
|
+
summary: 'Delete a comment',
|
|
419
|
+
description: 'Soft-deletes a comment. Only the author or admin can delete.',
|
|
420
|
+
operationId: 'deleteComment',
|
|
421
|
+
tags: ['records'],
|
|
422
|
+
request: { params: commentIdParamSchema },
|
|
423
|
+
responses: {
|
|
424
|
+
204: {
|
|
425
|
+
description: 'Comment deleted (no content)',
|
|
426
|
+
},
|
|
427
|
+
401: {
|
|
428
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
429
|
+
description: 'Unauthorized',
|
|
430
|
+
},
|
|
431
|
+
403: {
|
|
432
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
433
|
+
description: 'Forbidden',
|
|
434
|
+
},
|
|
435
|
+
404: {
|
|
436
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
437
|
+
description: 'Comment not found',
|
|
438
|
+
},
|
|
439
|
+
},
|
|
440
|
+
}),
|
|
441
|
+
// eslint-disable-next-line unicorn/no-null -- Hono c.body() requires null for 204
|
|
442
|
+
(c) => c.body(null, 204)
|
|
443
|
+
)
|
|
444
|
+
}
|