1
0
mirror of https://github.com/ZeroCatDev/ClassworksKV.git synced 2025-09-03 16:19:24 +00:00

Implement device authentication middleware and Prisma model for device management. Update routes to include device info retrieval and password management, enhancing security and functionality.

This commit is contained in:
SunWuyuan 2025-05-17 20:02:55 +08:00
parent 69887f9c47
commit 3bff254a22
No known key found for this signature in database
GPG Key ID: A6A54CF66F56BB64
5 changed files with 259 additions and 343 deletions

View File

@ -1,22 +1,120 @@
import { siteKey } from "../config.js";
import AppError from "../utils/errors.js";
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const checkSiteKey = (req, res, next) => {
export const ACCESS_TYPES = {
NO_PASSWORD_WRITABLE: 'NO_PASSWORD_WRITABLE',
NO_PASSWORD_READABLE: 'NO_PASSWORD_READABLE',
NO_PASSWORD_UNREADABLE: 'NO_PASSWORD_UNREADABLE'
};
export const checkSiteKey = (req, res, next) => {
const providedKey = req.headers['x-site-key'];
if (!siteKey) {
return next();
}
if (!providedKey || providedKey !== siteKey) {
const error = AppError.createError(
AppError.HTTP_STATUS.UNAUTHORIZED,
"此服务器已开启站点密钥验证,请在请求头中添加 x-site-key 以继续访问"
);
return res.status(error.statusCode).json(error);
}
if (!providedKey || providedKey !== siteKey) {
const error = AppError.createError(
AppError.HTTP_STATUS.UNAUTHORIZED,
"此服务器已开启站点密钥验证,请在请求头中添加 x-site-key 以继续访问"
);
return res.status(error.statusCode).json(error);
}
next();
};
export default checkSiteKey;
async function getOrCreateDevice(uuid, className) {
let device = await prisma.device.findUnique({
where: { uuid }
});
if (!device) {
device = await prisma.device.create({
data: {
uuid,
name: className || null,
accessType: ACCESS_TYPES.NO_PASSWORD_WRITABLE
}
});
}
return device;
}
async function validatePassword(device, password) {
if (!device.password) return true;
return device.password === password;
}
export const authMiddleware = async (req, res, next) => {
const { namespace } = req.params;
const { password } = req.body;
try {
const device = await getOrCreateDevice(namespace, req.body.className);
req.device = device;
if (device.password && !await validatePassword(device, password)) {
return res.status(401).json({ error: 'Invalid password' });
}
next();
} catch (error) {
console.error('Auth middleware error:', error);
res.status(500).json({ error: 'Internal server error' });
}
};
export const readAuthMiddleware = async (req, res, next) => {
const { namespace } = req.params;
try {
const device = await getOrCreateDevice(namespace);
req.device = device;
if (device.accessType === ACCESS_TYPES.NO_PASSWORD_UNREADABLE) {
return res.status(403).json({ error: 'Device is not readable' });
}
if (device.accessType === ACCESS_TYPES.NO_PASSWORD_READABLE) {
return next();
}
if (device.password) {
const { password } = req.body;
if (!await validatePassword(device, password)) {
return res.status(401).json({ error: 'Invalid password' });
}
}
next();
} catch (error) {
console.error('Read auth middleware error:', error);
res.status(500).json({ error: 'Internal server error' });
}
};
export const writeAuthMiddleware = async (req, res, next) => {
const { namespace } = req.params;
try {
const device = await getOrCreateDevice(namespace);
req.device = device;
if (device.password) {
const { password } = req.body;
if (!await validatePassword(device, password)) {
return res.status(401).json({ error: 'Invalid password' });
}
}
next();
} catch (error) {
console.error('Write auth middleware error:', error);
res.status(500).json({ error: 'Internal server error' });
}
};

View File

@ -21,7 +21,7 @@
"@opentelemetry/sdk-node": "^0.200.0",
"@opentelemetry/sdk-trace-base": "^2.0.0",
"@opentelemetry/semantic-conventions": "^1.33.0",
"@prisma/client": "6.7.0",
"@prisma/client": "6.8.2",
"axios": "^1.9.0",
"body-parser": "^2.2.0",
"cookie-parser": "~1.4.4",
@ -36,6 +36,6 @@
"uuid": "^11.1.0"
},
"devDependencies": {
"prisma": "6.7.0"
"prisma": "6.8.2"
}
}

371
pnpm-lock.yaml generated
View File

@ -27,8 +27,8 @@ importers:
specifier: ^1.33.0
version: 1.33.0
'@prisma/client':
specifier: 6.7.0
version: 6.7.0(prisma@6.7.0)
specifier: 6.8.2
version: 6.8.2(prisma@6.8.2)
axios:
specifier: ^1.9.0
version: 1.9.0(debug@4.4.0)
@ -67,161 +67,11 @@ importers:
version: 11.1.0
devDependencies:
prisma:
specifier: 6.7.0
version: 6.7.0
specifier: 6.8.2
version: 6.8.2
packages:
'@esbuild/aix-ppc64@0.25.0':
resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
'@esbuild/android-arm64@0.25.0':
resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
'@esbuild/android-arm@0.25.0':
resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
'@esbuild/android-x64@0.25.0':
resolution: {integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
'@esbuild/darwin-arm64@0.25.0':
resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-x64@0.25.0':
resolution: {integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
'@esbuild/freebsd-arm64@0.25.0':
resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-x64@0.25.0':
resolution: {integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
'@esbuild/linux-arm64@0.25.0':
resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm@0.25.0':
resolution: {integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
'@esbuild/linux-ia32@0.25.0':
resolution: {integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
'@esbuild/linux-loong64@0.25.0':
resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
'@esbuild/linux-mips64el@0.25.0':
resolution: {integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
'@esbuild/linux-ppc64@0.25.0':
resolution: {integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
'@esbuild/linux-riscv64@0.25.0':
resolution: {integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
'@esbuild/linux-s390x@0.25.0':
resolution: {integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
'@esbuild/linux-x64@0.25.0':
resolution: {integrity: sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==}
engines: {node: '>=18'}
cpu: [x64]
os: [linux]
'@esbuild/netbsd-arm64@0.25.0':
resolution: {integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [netbsd]
'@esbuild/netbsd-x64@0.25.0':
resolution: {integrity: sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
'@esbuild/openbsd-arm64@0.25.0':
resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openbsd]
'@esbuild/openbsd-x64@0.25.0':
resolution: {integrity: sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
'@esbuild/sunos-x64@0.25.0':
resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
'@esbuild/win32-arm64@0.25.0':
resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
'@esbuild/win32-ia32@0.25.0':
resolution: {integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
'@esbuild/win32-x64@0.25.0':
resolution: {integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
'@grpc/grpc-js@1.13.3':
resolution: {integrity: sha512-FTXHdOoPbZrBjlVLHuKbDZnsTxXv2BlHF57xw6LuThXacXvtkahEPED0CKMk6obZDf65Hv4k3z62eyPNpvinIg==}
engines: {node: '>=12.10.0'}
@ -689,8 +539,8 @@ packages:
peerDependencies:
'@opentelemetry/api': ^1.1.0
'@prisma/client@6.7.0':
resolution: {integrity: sha512-+k61zZn1XHjbZul8q6TdQLpuI/cvyfil87zqK2zpreNIXyXtpUv3+H/oM69hcsFcZXaokHJIzPAt5Z8C8eK2QA==}
'@prisma/client@6.8.2':
resolution: {integrity: sha512-5II+vbyzv4si6Yunwgkj0qT/iY0zyspttoDrL3R4BYgLdp42/d2C8xdi9vqkrYtKt9H32oFIukvyw3Koz5JoDg==}
engines: {node: '>=18.18'}
peerDependencies:
prisma: '*'
@ -701,23 +551,23 @@ packages:
typescript:
optional: true
'@prisma/config@6.7.0':
resolution: {integrity: sha512-di8QDdvSz7DLUi3OOcCHSwxRNeW7jtGRUD2+Z3SdNE3A+pPiNT8WgUJoUyOwJmUr5t+JA2W15P78C/N+8RXrOA==}
'@prisma/config@6.8.2':
resolution: {integrity: sha512-ZJY1fF4qRBPdLQ/60wxNtX+eu89c3AkYEcP7L3jkp0IPXCNphCYxikTg55kPJLDOG6P0X+QG5tCv6CmsBRZWFQ==}
'@prisma/debug@6.7.0':
resolution: {integrity: sha512-RabHn9emKoYFsv99RLxvfG2GHzWk2ZI1BuVzqYtmMSIcuGboHY5uFt3Q3boOREM9de6z5s3bQoyKeWnq8Fz22w==}
'@prisma/debug@6.8.2':
resolution: {integrity: sha512-4muBSSUwJJ9BYth5N8tqts8JtiLT8QI/RSAzEogwEfpbYGFo9mYsInsVo8dqXdPO2+Rm5OG5q0qWDDE3nyUbVg==}
'@prisma/engines-version@6.7.0-36.3cff47a7f5d65c3ea74883f1d736e41d68ce91ed':
resolution: {integrity: sha512-EvpOFEWf1KkJpDsBCrih0kg3HdHuaCnXmMn7XFPObpFTzagK1N0Q0FMnYPsEhvARfANP5Ok11QyoTIRA2hgJTA==}
'@prisma/engines-version@6.8.0-43.2060c79ba17c6bb9f5823312b6f6b7f4a845738e':
resolution: {integrity: sha512-Rkik9lMyHpFNGaLpPF3H5q5TQTkm/aE7DsGM5m92FZTvWQsvmi6Va8On3pWvqLHOt5aPUvFb/FeZTmphI4CPiQ==}
'@prisma/engines@6.7.0':
resolution: {integrity: sha512-3wDMesnOxPrOsq++e5oKV9LmIiEazFTRFZrlULDQ8fxdub5w4NgRBoxtWbvXmj2nJVCnzuz6eFix3OhIqsZ1jw==}
'@prisma/engines@6.8.2':
resolution: {integrity: sha512-XqAJ//LXjqYRQ1RRabs79KOY4+v6gZOGzbcwDQl0D6n9WBKjV7qdrbd042CwSK0v0lM9MSHsbcFnU2Yn7z8Zlw==}
'@prisma/fetch-engine@6.7.0':
resolution: {integrity: sha512-zLlAGnrkmioPKJR4Yf7NfW3hftcvqeNNEHleMZK9yX7RZSkhmxacAYyfGsCcqRt47jiZ7RKdgE0Wh2fWnm7WsQ==}
'@prisma/fetch-engine@6.8.2':
resolution: {integrity: sha512-lCvikWOgaLOfqXGacEKSNeenvj0n3qR5QvZUOmPE2e1Eh8cMYSobxonCg9rqM6FSdTfbpqp9xwhSAOYfNqSW0g==}
'@prisma/get-platform@6.7.0':
resolution: {integrity: sha512-i9IH5lO4fQwnMLvQLYNdgVh9TK3PuWBfQd7QLk/YurnAIg+VeADcZDbmhAi4XBBDD+hDif9hrKyASu0hbjwabw==}
'@prisma/get-platform@6.8.2':
resolution: {integrity: sha512-vXSxyUgX3vm1Q70QwzwkjeYfRryIvKno1SXbIqwSptKwqKzskINnDUcx85oX+ys6ooN2ATGSD0xN2UTfg6Zcow==}
'@protobufjs/aspromise@1.1.2':
resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==}
@ -962,16 +812,6 @@ packages:
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
engines: {node: '>= 0.4'}
esbuild-register@3.6.0:
resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==}
peerDependencies:
esbuild: '>=0.12 <1'
esbuild@0.25.0:
resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==}
engines: {node: '>=18'}
hasBin: true
escalade@3.2.0:
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
engines: {node: '>=6'}
@ -1027,11 +867,6 @@ packages:
resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
engines: {node: '>= 0.8'}
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
@ -1125,6 +960,10 @@ packages:
engines: {node: '>=10'}
hasBin: true
jiti@2.4.2:
resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==}
hasBin: true
json-bigint@1.0.0:
resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==}
@ -1256,8 +1095,8 @@ packages:
resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==}
engines: {node: '>=0.10.0'}
prisma@6.7.0:
resolution: {integrity: sha512-vArg+4UqnQ13CVhc2WUosemwh6hr6cr6FY2uzDvCIFwH8pu8BXVv38PktoMLVjtX7sbYThxbnZF5YiR8sN2clw==}
prisma@6.8.2:
resolution: {integrity: sha512-JNricTXQxzDtRS7lCGGOB4g5DJ91eg3nozdubXze3LpcMl1oWwcFddrj++Up3jnRE6X/3gB/xz3V+ecBk/eEGA==}
engines: {node: '>=18.18'}
hasBin: true
peerDependencies:
@ -1426,81 +1265,6 @@ packages:
snapshots:
'@esbuild/aix-ppc64@0.25.0':
optional: true
'@esbuild/android-arm64@0.25.0':
optional: true
'@esbuild/android-arm@0.25.0':
optional: true
'@esbuild/android-x64@0.25.0':
optional: true
'@esbuild/darwin-arm64@0.25.0':
optional: true
'@esbuild/darwin-x64@0.25.0':
optional: true
'@esbuild/freebsd-arm64@0.25.0':
optional: true
'@esbuild/freebsd-x64@0.25.0':
optional: true
'@esbuild/linux-arm64@0.25.0':
optional: true
'@esbuild/linux-arm@0.25.0':
optional: true
'@esbuild/linux-ia32@0.25.0':
optional: true
'@esbuild/linux-loong64@0.25.0':
optional: true
'@esbuild/linux-mips64el@0.25.0':
optional: true
'@esbuild/linux-ppc64@0.25.0':
optional: true
'@esbuild/linux-riscv64@0.25.0':
optional: true
'@esbuild/linux-s390x@0.25.0':
optional: true
'@esbuild/linux-x64@0.25.0':
optional: true
'@esbuild/netbsd-arm64@0.25.0':
optional: true
'@esbuild/netbsd-x64@0.25.0':
optional: true
'@esbuild/openbsd-arm64@0.25.0':
optional: true
'@esbuild/openbsd-x64@0.25.0':
optional: true
'@esbuild/sunos-x64@0.25.0':
optional: true
'@esbuild/win32-arm64@0.25.0':
optional: true
'@esbuild/win32-ia32@0.25.0':
optional: true
'@esbuild/win32-x64@0.25.0':
optional: true
'@grpc/grpc-js@1.13.3':
dependencies:
'@grpc/proto-loader': 0.7.15
@ -2191,37 +1955,34 @@ snapshots:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.0.0(@opentelemetry/api@1.9.0)
'@prisma/client@6.7.0(prisma@6.7.0)':
'@prisma/client@6.8.2(prisma@6.8.2)':
optionalDependencies:
prisma: 6.7.0
prisma: 6.8.2
'@prisma/config@6.7.0':
'@prisma/config@6.8.2':
dependencies:
esbuild: 0.25.0
esbuild-register: 3.6.0(esbuild@0.25.0)
transitivePeerDependencies:
- supports-color
jiti: 2.4.2
'@prisma/debug@6.7.0': {}
'@prisma/debug@6.8.2': {}
'@prisma/engines-version@6.7.0-36.3cff47a7f5d65c3ea74883f1d736e41d68ce91ed': {}
'@prisma/engines-version@6.8.0-43.2060c79ba17c6bb9f5823312b6f6b7f4a845738e': {}
'@prisma/engines@6.7.0':
'@prisma/engines@6.8.2':
dependencies:
'@prisma/debug': 6.7.0
'@prisma/engines-version': 6.7.0-36.3cff47a7f5d65c3ea74883f1d736e41d68ce91ed
'@prisma/fetch-engine': 6.7.0
'@prisma/get-platform': 6.7.0
'@prisma/debug': 6.8.2
'@prisma/engines-version': 6.8.0-43.2060c79ba17c6bb9f5823312b6f6b7f4a845738e
'@prisma/fetch-engine': 6.8.2
'@prisma/get-platform': 6.8.2
'@prisma/fetch-engine@6.7.0':
'@prisma/fetch-engine@6.8.2':
dependencies:
'@prisma/debug': 6.7.0
'@prisma/engines-version': 6.7.0-36.3cff47a7f5d65c3ea74883f1d736e41d68ce91ed
'@prisma/get-platform': 6.7.0
'@prisma/debug': 6.8.2
'@prisma/engines-version': 6.8.0-43.2060c79ba17c6bb9f5823312b6f6b7f4a845738e
'@prisma/get-platform': 6.8.2
'@prisma/get-platform@6.7.0':
'@prisma/get-platform@6.8.2':
dependencies:
'@prisma/debug': 6.7.0
'@prisma/debug': 6.8.2
'@protobufjs/aspromise@1.1.2': {}
@ -2450,41 +2211,6 @@ snapshots:
has-tostringtag: 1.0.2
hasown: 2.0.2
esbuild-register@3.6.0(esbuild@0.25.0):
dependencies:
debug: 4.4.0
esbuild: 0.25.0
transitivePeerDependencies:
- supports-color
esbuild@0.25.0:
optionalDependencies:
'@esbuild/aix-ppc64': 0.25.0
'@esbuild/android-arm': 0.25.0
'@esbuild/android-arm64': 0.25.0
'@esbuild/android-x64': 0.25.0
'@esbuild/darwin-arm64': 0.25.0
'@esbuild/darwin-x64': 0.25.0
'@esbuild/freebsd-arm64': 0.25.0
'@esbuild/freebsd-x64': 0.25.0
'@esbuild/linux-arm': 0.25.0
'@esbuild/linux-arm64': 0.25.0
'@esbuild/linux-ia32': 0.25.0
'@esbuild/linux-loong64': 0.25.0
'@esbuild/linux-mips64el': 0.25.0
'@esbuild/linux-ppc64': 0.25.0
'@esbuild/linux-riscv64': 0.25.0
'@esbuild/linux-s390x': 0.25.0
'@esbuild/linux-x64': 0.25.0
'@esbuild/netbsd-arm64': 0.25.0
'@esbuild/netbsd-x64': 0.25.0
'@esbuild/openbsd-arm64': 0.25.0
'@esbuild/openbsd-x64': 0.25.0
'@esbuild/sunos-x64': 0.25.0
'@esbuild/win32-arm64': 0.25.0
'@esbuild/win32-ia32': 0.25.0
'@esbuild/win32-x64': 0.25.0
escalade@3.2.0: {}
escape-html@1.0.3: {}
@ -2561,9 +2287,6 @@ snapshots:
fresh@2.0.0: {}
fsevents@2.3.3:
optional: true
function-bind@1.1.2: {}
gaxios@6.7.1:
@ -2671,6 +2394,8 @@ snapshots:
filelist: 1.0.4
minimatch: 3.1.2
jiti@2.4.2: {}
json-bigint@1.0.0:
dependencies:
bignumber.js: 9.3.0
@ -2773,14 +2498,10 @@ snapshots:
dependencies:
xtend: 4.0.2
prisma@6.7.0:
prisma@6.8.2:
dependencies:
'@prisma/config': 6.7.0
'@prisma/engines': 6.7.0
optionalDependencies:
fsevents: 2.3.3
transitivePeerDependencies:
- supports-color
'@prisma/config': 6.8.2
'@prisma/engines': 6.8.2
protobufjs@7.5.1:
dependencies:

View File

@ -17,3 +17,12 @@ model KVStore {
@@id([namespace, key])
}
model Device {
uuid String @id @db.Char(36)
password String?
name String?
accessType String @default("NO_PASSWORD_WRITABLE")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}

View File

@ -1,9 +1,13 @@
import { Router } from "express";
const router = Router();
import kvStore from "../models/kvStore.js";
import checkSiteKey from "../middleware/auth.js";
import { checkSiteKey } from "../middleware/auth.js";
import { v4 as uuidv4 } from "uuid";
import errors from "../utils/errors.js";
import { PrismaClient } from "@prisma/client";
import { readAuthMiddleware, writeAuthMiddleware } from "../middleware/auth.js";
const prisma = new PrismaClient();
// 检查是否为受限UUID的中间件
const checkRestrictedUUID = (req, res, next) => {
@ -17,6 +21,83 @@ const checkRestrictedUUID = (req, res, next) => {
};
router.use(checkSiteKey);
// Get device info
router.get(
"/:namespace/_info",
readAuthMiddleware,
errors.catchAsync(async (req, res) => {
try {
const { device } = req;
res.json({
uuid: device.uuid,
name: device.name,
accessType: device.accessType,
hasPassword: !!device.password,
});
} catch (error) {
console.error("Error getting device info:", error);
res.status(500).json({ error: "Internal server error" });
}
})
);
// Update device password
router.post(
"/:namespace/_password",
writeAuthMiddleware,
errors.catchAsync(async (req, res) => {
const { newPassword, oldPassword } = req.body;
const { device } = req;
try {
if (device.password && oldPassword !== device.password) {
return res.status(401).json({ error: "Invalid old password" });
}
await prisma.device.update({
where: { uuid: device.uuid },
data: { password: newPassword },
});
res.json({ message: "Password updated successfully" });
} catch (error) {
console.error("Error updating password:", error);
res.status(500).json({ error: "Internal server error" });
}
})
);
// Update device info
router.put(
"/:namespace/_info",
writeAuthMiddleware,
errors.catchAsync(async (req, res) => {
const { name, accessType } = req.body;
const { device } = req;
try {
const updatedDevice = await prisma.device.update({
where: { uuid: device.uuid },
data: {
name: name || device.name,
accessType: accessType || device.accessType,
},
});
res.json({
uuid: updatedDevice.uuid,
name: updatedDevice.name,
accessType: updatedDevice.accessType,
hasPassword: !!updatedDevice.password,
});
} catch (error) {
console.error("Error updating device info:", error);
res.status(500).json({ error: "Internal server error" });
}
})
);
/**
* GET /:namespace
* 获取指定命名空间下的所有键名及元数据列表
@ -72,6 +153,7 @@ router.get(
router.get(
"/:namespace/:key",
checkRestrictedUUID,
readAuthMiddleware,
errors.catchAsync(async (req, res, next) => {
const { namespace, key } = req.params;
@ -94,6 +176,7 @@ router.get(
router.get(
"/:namespace/:key/metadata",
checkRestrictedUUID,
readAuthMiddleware,
errors.catchAsync(async (req, res, next) => {
const { namespace, key } = req.params;
const metadata = await kvStore.getMetadata(namespace, key);
@ -115,6 +198,7 @@ router.get(
router.post(
"/:namespace/:key",
checkRestrictedUUID,
writeAuthMiddleware,
errors.catchAsync(async (req, res, next) => {
const { namespace, key } = req.params;
const value = req.body;
@ -147,14 +231,19 @@ router.post(
* 批量导入键值对到指定命名空间
*/
router.post(
"/:namespace/import/batch-import",
"/:namespace/_batchimport",
checkRestrictedUUID,
errors.catchAsync(async (req, res, next) => {
const { namespace } = req.params;
const data = req.body;
if (!data || Object.keys(data).length === 0) {
return next(errors.createError(400, "请提供有效的JSON数据格式为 {\"key\":{}, \"key2\":{}}"));
return next(
errors.createError(
400,
'请提供有效的JSON数据格式为 {"key":{}, "key2":{}}'
)
);
}
// 获取客户端IP
@ -174,12 +263,12 @@ router.post(
const result = await kvStore.upsert(namespace, key, value, creatorIp);
results.push({
key: result.key,
created: result.createdAt.getTime() === result.updatedAt.getTime()
created: result.createdAt.getTime() === result.updatedAt.getTime(),
});
} catch (error) {
errors.push({
key,
error: error.message
error: error.message,
});
}
}
@ -190,7 +279,7 @@ router.post(
successful: results.length,
failed: errors.length,
results,
errors: errors.length > 0 ? errors : undefined
errors: errors.length > 0 ? errors : undefined,
});
})
);
@ -253,5 +342,4 @@ router.get(
})
);
export default router;