mirror of
				https://github.com/ZeroCatDev/ClassworksKV.git
				synced 2025-10-25 03:53:10 +00:00 
			
		
		
		
	Compare commits
	
		
			4 Commits
		
	
	
		
			f1dba22f75
			...
			aea47eba7d
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | aea47eba7d | ||
|   | f250deb2bb | ||
|   | 15691b7333 | ||
|   | 22838ee71a | 
| @ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "ClassworksKV", | ||||
|   "version": "1.0.5", | ||||
|   "version": "1.0.7", | ||||
|   "private": true, | ||||
|   "scripts": { | ||||
|     "start": "node ./bin/www", | ||||
|  | ||||
							
								
								
									
										54
									
								
								routes/kv.js
									
									
									
									
									
								
							
							
						
						
									
										54
									
								
								routes/kv.js
									
									
									
									
									
								
							| @ -234,6 +234,60 @@ router.delete( | ||||
|   }) | ||||
| ); | ||||
| 
 | ||||
| /** | ||||
|  * GET /:namespace/_keys | ||||
|  * 获取指定命名空间下的键名列表(分页,不包括内容) | ||||
|  */ | ||||
| router.get( | ||||
|   "/:namespace/_keys", | ||||
|   checkRestrictedUUID, | ||||
|   readAuthMiddleware, | ||||
|   errors.catchAsync(async (req, res, next) => { | ||||
|     const { namespace } = req.params; | ||||
|     const { sortBy, sortDir, limit, skip } = req.query; | ||||
| 
 | ||||
|     // 构建选项
 | ||||
|     const options = { | ||||
|       sortBy: sortBy || "key", | ||||
|       sortDir: sortDir || "asc", | ||||
|       limit: limit ? parseInt(limit) : 100, | ||||
|       skip: skip ? parseInt(skip) : 0, | ||||
|     }; | ||||
| 
 | ||||
|     const keys = await kvStore.listKeysOnly(namespace, options); | ||||
| 
 | ||||
|     // 获取总记录数
 | ||||
|     const totalRows = await kvStore.count(namespace); | ||||
| 
 | ||||
|     // 构建响应对象
 | ||||
|     const response = { | ||||
|       keys: keys, | ||||
|       total_rows: totalRows, | ||||
|       current_page: { | ||||
|         limit: options.limit, | ||||
|         skip: options.skip, | ||||
|         count: keys.length, | ||||
|       }, | ||||
|     }; | ||||
| 
 | ||||
|     // 如果还有更多数据,添加load_more字段
 | ||||
|     const nextSkip = options.skip + options.limit; | ||||
|     if (nextSkip < totalRows) { | ||||
|       const baseUrl = `${req.baseUrl}/${namespace}/_keys`; | ||||
|       const queryParams = new URLSearchParams({ | ||||
|         sortBy: options.sortBy, | ||||
|         sortDir: options.sortDir, | ||||
|         limit: options.limit, | ||||
|         skip: nextSkip, | ||||
|       }).toString(); | ||||
| 
 | ||||
|       response.load_more = `${baseUrl}?${queryParams}`; | ||||
|     } | ||||
| 
 | ||||
|     return res.json(response); | ||||
|   }) | ||||
| ); | ||||
| 
 | ||||
| /** | ||||
|  * GET /:namespace | ||||
|  * 获取指定命名空间下的所有键名及元数据列表 | ||||
|  | ||||
| @ -160,6 +160,36 @@ class KVStore { | ||||
|     })); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * 获取指定命名空间下的键名列表(不包括内容) | ||||
|    * @param {string} namespace - 命名空间 | ||||
|    * @param {object} options - 查询选项 | ||||
|    * @returns {Array} 键名列表 | ||||
|    */ | ||||
|   async listKeysOnly(namespace, options = {}) { | ||||
|     const { sortBy = "key", sortDir = "asc", limit = 100, skip = 0 } = options; | ||||
| 
 | ||||
|     // 构建排序条件
 | ||||
|     const orderBy = {}; | ||||
|     orderBy[sortBy] = sortDir.toLowerCase(); | ||||
| 
 | ||||
|     // 查询以命名空间开头的所有键,只选择键名
 | ||||
|     const items = await prisma.kVStore.findMany({ | ||||
|       where: { | ||||
|         namespace: namespace, | ||||
|       }, | ||||
|       select: { | ||||
|         key: true, | ||||
|       }, | ||||
|       orderBy, | ||||
|       take: limit, | ||||
|       skip: skip, | ||||
|     }); | ||||
| 
 | ||||
|     // 只返回键名数组
 | ||||
|     return items.map((item) => item.key); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * 统计指定命名空间下的键值对数量 | ||||
|    * @param {string} namespace - 命名空间 | ||||
|  | ||||
| @ -20,37 +20,6 @@ | ||||
|       border-radius: 5px; | ||||
|       margin-bottom: 20px; | ||||
|     } | ||||
|     .form-section { | ||||
|       background-color: #f0f8ff; | ||||
|       padding: 20px; | ||||
|       border-radius: 5px; | ||||
|       margin-top: 30px; | ||||
|     } | ||||
|     textarea { | ||||
|       width: 100%; | ||||
|       height: 200px; | ||||
|       margin-bottom: 10px; | ||||
|       font-family: monospace; | ||||
|     } | ||||
|     button { | ||||
|       padding: 10px 15px; | ||||
|       background-color: #4CAF50; | ||||
|       color: white; | ||||
|       border: none; | ||||
|       border-radius: 4px; | ||||
|       cursor: pointer; | ||||
|     } | ||||
|     button:hover { | ||||
|       background-color: #45a049; | ||||
|     } | ||||
|     #response { | ||||
|       white-space: pre-wrap; | ||||
|       background-color: #f5f5f5; | ||||
|       padding: 15px; | ||||
|       border-radius: 5px; | ||||
|       margin-top: 20px; | ||||
|       font-family: monospace; | ||||
|     } | ||||
|   </style> | ||||
| </head> | ||||
| 
 | ||||
| @ -62,70 +31,6 @@ | ||||
|   <div class="readme-content"> | ||||
|     <pre><%= readmeValue.readme %></pre> | ||||
|   </div> | ||||
| 
 | ||||
|   <div class="form-section"> | ||||
|     <h2>批量数据导入测试</h2> | ||||
|     <form id="batchImportForm"> | ||||
|       <div> | ||||
|         <label for="namespace">命名空间:</label> | ||||
|         <input type="text" id="namespace" name="namespace" placeholder="输入命名空间" required> | ||||
|         <button type="button" id="generateUUID">生成UUID</button> | ||||
|       </div> | ||||
|       <div> | ||||
|         <label for="data">JSON数据 (格式: {"key":{}, "key2":{}})</label> | ||||
|         <textarea id="data" name="data" placeholder='{"key1": {"value": "test1"}, "key2": {"value": "test2"}}' required></textarea> | ||||
|       </div> | ||||
|       <button type="submit">导入数据</button> | ||||
|     </form> | ||||
|     <div> | ||||
|       <h3>响应结果:</h3> | ||||
|       <pre id="response"></pre> | ||||
|     </div> | ||||
|   </div> | ||||
| 
 | ||||
|   <script> | ||||
|     document.getElementById('generateUUID').addEventListener('click', async function() { | ||||
|       try { | ||||
|         const response = await fetch('/_uuid', { | ||||
|           method: 'GET' | ||||
|         }); | ||||
|         const data = await response.json(); | ||||
|         document.getElementById('namespace').value = data.namespace; | ||||
|       } catch (error) { | ||||
|         console.error('Error:', error); | ||||
|         document.getElementById('response').textContent = '获取UUID失败: ' + error.message; | ||||
|       } | ||||
|     }); | ||||
| 
 | ||||
|     document.getElementById('batchImportForm').addEventListener('submit', async function(e) { | ||||
|       e.preventDefault(); | ||||
|       const namespace = document.getElementById('namespace').value; | ||||
|       let jsonData; | ||||
| 
 | ||||
|       try { | ||||
|         jsonData = JSON.parse(document.getElementById('data').value); | ||||
|       } catch (error) { | ||||
|         document.getElementById('response').textContent = 'JSON解析错误: ' + error.message; | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       try { | ||||
|         const response = await fetch(`/${namespace}/_batchimport`, { | ||||
|           method: 'POST', | ||||
|           headers: { | ||||
|             'Content-Type': 'application/json' | ||||
|           }, | ||||
|           body: JSON.stringify(jsonData) | ||||
|         }); | ||||
| 
 | ||||
|         const result = await response.json(); | ||||
|         document.getElementById('response').textContent = JSON.stringify(result, null, 2); | ||||
|       } catch (error) { | ||||
|         console.error('Error:', error); | ||||
|         document.getElementById('response').textContent = '请求失败: ' + error.message; | ||||
|       } | ||||
|     }); | ||||
|   </script> | ||||
| </body> | ||||
| 
 | ||||
| </html> | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user