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:
parent
69887f9c47
commit
3bff254a22
@ -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' });
|
||||
}
|
||||
};
|
@ -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
371
pnpm-lock.yaml
generated
@ -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:
|
||||
|
@ -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
|
||||
}
|
||||
|
102
routes/kv.js
102
routes/kv.js
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user