@lobehub/lobehub 2.0.0-next.196 → 2.0.0-next.198
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/CHANGELOG.md +58 -0
- package/changelog/v1.json +21 -0
- package/package.json +2 -3
- package/packages/database/src/core/getTestDB.ts +50 -0
- package/packages/database/src/models/__tests__/_test_template.ts +1 -1
- package/packages/database/src/models/__tests__/agent.test.ts +1 -1
- package/packages/database/src/models/__tests__/aiModel.test.ts +1 -1
- package/packages/database/src/models/__tests__/aiProvider.test.ts +1 -1
- package/packages/database/src/models/__tests__/apiKey.test.ts +1 -1
- package/packages/database/src/models/__tests__/asyncTask.test.ts +1 -1
- package/packages/database/src/models/__tests__/chatGroup.test.ts +1 -1
- package/packages/database/src/models/__tests__/chunk.test.ts +1 -1
- package/packages/database/src/models/__tests__/document.test.ts +1 -1
- package/packages/database/src/models/__tests__/drizzleMigration.test.ts +1 -1
- package/packages/database/src/models/__tests__/embedding.test.ts +1 -1
- package/packages/database/src/models/__tests__/file.test.ts +1 -1
- package/packages/database/src/models/__tests__/generation.test.ts +1 -1
- package/packages/database/src/models/__tests__/generationBatch.test.ts +1 -1
- package/packages/database/src/models/__tests__/generationTopic.test.ts +1 -1
- package/packages/database/src/models/__tests__/knowledgeBase.test.ts +1 -1
- package/packages/database/src/models/__tests__/messages/message.create.test.ts +1 -1
- package/packages/database/src/models/__tests__/messages/message.delete.test.ts +1 -1
- package/packages/database/src/models/__tests__/messages/message.query.test.ts +1 -1
- package/packages/database/src/models/__tests__/messages/message.stats.test.ts +1 -1
- package/packages/database/src/models/__tests__/messages/message.thread-query.test.ts +1 -1
- package/packages/database/src/models/__tests__/messages/message.update.test.ts +1 -1
- package/packages/database/src/models/__tests__/messages/messageWithTask.test.ts +1 -1
- package/packages/database/src/models/__tests__/messages/queryWithMessageGroup.perf.test.ts +1 -1
- package/packages/database/src/models/__tests__/messages/queryWithMessageGroup.test.ts +1 -1
- package/packages/database/src/models/__tests__/oauthHandoff.test.ts +1 -1
- package/packages/database/src/models/__tests__/plugin.test.ts +1 -1
- package/packages/database/src/models/__tests__/session.test.ts +1 -1
- package/packages/database/src/models/__tests__/sessionGroup.test.ts +1 -1
- package/packages/database/src/models/__tests__/thread.test.ts +1 -1
- package/packages/database/src/models/__tests__/topicDocument.test.ts +1 -1
- package/packages/database/src/models/__tests__/topics/topic.create.test.ts +1 -1
- package/packages/database/src/models/__tests__/topics/topic.delete.test.ts +1 -1
- package/packages/database/src/models/__tests__/topics/topic.query.test.ts +1 -1
- package/packages/database/src/models/__tests__/topics/topic.stats.test.ts +1 -1
- package/packages/database/src/models/__tests__/topics/topic.update.test.ts +1 -1
- package/packages/database/src/models/__tests__/user.test.ts +1 -1
- package/packages/database/src/models/__tests__/userMemories.test.ts +1 -1
- package/packages/database/src/models/__tests__/userMemoryIdentity.test.ts +1 -1
- package/packages/database/src/models/userMemory/__tests__/context.test.ts +1 -1
- package/packages/database/src/models/userMemory/__tests__/experience.test.ts +1 -1
- package/packages/database/src/models/userMemory/__tests__/identity.test.ts +1 -1
- package/packages/database/src/models/userMemory/__tests__/preference.test.ts +1 -1
- package/packages/database/src/repositories/agentGroup/index.test.ts +1 -1
- package/packages/database/src/repositories/agentMigration/__tests__/agentMigrationRepo.test.ts +1 -1
- package/packages/database/src/repositories/aiInfra/index.test.ts +1 -1
- package/packages/database/src/repositories/compression/index.test.ts +1 -1
- package/packages/database/src/repositories/dataExporter/index.test.ts +1 -1
- package/packages/database/src/repositories/dataImporter/__tests__/index.test.ts +1 -1
- package/packages/database/src/repositories/dataImporter/deprecated/__tests__/index.test.ts +2 -2
- package/packages/database/src/repositories/home/__tests__/index.test.ts +1 -1
- package/packages/database/src/repositories/home/index.test.ts +1 -1
- package/packages/database/src/repositories/knowledge/index.test.ts +1 -1
- package/packages/database/src/repositories/search/index.test.ts +1 -1
- package/packages/database/src/repositories/topicImporter/__tests__/importTopic.test.ts +1 -1
- package/packages/database/src/repositories/userMemory/__tests__/UserMemoryTopicRepository.test.ts +1 -1
- package/packages/database/src/server/models/__tests__/adapter.test.ts +2 -2
- package/packages/database/src/server/models/__tests__/user.test.ts +2 -2
- package/packages/database/tests/test-utils.ts +1 -1
- package/packages/model-runtime/src/core/streams/openai/openai.test.ts +155 -0
- package/packages/model-runtime/src/core/streams/openai/openai.ts +26 -1
- package/packages/types/src/export.ts +1 -1
- package/packages/types/src/index.ts +0 -1
- package/src/features/ChatInput/ActionBar/Token/TokenTag.tsx +1 -0
- package/src/features/LibraryModal/AssignKnowledgeBase/List.tsx +12 -13
- package/src/features/ResourceManager/components/Explorer/MasonryView/index.tsx +1 -0
- package/src/server/routers/lambda/__tests__/file.test.ts +76 -3
- package/src/server/routers/lambda/file.ts +13 -1
- package/src/services/config.ts +2 -16
- package/packages/database/src/core/dbForTest.ts +0 -43
- package/packages/database/src/core/migrations.json +0 -1080
- package/packages/database/src/models/__tests__/_util.ts +0 -30
- package/packages/database/src/repositories/tableViewer/index.test.ts +0 -255
- package/packages/database/src/repositories/tableViewer/index.ts +0 -251
- package/packages/types/src/tableViewer.ts +0 -30
- package/scripts/migrateClientDB/compile-migrations.ts +0 -14
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { PGlite } from '@electric-sql/pglite';
|
|
2
|
-
import { vector } from '@electric-sql/pglite/vector';
|
|
3
|
-
import { drizzle } from 'drizzle-orm/pglite';
|
|
4
|
-
|
|
5
|
-
import migrations from '../../core/migrations.json';
|
|
6
|
-
import * as schema from '../../schemas';
|
|
7
|
-
import { LobeChatDatabase } from '../../type';
|
|
8
|
-
|
|
9
|
-
const isServerDBMode = process.env.TEST_SERVER_DB === '1';
|
|
10
|
-
|
|
11
|
-
let testClientDB: ReturnType<typeof drizzle<typeof schema>> | null = null;
|
|
12
|
-
|
|
13
|
-
export const getTestDB = async () => {
|
|
14
|
-
if (isServerDBMode) {
|
|
15
|
-
const { getTestDBInstance } = await import('../../core/dbForTest');
|
|
16
|
-
return await getTestDBInstance();
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
if (testClientDB) return testClientDB as unknown as LobeChatDatabase;
|
|
20
|
-
|
|
21
|
-
// 直接使用 pglite 内置资源,不需要从 CDN 下载
|
|
22
|
-
const pglite = new PGlite({ extensions: { vector } });
|
|
23
|
-
|
|
24
|
-
testClientDB = drizzle({ client: pglite, schema });
|
|
25
|
-
|
|
26
|
-
// @ts-expect-error - migrate internal API
|
|
27
|
-
await testClientDB.dialect.migrate(migrations, testClientDB.session, {});
|
|
28
|
-
|
|
29
|
-
return testClientDB as unknown as LobeChatDatabase;
|
|
30
|
-
};
|
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import { getTestDB } from '../../models/__tests__/_util';
|
|
4
|
-
import { LobeChatDatabase } from '../../type';
|
|
5
|
-
import { TableViewerRepo } from './index';
|
|
6
|
-
|
|
7
|
-
const userId = 'user-table-viewer';
|
|
8
|
-
|
|
9
|
-
// Mock database execution
|
|
10
|
-
const mockExecute = vi.fn();
|
|
11
|
-
const mockDB = {
|
|
12
|
-
execute: mockExecute,
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
let serverDB: LobeChatDatabase;
|
|
16
|
-
let repo: TableViewerRepo;
|
|
17
|
-
|
|
18
|
-
beforeAll(async () => {
|
|
19
|
-
serverDB = await getTestDB();
|
|
20
|
-
repo = new TableViewerRepo(serverDB, userId);
|
|
21
|
-
}, 30000);
|
|
22
|
-
|
|
23
|
-
beforeEach(() => {
|
|
24
|
-
vi.clearAllMocks();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
describe('TableViewerRepo', () => {
|
|
28
|
-
describe('getAllTables', () => {
|
|
29
|
-
it('should handle custom schema', async () => {
|
|
30
|
-
const result = await repo.getAllTables('custom_schema');
|
|
31
|
-
expect(result).toBeDefined();
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
describe('getTableDetails', () => {
|
|
36
|
-
it('should return table column details', async () => {
|
|
37
|
-
const tableName = 'test_table';
|
|
38
|
-
const mockColumns = {
|
|
39
|
-
rows: [
|
|
40
|
-
{
|
|
41
|
-
column_name: 'id',
|
|
42
|
-
data_type: 'uuid',
|
|
43
|
-
is_nullable: 'NO',
|
|
44
|
-
column_default: null,
|
|
45
|
-
is_primary_key: true,
|
|
46
|
-
foreign_key: null,
|
|
47
|
-
},
|
|
48
|
-
],
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
const testRepo = new TableViewerRepo(mockDB as any, userId);
|
|
52
|
-
mockExecute.mockResolvedValueOnce(mockColumns);
|
|
53
|
-
|
|
54
|
-
const result = await testRepo.getTableDetails(tableName);
|
|
55
|
-
|
|
56
|
-
expect(result).toEqual([
|
|
57
|
-
{
|
|
58
|
-
name: 'id',
|
|
59
|
-
type: 'uuid',
|
|
60
|
-
nullable: false,
|
|
61
|
-
defaultValue: null,
|
|
62
|
-
isPrimaryKey: true,
|
|
63
|
-
foreignKey: null,
|
|
64
|
-
},
|
|
65
|
-
]);
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
describe('getTableData', () => {
|
|
70
|
-
it('should return paginated data with filters', async () => {
|
|
71
|
-
const tableName = 'test_table';
|
|
72
|
-
const pagination = {
|
|
73
|
-
page: 1,
|
|
74
|
-
pageSize: 10,
|
|
75
|
-
sortBy: 'id',
|
|
76
|
-
sortOrder: 'desc' as const,
|
|
77
|
-
};
|
|
78
|
-
const filters = [
|
|
79
|
-
{
|
|
80
|
-
column: 'name',
|
|
81
|
-
operator: 'contains' as const,
|
|
82
|
-
value: 'test',
|
|
83
|
-
},
|
|
84
|
-
];
|
|
85
|
-
|
|
86
|
-
const mockData = {
|
|
87
|
-
rows: [{ id: 1, name: 'test' }],
|
|
88
|
-
};
|
|
89
|
-
const mockCount = {
|
|
90
|
-
rows: [{ total: 1 }],
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
const testRepo = new TableViewerRepo(mockDB as any, userId);
|
|
94
|
-
mockExecute.mockResolvedValueOnce(mockData).mockResolvedValueOnce(mockCount);
|
|
95
|
-
|
|
96
|
-
const result = await testRepo.getTableData(tableName, pagination, filters);
|
|
97
|
-
|
|
98
|
-
expect(result).toEqual({
|
|
99
|
-
data: mockData.rows,
|
|
100
|
-
pagination: {
|
|
101
|
-
page: 1,
|
|
102
|
-
pageSize: 10,
|
|
103
|
-
total: 1,
|
|
104
|
-
},
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
describe('updateRow', () => {
|
|
110
|
-
it('should update and return row data', async () => {
|
|
111
|
-
const tableName = 'test_table';
|
|
112
|
-
const id = '123';
|
|
113
|
-
const primaryKeyColumn = 'id';
|
|
114
|
-
const data = { name: 'updated' };
|
|
115
|
-
|
|
116
|
-
const mockResult = {
|
|
117
|
-
rows: [{ id: '123', name: 'updated' }],
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const testRepo = new TableViewerRepo(mockDB as any, userId);
|
|
121
|
-
mockExecute.mockResolvedValueOnce(mockResult);
|
|
122
|
-
|
|
123
|
-
const result = await testRepo.updateRow(tableName, id, primaryKeyColumn, data);
|
|
124
|
-
|
|
125
|
-
expect(result).toEqual(mockResult.rows[0]);
|
|
126
|
-
expect(mockExecute).toHaveBeenCalledTimes(1);
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
describe('deleteRow', () => {
|
|
131
|
-
it('should delete a row', async () => {
|
|
132
|
-
const tableName = 'test_table';
|
|
133
|
-
const id = '123';
|
|
134
|
-
const primaryKeyColumn = 'id';
|
|
135
|
-
|
|
136
|
-
const testRepo = new TableViewerRepo(mockDB as any, userId);
|
|
137
|
-
mockExecute.mockResolvedValueOnce({ rows: [] });
|
|
138
|
-
|
|
139
|
-
await testRepo.deleteRow(tableName, id, primaryKeyColumn);
|
|
140
|
-
|
|
141
|
-
expect(mockExecute).toHaveBeenCalledTimes(1);
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
describe('insertRow', () => {
|
|
146
|
-
it('should insert and return new row data', async () => {
|
|
147
|
-
const tableName = 'test_table';
|
|
148
|
-
const data = { name: 'new row' };
|
|
149
|
-
|
|
150
|
-
const mockResult = {
|
|
151
|
-
rows: [{ id: '123', name: 'new row' }],
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
const testRepo = new TableViewerRepo(mockDB as any, userId);
|
|
155
|
-
mockExecute.mockResolvedValueOnce(mockResult);
|
|
156
|
-
|
|
157
|
-
const result = await testRepo.insertRow(tableName, data);
|
|
158
|
-
|
|
159
|
-
expect(result).toEqual(mockResult.rows[0]);
|
|
160
|
-
expect(mockExecute).toHaveBeenCalledTimes(1);
|
|
161
|
-
});
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
describe('getTableCount', () => {
|
|
165
|
-
it('should return table count', async () => {
|
|
166
|
-
const tableName = 'test_table';
|
|
167
|
-
const mockResult = {
|
|
168
|
-
rows: [{ total: 42 }],
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
const testRepo = new TableViewerRepo(mockDB as any, userId);
|
|
172
|
-
mockExecute.mockResolvedValueOnce(mockResult);
|
|
173
|
-
|
|
174
|
-
const result = await testRepo.getTableCount(tableName);
|
|
175
|
-
|
|
176
|
-
expect(result).toBe(42);
|
|
177
|
-
expect(mockExecute).toHaveBeenCalledTimes(1);
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
describe('batchDelete', () => {
|
|
182
|
-
it('should delete multiple rows', async () => {
|
|
183
|
-
const tableName = 'test_table';
|
|
184
|
-
const ids = ['1', '2', '3'];
|
|
185
|
-
const primaryKeyColumn = 'id';
|
|
186
|
-
|
|
187
|
-
const testRepo = new TableViewerRepo(mockDB as any, userId);
|
|
188
|
-
mockExecute.mockResolvedValueOnce({ rows: [] });
|
|
189
|
-
|
|
190
|
-
await testRepo.batchDelete(tableName, ids, primaryKeyColumn);
|
|
191
|
-
|
|
192
|
-
expect(mockExecute).toHaveBeenCalledTimes(1);
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
describe('exportTableData', () => {
|
|
197
|
-
it('should export table data with default pagination', async () => {
|
|
198
|
-
const tableName = 'test_table';
|
|
199
|
-
const mockData = {
|
|
200
|
-
rows: [{ id: 1, name: 'test' }],
|
|
201
|
-
};
|
|
202
|
-
const mockCount = {
|
|
203
|
-
rows: [{ total: 1 }],
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
const testRepo = new TableViewerRepo(mockDB as any, userId);
|
|
207
|
-
mockExecute.mockResolvedValueOnce(mockData).mockResolvedValueOnce(mockCount);
|
|
208
|
-
|
|
209
|
-
const result = await testRepo.exportTableData(tableName);
|
|
210
|
-
|
|
211
|
-
expect(result).toEqual({
|
|
212
|
-
data: mockData.rows,
|
|
213
|
-
pagination: {
|
|
214
|
-
page: 1,
|
|
215
|
-
pageSize: 1000,
|
|
216
|
-
total: 1,
|
|
217
|
-
},
|
|
218
|
-
});
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
it('should export table data with custom pagination and filters', async () => {
|
|
222
|
-
const tableName = 'test_table';
|
|
223
|
-
const pagination = { page: 2, pageSize: 50 };
|
|
224
|
-
const filters = [
|
|
225
|
-
{
|
|
226
|
-
column: 'status',
|
|
227
|
-
operator: 'equals' as const,
|
|
228
|
-
value: 'active',
|
|
229
|
-
},
|
|
230
|
-
];
|
|
231
|
-
|
|
232
|
-
const mockData = {
|
|
233
|
-
rows: [{ id: 1, status: 'active' }],
|
|
234
|
-
};
|
|
235
|
-
const mockCount = {
|
|
236
|
-
rows: [{ total: 1 }],
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
mockExecute.mockResolvedValueOnce(mockData).mockResolvedValueOnce(mockCount);
|
|
240
|
-
|
|
241
|
-
const testRepo = new TableViewerRepo(mockDB as any, userId);
|
|
242
|
-
|
|
243
|
-
const result = await testRepo.exportTableData(tableName, pagination, filters);
|
|
244
|
-
|
|
245
|
-
expect(result).toEqual({
|
|
246
|
-
data: mockData.rows,
|
|
247
|
-
pagination: {
|
|
248
|
-
page: 2,
|
|
249
|
-
pageSize: 50,
|
|
250
|
-
total: 1,
|
|
251
|
-
},
|
|
252
|
-
});
|
|
253
|
-
});
|
|
254
|
-
});
|
|
255
|
-
});
|
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
FilterCondition,
|
|
3
|
-
PaginationParams,
|
|
4
|
-
TableBasicInfo,
|
|
5
|
-
TableColumnInfo,
|
|
6
|
-
} from '@lobechat/types';
|
|
7
|
-
import { sql } from 'drizzle-orm';
|
|
8
|
-
import pMap from 'p-map';
|
|
9
|
-
|
|
10
|
-
import { LobeChatDatabase } from '../../type';
|
|
11
|
-
|
|
12
|
-
export class TableViewerRepo {
|
|
13
|
-
private userId: string;
|
|
14
|
-
private db: LobeChatDatabase;
|
|
15
|
-
|
|
16
|
-
constructor(db: LobeChatDatabase, userId: string) {
|
|
17
|
-
this.userId = userId;
|
|
18
|
-
this.db = db;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Get all tables in the database
|
|
23
|
-
*/
|
|
24
|
-
async getAllTables(schema = 'public'): Promise<TableBasicInfo[]> {
|
|
25
|
-
const query = sql`
|
|
26
|
-
SELECT
|
|
27
|
-
table_name as name,
|
|
28
|
-
table_type as type
|
|
29
|
-
FROM information_schema.tables
|
|
30
|
-
WHERE table_schema = ${schema}
|
|
31
|
-
ORDER BY table_name;
|
|
32
|
-
`;
|
|
33
|
-
|
|
34
|
-
const tables = await this.db.execute(query);
|
|
35
|
-
|
|
36
|
-
const tableNames = tables.rows.map((row) => row.name) as string[];
|
|
37
|
-
|
|
38
|
-
const counts = await pMap(tableNames, async (name) => this.getTableCount(name), {
|
|
39
|
-
concurrency: 10,
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
return tables.rows.map((row, index) => ({
|
|
43
|
-
count: counts[index],
|
|
44
|
-
name: row.name,
|
|
45
|
-
type: row.type,
|
|
46
|
-
})) as TableBasicInfo[];
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Get detailed structure information for a specified table
|
|
51
|
-
*/
|
|
52
|
-
async getTableDetails(tableName: string): Promise<TableColumnInfo[]> {
|
|
53
|
-
const query = sql`
|
|
54
|
-
SELECT
|
|
55
|
-
c.column_name,
|
|
56
|
-
c.data_type,
|
|
57
|
-
c.is_nullable,
|
|
58
|
-
c.column_default,
|
|
59
|
-
-- Primary key information
|
|
60
|
-
(
|
|
61
|
-
SELECT true
|
|
62
|
-
FROM information_schema.table_constraints tc
|
|
63
|
-
JOIN information_schema.key_column_usage kcu
|
|
64
|
-
ON tc.constraint_name = kcu.constraint_name
|
|
65
|
-
WHERE tc.table_name = c.table_name
|
|
66
|
-
AND kcu.column_name = c.column_name
|
|
67
|
-
AND tc.constraint_type = 'PRIMARY KEY'
|
|
68
|
-
) is_primary_key,
|
|
69
|
-
-- Foreign key information
|
|
70
|
-
(
|
|
71
|
-
SELECT json_build_object(
|
|
72
|
-
'table', ccu.table_name,
|
|
73
|
-
'column', ccu.column_name
|
|
74
|
-
)
|
|
75
|
-
FROM information_schema.table_constraints tc
|
|
76
|
-
JOIN information_schema.key_column_usage kcu
|
|
77
|
-
ON tc.constraint_name = kcu.constraint_name
|
|
78
|
-
JOIN information_schema.constraint_column_usage ccu
|
|
79
|
-
ON ccu.constraint_name = tc.constraint_name
|
|
80
|
-
WHERE tc.table_name = c.table_name
|
|
81
|
-
AND kcu.column_name = c.column_name
|
|
82
|
-
AND tc.constraint_type = 'FOREIGN KEY'
|
|
83
|
-
) foreign_key
|
|
84
|
-
FROM information_schema.columns c
|
|
85
|
-
WHERE c.table_name = ${tableName}
|
|
86
|
-
AND c.table_schema = 'public'
|
|
87
|
-
ORDER BY c.ordinal_position;
|
|
88
|
-
`;
|
|
89
|
-
|
|
90
|
-
const columns = await this.db.execute(query);
|
|
91
|
-
|
|
92
|
-
return columns.rows.map((col: any) => ({
|
|
93
|
-
defaultValue: col.column_default,
|
|
94
|
-
foreignKey: col.foreign_key,
|
|
95
|
-
isPrimaryKey: !!col.is_primary_key,
|
|
96
|
-
name: col.column_name,
|
|
97
|
-
nullable: col.is_nullable === 'YES',
|
|
98
|
-
type: col.data_type,
|
|
99
|
-
}));
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Get table data with support for pagination, sorting, and filtering
|
|
104
|
-
*/
|
|
105
|
-
async getTableData(tableName: string, pagination: PaginationParams, filters?: FilterCondition[]) {
|
|
106
|
-
const offset = (pagination.page - 1) * pagination.pageSize;
|
|
107
|
-
|
|
108
|
-
// Build base query
|
|
109
|
-
let baseQuery = sql`SELECT * FROM ${sql.identifier(tableName)}`;
|
|
110
|
-
|
|
111
|
-
// Add filter conditions
|
|
112
|
-
if (filters && filters.length > 0) {
|
|
113
|
-
const whereConditions = filters.map((filter) => {
|
|
114
|
-
const column = sql.identifier(filter.column);
|
|
115
|
-
|
|
116
|
-
switch (filter.operator) {
|
|
117
|
-
case 'equals': {
|
|
118
|
-
return sql`${column} = ${filter.value}`;
|
|
119
|
-
}
|
|
120
|
-
case 'contains': {
|
|
121
|
-
return sql`${column} ILIKE ${`%${filter.value}%`}`;
|
|
122
|
-
}
|
|
123
|
-
case 'startsWith': {
|
|
124
|
-
return sql`${column} ILIKE ${`${filter.value}%`}`;
|
|
125
|
-
}
|
|
126
|
-
case 'endsWith': {
|
|
127
|
-
return sql`${column} ILIKE ${`%${filter.value}`}`;
|
|
128
|
-
}
|
|
129
|
-
default: {
|
|
130
|
-
return sql`1=1`;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
baseQuery = sql`${baseQuery} WHERE ${sql.join(whereConditions, sql` AND `)}`;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Add sorting
|
|
139
|
-
if (pagination.sortBy) {
|
|
140
|
-
const direction = pagination.sortOrder === 'desc' ? sql`DESC` : sql`ASC`;
|
|
141
|
-
baseQuery = sql`${baseQuery} ORDER BY ${sql.identifier(pagination.sortBy)} ${direction}`;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Add pagination
|
|
145
|
-
const query = sql`${baseQuery} LIMIT ${pagination.pageSize} OFFSET ${offset}`;
|
|
146
|
-
|
|
147
|
-
// Get total count
|
|
148
|
-
const countQuery = sql`SELECT COUNT(*) as total FROM ${sql.identifier(tableName)}`;
|
|
149
|
-
|
|
150
|
-
// Execute queries in parallel
|
|
151
|
-
const [data, count] = await Promise.all([this.db.execute(query), this.db.execute(countQuery)]);
|
|
152
|
-
|
|
153
|
-
return {
|
|
154
|
-
data: data.rows,
|
|
155
|
-
pagination: {
|
|
156
|
-
page: pagination.page,
|
|
157
|
-
pageSize: pagination.pageSize,
|
|
158
|
-
total: Number(count.rows[0].total),
|
|
159
|
-
},
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Update a row in the table
|
|
165
|
-
*/
|
|
166
|
-
async updateRow(
|
|
167
|
-
tableName: string,
|
|
168
|
-
id: string,
|
|
169
|
-
primaryKeyColumn: string,
|
|
170
|
-
data: Record<string, any>,
|
|
171
|
-
) {
|
|
172
|
-
const setColumns = Object.entries(data).map(([key, value]) => {
|
|
173
|
-
return sql`${sql.identifier(key)} = ${value}`;
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
const query = sql`
|
|
177
|
-
UPDATE ${sql.identifier(tableName)}
|
|
178
|
-
SET ${sql.join(setColumns, sql`, `)}
|
|
179
|
-
WHERE ${sql.identifier(primaryKeyColumn)} = ${id}
|
|
180
|
-
RETURNING *
|
|
181
|
-
`;
|
|
182
|
-
|
|
183
|
-
const result = await this.db.execute(query);
|
|
184
|
-
return result.rows[0];
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Delete a row from the table
|
|
189
|
-
*/
|
|
190
|
-
async deleteRow(tableName: string, id: string, primaryKeyColumn: string) {
|
|
191
|
-
const query = sql`
|
|
192
|
-
DELETE FROM ${sql.identifier(tableName)}
|
|
193
|
-
WHERE ${sql.identifier(primaryKeyColumn)} = ${id}
|
|
194
|
-
`;
|
|
195
|
-
|
|
196
|
-
await this.db.execute(query);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Insert new row data
|
|
201
|
-
*/
|
|
202
|
-
async insertRow(tableName: string, data: Record<string, any>) {
|
|
203
|
-
const columns = Object.keys(data).map((key) => sql.identifier(key));
|
|
204
|
-
const values = Object.values(data);
|
|
205
|
-
|
|
206
|
-
const query = sql`
|
|
207
|
-
INSERT INTO ${sql.identifier(tableName)}
|
|
208
|
-
(${sql.join(columns, sql`, `)})
|
|
209
|
-
VALUES (${sql.join(
|
|
210
|
-
values.map((v) => sql`${v}`),
|
|
211
|
-
sql`, `,
|
|
212
|
-
)})
|
|
213
|
-
RETURNING *
|
|
214
|
-
`;
|
|
215
|
-
|
|
216
|
-
const result = await this.db.execute(query);
|
|
217
|
-
return result.rows[0];
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Get total record count of a table
|
|
222
|
-
*/
|
|
223
|
-
async getTableCount(tableName: string): Promise<number> {
|
|
224
|
-
const query = sql`SELECT COUNT(*) as total FROM ${sql.identifier(tableName)}`;
|
|
225
|
-
const result = await this.db.execute(query);
|
|
226
|
-
return Number(result.rows[0].total);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Batch delete data
|
|
231
|
-
*/
|
|
232
|
-
async batchDelete(tableName: string, ids: string[], primaryKeyColumn: string) {
|
|
233
|
-
const query = sql`
|
|
234
|
-
DELETE FROM ${sql.identifier(tableName)}
|
|
235
|
-
WHERE ${sql.identifier(primaryKeyColumn)} = ANY(${ids})
|
|
236
|
-
`;
|
|
237
|
-
|
|
238
|
-
await this.db.execute(query);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Export table data (supports paginated export)
|
|
243
|
-
*/
|
|
244
|
-
async exportTableData(
|
|
245
|
-
tableName: string,
|
|
246
|
-
pagination?: PaginationParams,
|
|
247
|
-
filters?: FilterCondition[],
|
|
248
|
-
) {
|
|
249
|
-
return this.getTableData(tableName, pagination || { page: 1, pageSize: 1000 }, filters);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
export interface TableBasicInfo {
|
|
2
|
-
count: number;
|
|
3
|
-
name: string;
|
|
4
|
-
type: 'BASE TABLE' | 'VIEW';
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export interface TableColumnInfo {
|
|
8
|
-
defaultValue?: string;
|
|
9
|
-
foreignKey?: {
|
|
10
|
-
column: string;
|
|
11
|
-
table: string;
|
|
12
|
-
};
|
|
13
|
-
isPrimaryKey: boolean;
|
|
14
|
-
name: string;
|
|
15
|
-
nullable: boolean;
|
|
16
|
-
type: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface PaginationParams {
|
|
20
|
-
page: number;
|
|
21
|
-
pageSize: number;
|
|
22
|
-
sortBy?: string;
|
|
23
|
-
sortOrder?: 'asc' | 'desc';
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface FilterCondition {
|
|
27
|
-
column: string;
|
|
28
|
-
operator: 'equals' | 'contains' | 'startsWith' | 'endsWith';
|
|
29
|
-
value: any;
|
|
30
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { readMigrationFiles } from 'drizzle-orm/migrator';
|
|
2
|
-
import { writeFileSync } from 'node:fs';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
|
|
5
|
-
const dbBase = join(__dirname, '../../packages/database');
|
|
6
|
-
const migrationsFolder = join(dbBase, './migrations');
|
|
7
|
-
const migrations = readMigrationFiles({ migrationsFolder: migrationsFolder });
|
|
8
|
-
|
|
9
|
-
writeFileSync(
|
|
10
|
-
join(dbBase, './src/core/migrations.json'),
|
|
11
|
-
JSON.stringify(migrations, null, 2), // null, 2 adds indentation for better readability
|
|
12
|
-
);
|
|
13
|
-
|
|
14
|
-
console.log('🏁 client migrations.json compiled!');
|