digitaltwin-core 0.13.3 → 0.14.1
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/package.json +101 -106
- package/dist/auth/apisix_parser.d.ts +0 -146
- package/dist/auth/apisix_parser.d.ts.map +0 -1
- package/dist/auth/apisix_parser.js +0 -185
- package/dist/auth/apisix_parser.js.map +0 -1
- package/dist/auth/auth_config.d.ts +0 -126
- package/dist/auth/auth_config.d.ts.map +0 -1
- package/dist/auth/auth_config.js +0 -169
- package/dist/auth/auth_config.js.map +0 -1
- package/dist/auth/index.d.ts +0 -5
- package/dist/auth/index.d.ts.map +0 -1
- package/dist/auth/index.js +0 -4
- package/dist/auth/index.js.map +0 -1
- package/dist/auth/types.d.ts +0 -100
- package/dist/auth/types.d.ts.map +0 -1
- package/dist/auth/types.js +0 -2
- package/dist/auth/types.js.map +0 -1
- package/dist/auth/user_service.d.ts +0 -86
- package/dist/auth/user_service.d.ts.map +0 -1
- package/dist/auth/user_service.js +0 -237
- package/dist/auth/user_service.js.map +0 -1
- package/dist/components/assets_manager.d.ts +0 -662
- package/dist/components/assets_manager.d.ts.map +0 -1
- package/dist/components/assets_manager.js +0 -1529
- package/dist/components/assets_manager.js.map +0 -1
- package/dist/components/async_upload.d.ts +0 -20
- package/dist/components/async_upload.d.ts.map +0 -1
- package/dist/components/async_upload.js +0 -10
- package/dist/components/async_upload.js.map +0 -1
- package/dist/components/collector.d.ts +0 -203
- package/dist/components/collector.d.ts.map +0 -1
- package/dist/components/collector.js +0 -202
- package/dist/components/collector.js.map +0 -1
- package/dist/components/custom_table_manager.d.ts +0 -503
- package/dist/components/custom_table_manager.d.ts.map +0 -1
- package/dist/components/custom_table_manager.js +0 -1052
- package/dist/components/custom_table_manager.js.map +0 -1
- package/dist/components/global_assets_handler.d.ts +0 -63
- package/dist/components/global_assets_handler.d.ts.map +0 -1
- package/dist/components/global_assets_handler.js +0 -127
- package/dist/components/global_assets_handler.js.map +0 -1
- package/dist/components/handler.d.ts +0 -104
- package/dist/components/handler.d.ts.map +0 -1
- package/dist/components/handler.js +0 -110
- package/dist/components/handler.js.map +0 -1
- package/dist/components/harvester.d.ts +0 -182
- package/dist/components/harvester.d.ts.map +0 -1
- package/dist/components/harvester.js +0 -393
- package/dist/components/harvester.js.map +0 -1
- package/dist/components/index.d.ts +0 -11
- package/dist/components/index.d.ts.map +0 -1
- package/dist/components/index.js +0 -9
- package/dist/components/index.js.map +0 -1
- package/dist/components/interfaces.d.ts +0 -126
- package/dist/components/interfaces.d.ts.map +0 -1
- package/dist/components/interfaces.js +0 -8
- package/dist/components/interfaces.js.map +0 -1
- package/dist/components/map_manager.d.ts +0 -61
- package/dist/components/map_manager.d.ts.map +0 -1
- package/dist/components/map_manager.js +0 -242
- package/dist/components/map_manager.js.map +0 -1
- package/dist/components/tileset_manager.d.ts +0 -125
- package/dist/components/tileset_manager.d.ts.map +0 -1
- package/dist/components/tileset_manager.js +0 -618
- package/dist/components/tileset_manager.js.map +0 -1
- package/dist/components/types.d.ts +0 -226
- package/dist/components/types.d.ts.map +0 -1
- package/dist/components/types.js +0 -8
- package/dist/components/types.js.map +0 -1
- package/dist/database/adapters/knex_database_adapter.d.ts +0 -92
- package/dist/database/adapters/knex_database_adapter.d.ts.map +0 -1
- package/dist/database/adapters/knex_database_adapter.js +0 -647
- package/dist/database/adapters/knex_database_adapter.js.map +0 -1
- package/dist/database/database_adapter.d.ts +0 -251
- package/dist/database/database_adapter.d.ts.map +0 -1
- package/dist/database/database_adapter.js +0 -46
- package/dist/database/database_adapter.js.map +0 -1
- package/dist/engine/digital_twin_engine.d.ts +0 -253
- package/dist/engine/digital_twin_engine.d.ts.map +0 -1
- package/dist/engine/digital_twin_engine.js +0 -790
- package/dist/engine/digital_twin_engine.js.map +0 -1
- package/dist/engine/endpoints.d.ts +0 -47
- package/dist/engine/endpoints.d.ts.map +0 -1
- package/dist/engine/endpoints.js +0 -56
- package/dist/engine/endpoints.js.map +0 -1
- package/dist/engine/events.d.ts +0 -93
- package/dist/engine/events.d.ts.map +0 -1
- package/dist/engine/events.js +0 -71
- package/dist/engine/events.js.map +0 -1
- package/dist/engine/initializer.d.ts +0 -62
- package/dist/engine/initializer.d.ts.map +0 -1
- package/dist/engine/initializer.js +0 -108
- package/dist/engine/initializer.js.map +0 -1
- package/dist/engine/queue_manager.d.ts +0 -87
- package/dist/engine/queue_manager.d.ts.map +0 -1
- package/dist/engine/queue_manager.js +0 -196
- package/dist/engine/queue_manager.js.map +0 -1
- package/dist/engine/scheduler.d.ts +0 -30
- package/dist/engine/scheduler.d.ts.map +0 -1
- package/dist/engine/scheduler.js +0 -370
- package/dist/engine/scheduler.js.map +0 -1
- package/dist/engine/upload_processor.d.ts +0 -36
- package/dist/engine/upload_processor.d.ts.map +0 -1
- package/dist/engine/upload_processor.js +0 -101
- package/dist/engine/upload_processor.js.map +0 -1
- package/dist/env/env.d.ts +0 -134
- package/dist/env/env.d.ts.map +0 -1
- package/dist/env/env.js +0 -177
- package/dist/env/env.js.map +0 -1
- package/dist/index.d.ts +0 -49
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -57
- package/dist/index.js.map +0 -1
- package/dist/openapi/generator.d.ts +0 -93
- package/dist/openapi/generator.d.ts.map +0 -1
- package/dist/openapi/generator.js +0 -293
- package/dist/openapi/generator.js.map +0 -1
- package/dist/openapi/index.d.ts +0 -9
- package/dist/openapi/index.d.ts.map +0 -1
- package/dist/openapi/index.js +0 -9
- package/dist/openapi/index.js.map +0 -1
- package/dist/openapi/types.d.ts +0 -182
- package/dist/openapi/types.d.ts.map +0 -1
- package/dist/openapi/types.js +0 -16
- package/dist/openapi/types.js.map +0 -1
- package/dist/storage/adapters/local_storage_service.d.ts +0 -51
- package/dist/storage/adapters/local_storage_service.d.ts.map +0 -1
- package/dist/storage/adapters/local_storage_service.js +0 -110
- package/dist/storage/adapters/local_storage_service.js.map +0 -1
- package/dist/storage/adapters/ovh_storage_service.d.ts +0 -61
- package/dist/storage/adapters/ovh_storage_service.d.ts.map +0 -1
- package/dist/storage/adapters/ovh_storage_service.js +0 -172
- package/dist/storage/adapters/ovh_storage_service.js.map +0 -1
- package/dist/storage/storage_factory.d.ts +0 -14
- package/dist/storage/storage_factory.d.ts.map +0 -1
- package/dist/storage/storage_factory.js +0 -36
- package/dist/storage/storage_factory.js.map +0 -1
- package/dist/storage/storage_service.d.ts +0 -163
- package/dist/storage/storage_service.d.ts.map +0 -1
- package/dist/storage/storage_service.js +0 -54
- package/dist/storage/storage_service.js.map +0 -1
- package/dist/types/data_record.d.ts +0 -123
- package/dist/types/data_record.d.ts.map +0 -1
- package/dist/types/data_record.js +0 -8
- package/dist/types/data_record.js.map +0 -1
- package/dist/utils/http_responses.d.ts +0 -155
- package/dist/utils/http_responses.d.ts.map +0 -1
- package/dist/utils/http_responses.js +0 -190
- package/dist/utils/http_responses.js.map +0 -1
- package/dist/utils/index.d.ts +0 -8
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -6
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/logger.d.ts +0 -74
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/logger.js +0 -92
- package/dist/utils/logger.js.map +0 -1
- package/dist/utils/map_to_data_record.d.ts +0 -10
- package/dist/utils/map_to_data_record.d.ts.map +0 -1
- package/dist/utils/map_to_data_record.js +0 -36
- package/dist/utils/map_to_data_record.js.map +0 -1
- package/dist/utils/servable_endpoint.d.ts +0 -63
- package/dist/utils/servable_endpoint.d.ts.map +0 -1
- package/dist/utils/servable_endpoint.js +0 -67
- package/dist/utils/servable_endpoint.js.map +0 -1
- package/dist/utils/zip_utils.d.ts +0 -66
- package/dist/utils/zip_utils.d.ts.map +0 -1
- package/dist/utils/zip_utils.js +0 -169
- package/dist/utils/zip_utils.js.map +0 -1
|
@@ -1,1052 +0,0 @@
|
|
|
1
|
-
import { UserService } from '../auth/user_service.js';
|
|
2
|
-
import { ApisixAuthParser } from '../auth/apisix_parser.js';
|
|
3
|
-
export class CustomTableManager {
|
|
4
|
-
/**
|
|
5
|
-
* Injects required dependencies into the custom table manager instance.
|
|
6
|
-
*
|
|
7
|
-
* Called by the Digital Twin Engine during component initialization.
|
|
8
|
-
*
|
|
9
|
-
* @param {DatabaseAdapter} db - Database adapter for data operations
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```typescript
|
|
13
|
-
* const customTableManager = new MyCustomTableManager()
|
|
14
|
-
* customTableManager.setDependencies(databaseAdapter)
|
|
15
|
-
* ```
|
|
16
|
-
*/
|
|
17
|
-
setDependencies(db) {
|
|
18
|
-
this.db = db;
|
|
19
|
-
this.userService = new UserService(db);
|
|
20
|
-
this.tableName = this.getConfiguration().name;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Initialize the database table with custom columns.
|
|
24
|
-
*
|
|
25
|
-
* Creates the table if it doesn't exist, with standard columns (id, created_at, updated_at)
|
|
26
|
-
* plus the custom columns defined in the configuration.
|
|
27
|
-
* Automatically adds an 'owner_id' column for user ownership tracking.
|
|
28
|
-
*
|
|
29
|
-
* @returns {Promise<void>}
|
|
30
|
-
*
|
|
31
|
-
* @example
|
|
32
|
-
* ```typescript
|
|
33
|
-
* // Called automatically by the framework
|
|
34
|
-
* await customTableManager.initializeTable()
|
|
35
|
-
* ```
|
|
36
|
-
*/
|
|
37
|
-
async initializeTable() {
|
|
38
|
-
const config = this.getConfiguration();
|
|
39
|
-
// Add owner_id column automatically for user ownership
|
|
40
|
-
const columnsWithOwnership = {
|
|
41
|
-
owner_id: 'integer',
|
|
42
|
-
...config.columns
|
|
43
|
-
};
|
|
44
|
-
// Use the new createTableWithColumns method from DatabaseAdapter
|
|
45
|
-
await this.db.createTableWithColumns(config.name, columnsWithOwnership);
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Validate query conditions against requirements.
|
|
49
|
-
*
|
|
50
|
-
* @private
|
|
51
|
-
* @param {Record<string, any>} conditions - Conditions to validate
|
|
52
|
-
* @param {QueryValidationOptions} options - Validation options
|
|
53
|
-
* @throws {Error} If validation fails
|
|
54
|
-
*/
|
|
55
|
-
validateQuery(conditions, options) {
|
|
56
|
-
if (!options)
|
|
57
|
-
return;
|
|
58
|
-
// Check required fields
|
|
59
|
-
if (options.required) {
|
|
60
|
-
for (const field of options.required) {
|
|
61
|
-
if (!(field in conditions)) {
|
|
62
|
-
throw new Error(`Field '${field}' is required`);
|
|
63
|
-
}
|
|
64
|
-
const value = conditions[field];
|
|
65
|
-
if (value == null || value === '') {
|
|
66
|
-
throw new Error(`Field '${field}' must have a non-empty value`);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
// Custom validation
|
|
71
|
-
if (options.validate) {
|
|
72
|
-
try {
|
|
73
|
-
options.validate(conditions);
|
|
74
|
-
}
|
|
75
|
-
catch (error) {
|
|
76
|
-
throw new Error(`Validation failed: ${error instanceof Error ? error.message : 'Unknown validation error'}`);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Create a new record in the custom table.
|
|
82
|
-
*
|
|
83
|
-
* @param {Record<string, any>} data - Data to insert (excluding id, created_at, updated_at)
|
|
84
|
-
* @returns {Promise<number>} The ID of the created record
|
|
85
|
-
*
|
|
86
|
-
* @example
|
|
87
|
-
* ```typescript
|
|
88
|
-
* const id = await customTableManager.create({
|
|
89
|
-
* sensor_id: 'TEMP001',
|
|
90
|
-
* type: 'temperature',
|
|
91
|
-
* location: 'Building A - Floor 1',
|
|
92
|
-
* active: true
|
|
93
|
-
* })
|
|
94
|
-
* console.log(`Created sensor with ID: ${id}`)
|
|
95
|
-
* ```
|
|
96
|
-
*/
|
|
97
|
-
async create(data) {
|
|
98
|
-
// Use the specialized method for custom tables
|
|
99
|
-
return await this.db.insertCustomTableRecord(this.tableName, data);
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Find all records in the custom table.
|
|
103
|
-
*
|
|
104
|
-
* @returns {Promise<CustomTableRecord[]>} Array of all records
|
|
105
|
-
*
|
|
106
|
-
* @example
|
|
107
|
-
* ```typescript
|
|
108
|
-
* const allSensors = await customTableManager.findAll()
|
|
109
|
-
* console.log(`Found ${allSensors.length} sensors`)
|
|
110
|
-
* ```
|
|
111
|
-
*/
|
|
112
|
-
async findAll() {
|
|
113
|
-
// Use specialized method for custom tables that returns raw rows
|
|
114
|
-
const records = await this.db.findCustomTableRecords(this.tableName, {});
|
|
115
|
-
return records.map(record => ({
|
|
116
|
-
id: record.id,
|
|
117
|
-
created_at: new Date(record.created_at),
|
|
118
|
-
updated_at: new Date(record.updated_at),
|
|
119
|
-
...this.extractCustomFields(record)
|
|
120
|
-
}));
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Find a record by its ID.
|
|
124
|
-
*
|
|
125
|
-
* @param {number} id - The record ID to find
|
|
126
|
-
* @returns {Promise<CustomTableRecord | null>} The record or null if not found
|
|
127
|
-
*
|
|
128
|
-
* @example
|
|
129
|
-
* ```typescript
|
|
130
|
-
* const sensor = await customTableManager.findById(123)
|
|
131
|
-
* if (sensor) {
|
|
132
|
-
* console.log(`Sensor: ${sensor.sensor_id}`)
|
|
133
|
-
* }
|
|
134
|
-
* ```
|
|
135
|
-
*/
|
|
136
|
-
async findById(id) {
|
|
137
|
-
const record = await this.db.getCustomTableRecordById(this.tableName, id);
|
|
138
|
-
if (!record) {
|
|
139
|
-
return null;
|
|
140
|
-
}
|
|
141
|
-
return {
|
|
142
|
-
id: record.id,
|
|
143
|
-
created_at: new Date(record.created_at),
|
|
144
|
-
updated_at: new Date(record.updated_at),
|
|
145
|
-
...this.extractCustomFields(record)
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Find records by a single column value with optional validation.
|
|
150
|
-
*
|
|
151
|
-
* @param {string} columnName - Name of the column to search by
|
|
152
|
-
* @param {any} value - Value to search for
|
|
153
|
-
* @param {boolean} required - Whether the value is required (default: true)
|
|
154
|
-
* @returns {Promise<CustomTableRecord[]>} Array of matching records
|
|
155
|
-
*
|
|
156
|
-
* @example
|
|
157
|
-
* ```typescript
|
|
158
|
-
* const wmsLayers = await wmsManager.findByColumn('wms_url', 'https://example.com/wms')
|
|
159
|
-
* const activeLayers = await wmsManager.findByColumn('active', true)
|
|
160
|
-
*
|
|
161
|
-
* // Optional value (won't throw if empty)
|
|
162
|
-
* const layers = await wmsManager.findByColumn('description', '', false)
|
|
163
|
-
* ```
|
|
164
|
-
*/
|
|
165
|
-
async findByColumn(columnName, value, required = true) {
|
|
166
|
-
if (required && (value == null || value === '')) {
|
|
167
|
-
throw new Error(`Value for column '${columnName}' is required and cannot be empty`);
|
|
168
|
-
}
|
|
169
|
-
return this.findByCondition({ [columnName]: value });
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
|
-
* Find records by multiple column values with validation support.
|
|
173
|
-
*
|
|
174
|
-
* @param {Record<string, any>} conditions - Key-value pairs to match
|
|
175
|
-
* @param {QueryValidationOptions} options - Validation options
|
|
176
|
-
* @returns {Promise<CustomTableRecord[]>} Array of matching records
|
|
177
|
-
*
|
|
178
|
-
* @example
|
|
179
|
-
* ```typescript
|
|
180
|
-
* // Simple query
|
|
181
|
-
* const layers = await wmsManager.findByColumns({
|
|
182
|
-
* wms_url: 'https://example.com/wms',
|
|
183
|
-
* active: true
|
|
184
|
-
* })
|
|
185
|
-
*
|
|
186
|
-
* // With validation
|
|
187
|
-
* const layers = await wmsManager.findByColumns(
|
|
188
|
-
* { wms_url: wmsUrl, projection: projection },
|
|
189
|
-
* {
|
|
190
|
-
* required: ['wms_url', 'projection'],
|
|
191
|
-
* validate: (conditions) => {
|
|
192
|
-
* if (!conditions.wms_url.startsWith('http')) {
|
|
193
|
-
* throw new Error('WMS URL must start with http or https')
|
|
194
|
-
* }
|
|
195
|
-
* }
|
|
196
|
-
* }
|
|
197
|
-
* )
|
|
198
|
-
* ```
|
|
199
|
-
*/
|
|
200
|
-
async findByColumns(conditions, options) {
|
|
201
|
-
this.validateQuery(conditions, options);
|
|
202
|
-
return this.findByCondition(conditions);
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* Find records matching specific conditions (base method).
|
|
206
|
-
*
|
|
207
|
-
* @param {Record<string, any>} conditions - Key-value pairs to match
|
|
208
|
-
* @returns {Promise<CustomTableRecord[]>} Array of matching records
|
|
209
|
-
*
|
|
210
|
-
* @example
|
|
211
|
-
* ```typescript
|
|
212
|
-
* const activeSensors = await customTableManager.findByCondition({ active: true })
|
|
213
|
-
* const tempSensors = await customTableManager.findByCondition({ type: 'temperature' })
|
|
214
|
-
* ```
|
|
215
|
-
*/
|
|
216
|
-
async findByCondition(conditions) {
|
|
217
|
-
const records = await this.db.findCustomTableRecords(this.tableName, conditions);
|
|
218
|
-
return records.map(record => ({
|
|
219
|
-
id: record.id,
|
|
220
|
-
created_at: new Date(record.created_at),
|
|
221
|
-
updated_at: new Date(record.updated_at),
|
|
222
|
-
...this.extractCustomFields(record)
|
|
223
|
-
}));
|
|
224
|
-
}
|
|
225
|
-
/**
|
|
226
|
-
* Update a record by its ID.
|
|
227
|
-
*
|
|
228
|
-
* @param {number} id - The record ID to update
|
|
229
|
-
* @param {Record<string, any>} data - Data to update (excluding id, created_at)
|
|
230
|
-
* @returns {Promise<void>}
|
|
231
|
-
*
|
|
232
|
-
* @example
|
|
233
|
-
* ```typescript
|
|
234
|
-
* await customTableManager.update(123, {
|
|
235
|
-
* active: false,
|
|
236
|
-
* last_ping: new Date()
|
|
237
|
-
* })
|
|
238
|
-
* ```
|
|
239
|
-
*/
|
|
240
|
-
async update(id, data) {
|
|
241
|
-
// Use the new efficient SQL UPDATE method
|
|
242
|
-
await this.db.updateById(this.tableName, id, data);
|
|
243
|
-
}
|
|
244
|
-
/**
|
|
245
|
-
* Delete a record by its ID.
|
|
246
|
-
*
|
|
247
|
-
* @param {number} id - The record ID to delete
|
|
248
|
-
* @returns {Promise<void>}
|
|
249
|
-
*
|
|
250
|
-
* @example
|
|
251
|
-
* ```typescript
|
|
252
|
-
* await customTableManager.delete(123)
|
|
253
|
-
* console.log('Sensor deleted successfully')
|
|
254
|
-
* ```
|
|
255
|
-
*/
|
|
256
|
-
async delete(id) {
|
|
257
|
-
await this.db.delete(id.toString(), this.tableName);
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* Delete records by a single column value.
|
|
261
|
-
*
|
|
262
|
-
* @param {string} columnName - Name of the column to match for deletion
|
|
263
|
-
* @param {any} value - Value to match for deletion
|
|
264
|
-
* @returns {Promise<number>} Number of records deleted
|
|
265
|
-
*
|
|
266
|
-
* @example
|
|
267
|
-
* ```typescript
|
|
268
|
-
* const deleted = await wmsManager.deleteByColumn('active', false)
|
|
269
|
-
* console.log(`Deleted ${deleted} inactive layers`)
|
|
270
|
-
* ```
|
|
271
|
-
*/
|
|
272
|
-
async deleteByColumn(columnName, value) {
|
|
273
|
-
return this.deleteByCondition({ [columnName]: value });
|
|
274
|
-
}
|
|
275
|
-
/**
|
|
276
|
-
* Delete records matching specific conditions.
|
|
277
|
-
*
|
|
278
|
-
* @param {Record<string, any>} conditions - Key-value pairs to match for deletion
|
|
279
|
-
* @returns {Promise<number>} Number of records deleted
|
|
280
|
-
*
|
|
281
|
-
* @example
|
|
282
|
-
* ```typescript
|
|
283
|
-
* const deleted = await customTableManager.deleteByCondition({ active: false })
|
|
284
|
-
* console.log(`Deleted ${deleted} inactive sensors`)
|
|
285
|
-
* ```
|
|
286
|
-
*/
|
|
287
|
-
async deleteByCondition(conditions) {
|
|
288
|
-
const recordsToDelete = await this.findByCondition(conditions);
|
|
289
|
-
for (const record of recordsToDelete) {
|
|
290
|
-
await this.delete(record.id);
|
|
291
|
-
}
|
|
292
|
-
return recordsToDelete.length;
|
|
293
|
-
}
|
|
294
|
-
/**
|
|
295
|
-
* Extract custom fields from a database record, excluding framework fields.
|
|
296
|
-
*
|
|
297
|
-
* @private
|
|
298
|
-
* @param {any} record - Database record
|
|
299
|
-
* @returns {Record<string, any>} Custom fields only (including owner_id)
|
|
300
|
-
*/
|
|
301
|
-
extractCustomFields(record) {
|
|
302
|
-
const config = this.getConfiguration();
|
|
303
|
-
const customFields = {};
|
|
304
|
-
// Add owner_id if present
|
|
305
|
-
if ('owner_id' in record) {
|
|
306
|
-
customFields.owner_id = record.owner_id;
|
|
307
|
-
}
|
|
308
|
-
// Add all configured custom columns
|
|
309
|
-
for (const columnName of Object.keys(config.columns)) {
|
|
310
|
-
if (columnName in record) {
|
|
311
|
-
customFields[columnName] = record[columnName];
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
return customFields;
|
|
315
|
-
}
|
|
316
|
-
/**
|
|
317
|
-
* AUTHENTICATION HELPERS
|
|
318
|
-
* These methods help custom endpoints implement authentication and authorization.
|
|
319
|
-
*/
|
|
320
|
-
/**
|
|
321
|
-
* Authenticate a request and return the user record.
|
|
322
|
-
*
|
|
323
|
-
* Use this for endpoints that require authentication but don't need ownership checks.
|
|
324
|
-
*
|
|
325
|
-
* @protected
|
|
326
|
-
* @param {any} req - HTTP request object
|
|
327
|
-
* @returns {Promise<UserRecord | null>} User record if authenticated, null otherwise
|
|
328
|
-
*
|
|
329
|
-
* @example
|
|
330
|
-
* ```typescript
|
|
331
|
-
* async myCustomEndpoint(req: any): Promise<DataResponse> {
|
|
332
|
-
* const userRecord = await this.authenticateRequest(req)
|
|
333
|
-
* if (!userRecord) {
|
|
334
|
-
* return this.authErrorResponse()
|
|
335
|
-
* }
|
|
336
|
-
*
|
|
337
|
-
* // Use userRecord.id for your logic
|
|
338
|
-
* console.log(`User ${userRecord.id} is authenticated`)
|
|
339
|
-
* }
|
|
340
|
-
* ```
|
|
341
|
-
*/
|
|
342
|
-
async authenticateRequest(req) {
|
|
343
|
-
if (!ApisixAuthParser.hasValidAuth(req.headers || {})) {
|
|
344
|
-
return null;
|
|
345
|
-
}
|
|
346
|
-
const authUser = ApisixAuthParser.parseAuthHeaders(req.headers || {});
|
|
347
|
-
if (!authUser) {
|
|
348
|
-
return null;
|
|
349
|
-
}
|
|
350
|
-
const userRecord = await this.userService.findOrCreateUser(authUser);
|
|
351
|
-
if (!userRecord.id) {
|
|
352
|
-
return null;
|
|
353
|
-
}
|
|
354
|
-
return userRecord;
|
|
355
|
-
}
|
|
356
|
-
/**
|
|
357
|
-
* Authenticate a request and verify the user owns the specified record.
|
|
358
|
-
*
|
|
359
|
-
* Use this for endpoints that modify or delete specific records.
|
|
360
|
-
*
|
|
361
|
-
* @protected
|
|
362
|
-
* @param {any} req - HTTP request object
|
|
363
|
-
* @param {number} recordId - ID of the record to check ownership for
|
|
364
|
-
* @returns {Promise<{ userRecord: UserRecord; record: CustomTableRecord } | null>}
|
|
365
|
-
* Both user and record if authenticated and owns the record, null otherwise
|
|
366
|
-
*
|
|
367
|
-
* @example
|
|
368
|
-
* ```typescript
|
|
369
|
-
* async deleteMyRecord(req: any): Promise<DataResponse> {
|
|
370
|
-
* const recordId = parseInt(req.params.id)
|
|
371
|
-
* const auth = await this.authenticateAndCheckOwnership(req, recordId)
|
|
372
|
-
*
|
|
373
|
-
* if (!auth) {
|
|
374
|
-
* return this.authErrorResponse()
|
|
375
|
-
* }
|
|
376
|
-
*
|
|
377
|
-
* if (auth.record.owner_id !== auth.userRecord.id) {
|
|
378
|
-
* return this.forbiddenErrorResponse()
|
|
379
|
-
* }
|
|
380
|
-
*
|
|
381
|
-
* await this.delete(recordId)
|
|
382
|
-
* return { status: 200, content: JSON.stringify({ message: 'Deleted' }) }
|
|
383
|
-
* }
|
|
384
|
-
* ```
|
|
385
|
-
*/
|
|
386
|
-
async authenticateAndCheckOwnership(req, recordId) {
|
|
387
|
-
const userRecord = await this.authenticateRequest(req);
|
|
388
|
-
if (!userRecord) {
|
|
389
|
-
return null;
|
|
390
|
-
}
|
|
391
|
-
const record = await this.findById(recordId);
|
|
392
|
-
if (!record) {
|
|
393
|
-
return null;
|
|
394
|
-
}
|
|
395
|
-
return { userRecord, record };
|
|
396
|
-
}
|
|
397
|
-
/**
|
|
398
|
-
* Get authenticated user from request, throwing an error response if not authenticated.
|
|
399
|
-
*
|
|
400
|
-
* Use this when authentication is required and you want to fail fast.
|
|
401
|
-
*
|
|
402
|
-
* @protected
|
|
403
|
-
* @param {any} req - HTTP request object
|
|
404
|
-
* @returns {Promise<UserRecord | DataResponse>} User record if authenticated, error response otherwise
|
|
405
|
-
*
|
|
406
|
-
* @example
|
|
407
|
-
* ```typescript
|
|
408
|
-
* async myProtectedEndpoint(req: any): Promise<DataResponse> {
|
|
409
|
-
* const userOrError = await this.requireAuthentication(req)
|
|
410
|
-
* if ('status' in userOrError) {
|
|
411
|
-
* return userOrError // Return error response
|
|
412
|
-
* }
|
|
413
|
-
*
|
|
414
|
-
* const userRecord = userOrError
|
|
415
|
-
* // Continue with authenticated user
|
|
416
|
-
* }
|
|
417
|
-
* ```
|
|
418
|
-
*/
|
|
419
|
-
async requireAuthentication(req) {
|
|
420
|
-
const userRecord = await this.authenticateRequest(req);
|
|
421
|
-
if (!userRecord) {
|
|
422
|
-
return this.authErrorResponse();
|
|
423
|
-
}
|
|
424
|
-
return userRecord;
|
|
425
|
-
}
|
|
426
|
-
/**
|
|
427
|
-
* Helper to return a 401 Unauthorized error response.
|
|
428
|
-
*
|
|
429
|
-
* @protected
|
|
430
|
-
* @param {string} message - Custom error message (default: "Authentication required")
|
|
431
|
-
* @returns {DataResponse} 401 error response
|
|
432
|
-
*
|
|
433
|
-
* @example
|
|
434
|
-
* ```typescript
|
|
435
|
-
* if (!userRecord) {
|
|
436
|
-
* return this.authErrorResponse()
|
|
437
|
-
* }
|
|
438
|
-
* ```
|
|
439
|
-
*/
|
|
440
|
-
authErrorResponse(message = 'Authentication required') {
|
|
441
|
-
return {
|
|
442
|
-
status: 401,
|
|
443
|
-
content: JSON.stringify({ error: message }),
|
|
444
|
-
headers: { 'Content-Type': 'application/json' }
|
|
445
|
-
};
|
|
446
|
-
}
|
|
447
|
-
/**
|
|
448
|
-
* Helper to return a 403 Forbidden error response.
|
|
449
|
-
*
|
|
450
|
-
* @protected
|
|
451
|
-
* @param {string} message - Custom error message (default: "You don't have permission to access this resource")
|
|
452
|
-
* @returns {DataResponse} 403 error response
|
|
453
|
-
*
|
|
454
|
-
* @example
|
|
455
|
-
* ```typescript
|
|
456
|
-
* if (record.owner_id !== userRecord.id) {
|
|
457
|
-
* return this.forbiddenErrorResponse('You can only modify your own records')
|
|
458
|
-
* }
|
|
459
|
-
* ```
|
|
460
|
-
*/
|
|
461
|
-
forbiddenErrorResponse(message = "You don't have permission to access this resource") {
|
|
462
|
-
return {
|
|
463
|
-
status: 403,
|
|
464
|
-
content: JSON.stringify({ error: message }),
|
|
465
|
-
headers: { 'Content-Type': 'application/json' }
|
|
466
|
-
};
|
|
467
|
-
}
|
|
468
|
-
/**
|
|
469
|
-
* Helper to check if a user owns a specific record.
|
|
470
|
-
*
|
|
471
|
-
* Returns false if the record doesn't have an owner_id (legacy records without ownership).
|
|
472
|
-
*
|
|
473
|
-
* @protected
|
|
474
|
-
* @param {CustomTableRecord} record - The record to check
|
|
475
|
-
* @param {UserRecord} userRecord - The user to check against
|
|
476
|
-
* @returns {boolean} True if user owns the record, false otherwise
|
|
477
|
-
*
|
|
478
|
-
* @example
|
|
479
|
-
* ```typescript
|
|
480
|
-
* const record = await this.findById(recordId)
|
|
481
|
-
* const userRecord = await this.authenticateRequest(req)
|
|
482
|
-
*
|
|
483
|
-
* if (!this.userOwnsRecord(record, userRecord)) {
|
|
484
|
-
* return this.forbiddenErrorResponse()
|
|
485
|
-
* }
|
|
486
|
-
* ```
|
|
487
|
-
*/
|
|
488
|
-
userOwnsRecord(record, userRecord) {
|
|
489
|
-
// If record doesn't have an owner_id, deny access for security
|
|
490
|
-
if (!record.owner_id) {
|
|
491
|
-
return false;
|
|
492
|
-
}
|
|
493
|
-
return record.owner_id === userRecord.id;
|
|
494
|
-
}
|
|
495
|
-
/**
|
|
496
|
-
* Get HTTP endpoints exposed by this custom table manager.
|
|
497
|
-
*
|
|
498
|
-
* Returns both standard CRUD endpoints and custom endpoints defined in configuration.
|
|
499
|
-
*
|
|
500
|
-
* @returns {Array} Array of endpoint descriptors with methods, paths, and handlers
|
|
501
|
-
*
|
|
502
|
-
* @example
|
|
503
|
-
* ```typescript
|
|
504
|
-
* // Standard endpoints for a table named 'sensors':
|
|
505
|
-
* GET /sensors - Get all records
|
|
506
|
-
* POST /sensors - Create new record
|
|
507
|
-
* GET /sensors/:id - Get specific record
|
|
508
|
-
* PUT /sensors/:id - Update specific record
|
|
509
|
-
* DELETE /sensors/:id - Delete specific record
|
|
510
|
-
*
|
|
511
|
-
* // Plus any custom endpoints defined in configuration
|
|
512
|
-
* ```
|
|
513
|
-
*/
|
|
514
|
-
getEndpoints() {
|
|
515
|
-
const config = this.getConfiguration();
|
|
516
|
-
const endpoints = [];
|
|
517
|
-
// Custom endpoints from configuration - MUST be registered BEFORE :id routes
|
|
518
|
-
// to avoid Express matching /proxy as /:id
|
|
519
|
-
if (config.endpoints) {
|
|
520
|
-
for (const endpoint of config.endpoints) {
|
|
521
|
-
const handlerMethod = this[endpoint.handler];
|
|
522
|
-
if (typeof handlerMethod === 'function') {
|
|
523
|
-
endpoints.push({
|
|
524
|
-
method: endpoint.method,
|
|
525
|
-
path: `/${config.name}${endpoint.path}`,
|
|
526
|
-
handler: handlerMethod.bind(this),
|
|
527
|
-
responseType: endpoint.responseType || 'application/json'
|
|
528
|
-
});
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
// Standard CRUD endpoints
|
|
533
|
-
endpoints.push({
|
|
534
|
-
method: 'get',
|
|
535
|
-
path: `/${config.name}`,
|
|
536
|
-
handler: this.handleGetAll.bind(this),
|
|
537
|
-
responseType: 'application/json'
|
|
538
|
-
});
|
|
539
|
-
endpoints.push({
|
|
540
|
-
method: 'post',
|
|
541
|
-
path: `/${config.name}`,
|
|
542
|
-
handler: this.handleCreate.bind(this),
|
|
543
|
-
responseType: 'application/json'
|
|
544
|
-
});
|
|
545
|
-
// :id routes MUST come AFTER custom endpoints
|
|
546
|
-
endpoints.push({
|
|
547
|
-
method: 'get',
|
|
548
|
-
path: `/${config.name}/:id`,
|
|
549
|
-
handler: this.handleGetById.bind(this),
|
|
550
|
-
responseType: 'application/json'
|
|
551
|
-
});
|
|
552
|
-
endpoints.push({
|
|
553
|
-
method: 'put',
|
|
554
|
-
path: `/${config.name}/:id`,
|
|
555
|
-
handler: this.handleUpdate.bind(this),
|
|
556
|
-
responseType: 'application/json'
|
|
557
|
-
});
|
|
558
|
-
endpoints.push({
|
|
559
|
-
method: 'delete',
|
|
560
|
-
path: `/${config.name}/:id`,
|
|
561
|
-
handler: this.handleDelete.bind(this),
|
|
562
|
-
responseType: 'application/json'
|
|
563
|
-
});
|
|
564
|
-
return endpoints;
|
|
565
|
-
}
|
|
566
|
-
/**
|
|
567
|
-
* Returns the OpenAPI specification for this custom table manager's endpoints.
|
|
568
|
-
*
|
|
569
|
-
* Generates documentation for all CRUD endpoints and custom endpoints.
|
|
570
|
-
* Can be overridden by subclasses for more detailed specifications.
|
|
571
|
-
*
|
|
572
|
-
* @returns {OpenAPIComponentSpec} OpenAPI paths, tags, and schemas for this custom table manager
|
|
573
|
-
*/
|
|
574
|
-
getOpenAPISpec() {
|
|
575
|
-
const config = this.getConfiguration();
|
|
576
|
-
const basePath = `/${config.name}`;
|
|
577
|
-
const tagName = config.tags?.[0] || config.name;
|
|
578
|
-
// Build properties schema from columns
|
|
579
|
-
const columnProperties = {
|
|
580
|
-
id: { type: 'integer', readOnly: true },
|
|
581
|
-
owner_id: { type: 'integer', nullable: true },
|
|
582
|
-
created_at: { type: 'string', format: 'date-time', readOnly: true },
|
|
583
|
-
updated_at: { type: 'string', format: 'date-time', readOnly: true }
|
|
584
|
-
};
|
|
585
|
-
// Add configured columns to the schema
|
|
586
|
-
for (const [columnName, columnDef] of Object.entries(config.columns)) {
|
|
587
|
-
columnProperties[columnName] = this.columnDefToOpenAPISchema(columnDef);
|
|
588
|
-
}
|
|
589
|
-
const paths = {
|
|
590
|
-
[basePath]: {
|
|
591
|
-
get: {
|
|
592
|
-
summary: `List all ${config.name} records`,
|
|
593
|
-
description: config.description,
|
|
594
|
-
tags: [tagName],
|
|
595
|
-
responses: {
|
|
596
|
-
'200': {
|
|
597
|
-
description: 'List of records',
|
|
598
|
-
content: {
|
|
599
|
-
'application/json': {
|
|
600
|
-
schema: {
|
|
601
|
-
type: 'array',
|
|
602
|
-
items: { $ref: `#/components/schemas/${config.name}Record` }
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
},
|
|
609
|
-
post: {
|
|
610
|
-
summary: `Create a new ${config.name} record`,
|
|
611
|
-
description: 'Create a new record. Requires authentication.',
|
|
612
|
-
tags: [tagName],
|
|
613
|
-
security: [{ ApiKeyAuth: [] }],
|
|
614
|
-
requestBody: {
|
|
615
|
-
required: true,
|
|
616
|
-
content: {
|
|
617
|
-
'application/json': {
|
|
618
|
-
schema: { $ref: `#/components/schemas/${config.name}Input` }
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
},
|
|
622
|
-
responses: {
|
|
623
|
-
'201': {
|
|
624
|
-
description: 'Record created successfully',
|
|
625
|
-
content: {
|
|
626
|
-
'application/json': {
|
|
627
|
-
schema: {
|
|
628
|
-
type: 'object',
|
|
629
|
-
properties: {
|
|
630
|
-
id: { type: 'integer' },
|
|
631
|
-
message: { type: 'string' }
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
},
|
|
637
|
-
'400': { description: 'Bad request' },
|
|
638
|
-
'401': { description: 'Unauthorized' }
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
},
|
|
642
|
-
[`${basePath}/{id}`]: {
|
|
643
|
-
get: {
|
|
644
|
-
summary: `Get ${config.name} record by ID`,
|
|
645
|
-
description: 'Returns a single record',
|
|
646
|
-
tags: [tagName],
|
|
647
|
-
parameters: [
|
|
648
|
-
{
|
|
649
|
-
name: 'id',
|
|
650
|
-
in: 'path',
|
|
651
|
-
required: true,
|
|
652
|
-
schema: { type: 'integer' },
|
|
653
|
-
description: 'Record ID'
|
|
654
|
-
}
|
|
655
|
-
],
|
|
656
|
-
responses: {
|
|
657
|
-
'200': {
|
|
658
|
-
description: 'Record details',
|
|
659
|
-
content: {
|
|
660
|
-
'application/json': {
|
|
661
|
-
schema: { $ref: `#/components/schemas/${config.name}Record` }
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
},
|
|
665
|
-
'404': { description: 'Record not found' }
|
|
666
|
-
}
|
|
667
|
-
},
|
|
668
|
-
put: {
|
|
669
|
-
summary: `Update ${config.name} record`,
|
|
670
|
-
description: 'Update a record. Requires authentication and ownership.',
|
|
671
|
-
tags: [tagName],
|
|
672
|
-
security: [{ ApiKeyAuth: [] }],
|
|
673
|
-
parameters: [
|
|
674
|
-
{
|
|
675
|
-
name: 'id',
|
|
676
|
-
in: 'path',
|
|
677
|
-
required: true,
|
|
678
|
-
schema: { type: 'integer' },
|
|
679
|
-
description: 'Record ID'
|
|
680
|
-
}
|
|
681
|
-
],
|
|
682
|
-
requestBody: {
|
|
683
|
-
required: true,
|
|
684
|
-
content: {
|
|
685
|
-
'application/json': {
|
|
686
|
-
schema: { $ref: `#/components/schemas/${config.name}Input` }
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
},
|
|
690
|
-
responses: {
|
|
691
|
-
'200': { description: 'Record updated successfully' },
|
|
692
|
-
'400': { description: 'Bad request' },
|
|
693
|
-
'401': { description: 'Unauthorized' },
|
|
694
|
-
'403': { description: 'Forbidden - not owner' },
|
|
695
|
-
'404': { description: 'Record not found' }
|
|
696
|
-
}
|
|
697
|
-
},
|
|
698
|
-
delete: {
|
|
699
|
-
summary: `Delete ${config.name} record`,
|
|
700
|
-
description: 'Delete a record. Requires authentication and ownership.',
|
|
701
|
-
tags: [tagName],
|
|
702
|
-
security: [{ ApiKeyAuth: [] }],
|
|
703
|
-
parameters: [
|
|
704
|
-
{
|
|
705
|
-
name: 'id',
|
|
706
|
-
in: 'path',
|
|
707
|
-
required: true,
|
|
708
|
-
schema: { type: 'integer' },
|
|
709
|
-
description: 'Record ID'
|
|
710
|
-
}
|
|
711
|
-
],
|
|
712
|
-
responses: {
|
|
713
|
-
'200': { description: 'Record deleted successfully' },
|
|
714
|
-
'401': { description: 'Unauthorized' },
|
|
715
|
-
'403': { description: 'Forbidden - not owner' },
|
|
716
|
-
'404': { description: 'Record not found' }
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
};
|
|
721
|
-
// Add custom endpoints from configuration
|
|
722
|
-
if (config.endpoints) {
|
|
723
|
-
for (const endpoint of config.endpoints) {
|
|
724
|
-
const fullPath = `${basePath}${endpoint.path}`;
|
|
725
|
-
const method = endpoint.method.toLowerCase();
|
|
726
|
-
if (!paths[fullPath]) {
|
|
727
|
-
paths[fullPath] = {};
|
|
728
|
-
}
|
|
729
|
-
paths[fullPath][method] = {
|
|
730
|
-
summary: `Custom endpoint: ${endpoint.path}`,
|
|
731
|
-
tags: [tagName],
|
|
732
|
-
responses: {
|
|
733
|
-
'200': { description: 'Successful response' }
|
|
734
|
-
}
|
|
735
|
-
};
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
// Build input schema (excludes readonly fields)
|
|
739
|
-
const inputProperties = {};
|
|
740
|
-
for (const [columnName, columnDef] of Object.entries(config.columns)) {
|
|
741
|
-
inputProperties[columnName] = this.columnDefToOpenAPISchema(columnDef);
|
|
742
|
-
}
|
|
743
|
-
return {
|
|
744
|
-
paths,
|
|
745
|
-
tags: [
|
|
746
|
-
{
|
|
747
|
-
name: tagName,
|
|
748
|
-
description: config.description
|
|
749
|
-
}
|
|
750
|
-
],
|
|
751
|
-
schemas: {
|
|
752
|
-
[`${config.name}Record`]: {
|
|
753
|
-
type: 'object',
|
|
754
|
-
properties: columnProperties
|
|
755
|
-
},
|
|
756
|
-
[`${config.name}Input`]: {
|
|
757
|
-
type: 'object',
|
|
758
|
-
properties: inputProperties
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
};
|
|
762
|
-
}
|
|
763
|
-
/**
|
|
764
|
-
* Convert SQL column definition to OpenAPI schema type.
|
|
765
|
-
*
|
|
766
|
-
* @private
|
|
767
|
-
* @param {string} columnDef - SQL column definition (e.g., 'text not null', 'integer default 0')
|
|
768
|
-
* @returns {object} OpenAPI schema object
|
|
769
|
-
*/
|
|
770
|
-
columnDefToOpenAPISchema(columnDef) {
|
|
771
|
-
const lowerDef = columnDef.toLowerCase();
|
|
772
|
-
if (lowerDef.includes('integer') || lowerDef.includes('int')) {
|
|
773
|
-
return { type: 'integer' };
|
|
774
|
-
}
|
|
775
|
-
else if (lowerDef.includes('real') || lowerDef.includes('float') || lowerDef.includes('double')) {
|
|
776
|
-
return { type: 'number' };
|
|
777
|
-
}
|
|
778
|
-
else if (lowerDef.includes('boolean') || lowerDef.includes('bool')) {
|
|
779
|
-
return { type: 'boolean' };
|
|
780
|
-
}
|
|
781
|
-
else if (lowerDef.includes('timestamp') || lowerDef.includes('datetime')) {
|
|
782
|
-
return { type: 'string', format: 'date-time' };
|
|
783
|
-
}
|
|
784
|
-
else if (lowerDef.includes('date')) {
|
|
785
|
-
return { type: 'string', format: 'date' };
|
|
786
|
-
}
|
|
787
|
-
else {
|
|
788
|
-
return { type: 'string' };
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
/**
|
|
792
|
-
* Standard endpoint handlers for CRUD operations
|
|
793
|
-
*/
|
|
794
|
-
async handleGetAll(_req) {
|
|
795
|
-
try {
|
|
796
|
-
const records = await this.findAll();
|
|
797
|
-
return {
|
|
798
|
-
status: 200,
|
|
799
|
-
content: JSON.stringify(records),
|
|
800
|
-
headers: { 'Content-Type': 'application/json' }
|
|
801
|
-
};
|
|
802
|
-
}
|
|
803
|
-
catch (error) {
|
|
804
|
-
return {
|
|
805
|
-
status: 500,
|
|
806
|
-
content: JSON.stringify({
|
|
807
|
-
error: error instanceof Error ? error.message : 'Unknown error'
|
|
808
|
-
}),
|
|
809
|
-
headers: { 'Content-Type': 'application/json' }
|
|
810
|
-
};
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
async handleCreate(req) {
|
|
814
|
-
try {
|
|
815
|
-
// Check authentication
|
|
816
|
-
if (!ApisixAuthParser.hasValidAuth(req.headers || {})) {
|
|
817
|
-
return {
|
|
818
|
-
status: 401,
|
|
819
|
-
content: JSON.stringify({ error: 'Authentication required' }),
|
|
820
|
-
headers: { 'Content-Type': 'application/json' }
|
|
821
|
-
};
|
|
822
|
-
}
|
|
823
|
-
// Parse authenticated user
|
|
824
|
-
const authUser = ApisixAuthParser.parseAuthHeaders(req.headers || {});
|
|
825
|
-
if (!authUser) {
|
|
826
|
-
return {
|
|
827
|
-
status: 401,
|
|
828
|
-
content: JSON.stringify({ error: 'Invalid authentication headers' }),
|
|
829
|
-
headers: { 'Content-Type': 'application/json' }
|
|
830
|
-
};
|
|
831
|
-
}
|
|
832
|
-
if (!req?.body) {
|
|
833
|
-
return {
|
|
834
|
-
status: 400,
|
|
835
|
-
content: JSON.stringify({ error: 'Request body is required' }),
|
|
836
|
-
headers: { 'Content-Type': 'application/json' }
|
|
837
|
-
};
|
|
838
|
-
}
|
|
839
|
-
// Find or create user in database
|
|
840
|
-
const userRecord = await this.userService.findOrCreateUser(authUser);
|
|
841
|
-
if (!userRecord.id) {
|
|
842
|
-
return {
|
|
843
|
-
status: 500,
|
|
844
|
-
content: JSON.stringify({ error: 'Failed to retrieve user information' }),
|
|
845
|
-
headers: { 'Content-Type': 'application/json' }
|
|
846
|
-
};
|
|
847
|
-
}
|
|
848
|
-
// Add owner_id to the data
|
|
849
|
-
const dataWithOwner = {
|
|
850
|
-
...req.body,
|
|
851
|
-
owner_id: userRecord.id
|
|
852
|
-
};
|
|
853
|
-
const id = await this.create(dataWithOwner);
|
|
854
|
-
return {
|
|
855
|
-
status: 201,
|
|
856
|
-
content: JSON.stringify({ id, message: 'Record created successfully' }),
|
|
857
|
-
headers: { 'Content-Type': 'application/json' }
|
|
858
|
-
};
|
|
859
|
-
}
|
|
860
|
-
catch (error) {
|
|
861
|
-
return {
|
|
862
|
-
status: 500,
|
|
863
|
-
content: JSON.stringify({
|
|
864
|
-
error: error instanceof Error ? error.message : 'Unknown error'
|
|
865
|
-
}),
|
|
866
|
-
headers: { 'Content-Type': 'application/json' }
|
|
867
|
-
};
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
async handleGetById(req) {
|
|
871
|
-
try {
|
|
872
|
-
const { id } = req.params || {};
|
|
873
|
-
if (!id) {
|
|
874
|
-
return {
|
|
875
|
-
status: 400,
|
|
876
|
-
content: JSON.stringify({ error: 'ID parameter is required' }),
|
|
877
|
-
headers: { 'Content-Type': 'application/json' }
|
|
878
|
-
};
|
|
879
|
-
}
|
|
880
|
-
const record = await this.findById(parseInt(id));
|
|
881
|
-
if (!record) {
|
|
882
|
-
return {
|
|
883
|
-
status: 404,
|
|
884
|
-
content: JSON.stringify({ error: 'Record not found' }),
|
|
885
|
-
headers: { 'Content-Type': 'application/json' }
|
|
886
|
-
};
|
|
887
|
-
}
|
|
888
|
-
return {
|
|
889
|
-
status: 200,
|
|
890
|
-
content: JSON.stringify(record),
|
|
891
|
-
headers: { 'Content-Type': 'application/json' }
|
|
892
|
-
};
|
|
893
|
-
}
|
|
894
|
-
catch (error) {
|
|
895
|
-
return {
|
|
896
|
-
status: 500,
|
|
897
|
-
content: JSON.stringify({
|
|
898
|
-
error: error instanceof Error ? error.message : 'Unknown error'
|
|
899
|
-
}),
|
|
900
|
-
headers: { 'Content-Type': 'application/json' }
|
|
901
|
-
};
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
async handleUpdate(req) {
|
|
905
|
-
try {
|
|
906
|
-
// Check authentication
|
|
907
|
-
if (!ApisixAuthParser.hasValidAuth(req.headers || {})) {
|
|
908
|
-
return {
|
|
909
|
-
status: 401,
|
|
910
|
-
content: JSON.stringify({ error: 'Authentication required' }),
|
|
911
|
-
headers: { 'Content-Type': 'application/json' }
|
|
912
|
-
};
|
|
913
|
-
}
|
|
914
|
-
// Parse authenticated user
|
|
915
|
-
const authUser = ApisixAuthParser.parseAuthHeaders(req.headers || {});
|
|
916
|
-
if (!authUser) {
|
|
917
|
-
return {
|
|
918
|
-
status: 401,
|
|
919
|
-
content: JSON.stringify({ error: 'Invalid authentication headers' }),
|
|
920
|
-
headers: { 'Content-Type': 'application/json' }
|
|
921
|
-
};
|
|
922
|
-
}
|
|
923
|
-
const { id } = req.params || {};
|
|
924
|
-
if (!id) {
|
|
925
|
-
return {
|
|
926
|
-
status: 400,
|
|
927
|
-
content: JSON.stringify({ error: 'ID parameter is required' }),
|
|
928
|
-
headers: { 'Content-Type': 'application/json' }
|
|
929
|
-
};
|
|
930
|
-
}
|
|
931
|
-
if (!req?.body) {
|
|
932
|
-
return {
|
|
933
|
-
status: 400,
|
|
934
|
-
content: JSON.stringify({ error: 'Request body is required' }),
|
|
935
|
-
headers: { 'Content-Type': 'application/json' }
|
|
936
|
-
};
|
|
937
|
-
}
|
|
938
|
-
// Find or create user in database
|
|
939
|
-
const userRecord = await this.userService.findOrCreateUser(authUser);
|
|
940
|
-
if (!userRecord.id) {
|
|
941
|
-
return {
|
|
942
|
-
status: 500,
|
|
943
|
-
content: JSON.stringify({ error: 'Failed to retrieve user information' }),
|
|
944
|
-
headers: { 'Content-Type': 'application/json' }
|
|
945
|
-
};
|
|
946
|
-
}
|
|
947
|
-
// Check if record exists and belongs to this user
|
|
948
|
-
const existingRecord = await this.findById(parseInt(id));
|
|
949
|
-
if (!existingRecord) {
|
|
950
|
-
return {
|
|
951
|
-
status: 404,
|
|
952
|
-
content: JSON.stringify({ error: 'Record not found' }),
|
|
953
|
-
headers: { 'Content-Type': 'application/json' }
|
|
954
|
-
};
|
|
955
|
-
}
|
|
956
|
-
// Check ownership (only owner can modify their records)
|
|
957
|
-
if (!existingRecord.owner_id || existingRecord.owner_id !== userRecord.id) {
|
|
958
|
-
return {
|
|
959
|
-
status: 403,
|
|
960
|
-
content: JSON.stringify({ error: 'You can only modify your own records' }),
|
|
961
|
-
headers: { 'Content-Type': 'application/json' }
|
|
962
|
-
};
|
|
963
|
-
}
|
|
964
|
-
await this.update(parseInt(id), req.body);
|
|
965
|
-
return {
|
|
966
|
-
status: 200,
|
|
967
|
-
content: JSON.stringify({ message: 'Record updated successfully' }),
|
|
968
|
-
headers: { 'Content-Type': 'application/json' }
|
|
969
|
-
};
|
|
970
|
-
}
|
|
971
|
-
catch (error) {
|
|
972
|
-
return {
|
|
973
|
-
status: 500,
|
|
974
|
-
content: JSON.stringify({
|
|
975
|
-
error: error instanceof Error ? error.message : 'Unknown error'
|
|
976
|
-
}),
|
|
977
|
-
headers: { 'Content-Type': 'application/json' }
|
|
978
|
-
};
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
async handleDelete(req) {
|
|
982
|
-
try {
|
|
983
|
-
// Check authentication
|
|
984
|
-
if (!ApisixAuthParser.hasValidAuth(req.headers || {})) {
|
|
985
|
-
return {
|
|
986
|
-
status: 401,
|
|
987
|
-
content: JSON.stringify({ error: 'Authentication required' }),
|
|
988
|
-
headers: { 'Content-Type': 'application/json' }
|
|
989
|
-
};
|
|
990
|
-
}
|
|
991
|
-
// Parse authenticated user
|
|
992
|
-
const authUser = ApisixAuthParser.parseAuthHeaders(req.headers || {});
|
|
993
|
-
if (!authUser) {
|
|
994
|
-
return {
|
|
995
|
-
status: 401,
|
|
996
|
-
content: JSON.stringify({ error: 'Invalid authentication headers' }),
|
|
997
|
-
headers: { 'Content-Type': 'application/json' }
|
|
998
|
-
};
|
|
999
|
-
}
|
|
1000
|
-
const { id } = req.params || {};
|
|
1001
|
-
if (!id) {
|
|
1002
|
-
return {
|
|
1003
|
-
status: 400,
|
|
1004
|
-
content: JSON.stringify({ error: 'ID parameter is required' }),
|
|
1005
|
-
headers: { 'Content-Type': 'application/json' }
|
|
1006
|
-
};
|
|
1007
|
-
}
|
|
1008
|
-
// Find or create user in database
|
|
1009
|
-
const userRecord = await this.userService.findOrCreateUser(authUser);
|
|
1010
|
-
if (!userRecord.id) {
|
|
1011
|
-
return {
|
|
1012
|
-
status: 500,
|
|
1013
|
-
content: JSON.stringify({ error: 'Failed to retrieve user information' }),
|
|
1014
|
-
headers: { 'Content-Type': 'application/json' }
|
|
1015
|
-
};
|
|
1016
|
-
}
|
|
1017
|
-
// Check if record exists and belongs to this user
|
|
1018
|
-
const existingRecord = await this.findById(parseInt(id));
|
|
1019
|
-
if (!existingRecord) {
|
|
1020
|
-
return {
|
|
1021
|
-
status: 404,
|
|
1022
|
-
content: JSON.stringify({ error: 'Record not found' }),
|
|
1023
|
-
headers: { 'Content-Type': 'application/json' }
|
|
1024
|
-
};
|
|
1025
|
-
}
|
|
1026
|
-
// Check ownership (only owner can delete their records)
|
|
1027
|
-
if (!existingRecord.owner_id || existingRecord.owner_id !== userRecord.id) {
|
|
1028
|
-
return {
|
|
1029
|
-
status: 403,
|
|
1030
|
-
content: JSON.stringify({ error: 'You can only delete your own records' }),
|
|
1031
|
-
headers: { 'Content-Type': 'application/json' }
|
|
1032
|
-
};
|
|
1033
|
-
}
|
|
1034
|
-
await this.delete(parseInt(id));
|
|
1035
|
-
return {
|
|
1036
|
-
status: 200,
|
|
1037
|
-
content: JSON.stringify({ message: 'Record deleted successfully' }),
|
|
1038
|
-
headers: { 'Content-Type': 'application/json' }
|
|
1039
|
-
};
|
|
1040
|
-
}
|
|
1041
|
-
catch (error) {
|
|
1042
|
-
return {
|
|
1043
|
-
status: 500,
|
|
1044
|
-
content: JSON.stringify({
|
|
1045
|
-
error: error instanceof Error ? error.message : 'Unknown error'
|
|
1046
|
-
}),
|
|
1047
|
-
headers: { 'Content-Type': 'application/json' }
|
|
1048
|
-
};
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
1052
|
-
//# sourceMappingURL=custom_table_manager.js.map
|