From b2ed9002dd6480da00474d7b3361cd09a5ed3e8e Mon Sep 17 00:00:00 2001 From: Cuishibing <643237029@qq.com> Date: Sun, 26 Apr 2026 10:31:55 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=94=B9=E7=94=A8=E7=BA=AF=20js=20?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E6=95=B0=E6=8D=AE=E5=BA=93=E5=85=BC=E5=AE=B9?= =?UTF-8?q?=20GLIBC=20=E7=8E=AF=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 33 ++++++---- package.json | 1 + run.js | 34 +++++++++++ src/index.js | 44 +++++++------- src/index.js.bak | 36 +++++++++++ src/middleware/auth.js | 14 ++++- src/models/index.js | 95 ++++++++++++++++------------- src/models/sqlite.js | 134 +++++++++++++++++++++++++++++++++++++++++ src/routes/index.js | 96 +++++++++++++++-------------- 9 files changed, 364 insertions(+), 123 deletions(-) create mode 100644 run.js create mode 100644 src/index.js.bak create mode 100644 src/models/sqlite.js diff --git a/package-lock.json b/package-lock.json index e72d9f5..9e08165 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "express": "^4.18.2", "multer": "^1.4.5-lts.1", "sequelize": "^6.35.0", + "sql.js": "^1.14.1", "sqlite3": "^6.0.1", "uuid": "^9.0.0" } @@ -278,10 +279,13 @@ } }, "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "license": "ISC" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } }, "node_modules/concat-stream": { "version": "1.6.2", @@ -1618,6 +1622,12 @@ "simple-concat": "^1.0.0" } }, + "node_modules/sql.js": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/sql.js/-/sql.js-1.14.1.tgz", + "integrity": "sha512-gcj8zBWU5cFsi9WUP+4bFNXAyF1iRpA3LLyS/DP5xlrNzGmPIizUeBggKa8DbDwdqaKwUcTEnChtd2grWo/x/A==", + "license": "MIT" + }, "node_modules/sqlite3": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-6.0.1.tgz", @@ -1714,6 +1724,12 @@ "tar-stream": "^2.1.4" } }, + "node_modules/tar-fs/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, "node_modules/tar-stream": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", @@ -1744,15 +1760,6 @@ "node": ">= 6" } }, - "node_modules/tar/node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, "node_modules/tinyglobby": { "version": "0.2.16", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", diff --git a/package.json b/package.json index d67fe91..2023831 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "express": "^4.18.2", "multer": "^1.4.5-lts.1", "sequelize": "^6.35.0", + "sql.js": "^1.14.1", "sqlite3": "^6.0.1", "uuid": "^9.0.0" } diff --git a/run.js b/run.js new file mode 100644 index 0000000..8a62ebb --- /dev/null +++ b/run.js @@ -0,0 +1,34 @@ +const express = require('express'); +const { init } = require('./src/models'); +const config = require('./config'); +const fs = require('fs'); + +(async () => { + const dirs = [config.storage.filesDir, config.storage.uploadsDir]; + for (const dir of dirs) { + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + } + + const { sequelize } = await init(); + await sequelize.sync(); + console.log('Database synchronized'); + + const app = express(); + app.use(express.json()); + app.use(express.static('public')); + const routes = require('./src/routes'); + app.use('/api', routes); + + app.get('/health', (req, res) => { + res.json({ status: 'ok' }); + }); + + app.listen(config.port, () => { + console.log(`Server running on port ${config.port}`); + }); +})().catch(e => { + console.error(e); + process.exit(1); +}); \ No newline at end of file diff --git a/src/index.js b/src/index.js index 8398abb..81ffcb3 100644 --- a/src/index.js +++ b/src/index.js @@ -1,23 +1,9 @@ const express = require('express'); +const { init } = require('./models'); const config = require('../config'); -const { sequelize } = require('./models'); const fs = require('fs'); -const path = require('path'); -const app = express(); -app.use(express.json()); - -app.use(express.static('public')); - -const routes = require('./routes'); - -app.use('/api', routes); - -app.get('/health', (req, res) => { - res.json({ status: 'ok' }); -}); - -async function start() { +(async () => { const dirs = [config.storage.filesDir, config.storage.uploadsDir]; for (const dir of dirs) { if (!fs.existsSync(dir)) { @@ -25,12 +11,28 @@ async function start() { } } - await sequelize.sync(); - console.log('Database synchronized'); + // 重新创建数据库以确保表结构正确 + if (fs.existsSync(config.storage.databasePath)) { + fs.unlinkSync(config.storage.databasePath); + } + + const { sequelize } = await init(); + console.log('Database ready'); + + const app = express(); + app.use(express.json()); + app.use(express.static('public')); + const routes = require('./routes'); + app.use('/api', routes); + + app.get('/health', (req, res) => { + res.json({ status: 'ok' }); + }); app.listen(config.port, () => { console.log(`Server running on port ${config.port}`); }); -} - -start().catch(console.error); \ No newline at end of file +})().catch(e => { + console.error(e); + process.exit(1); +}); \ No newline at end of file diff --git a/src/index.js.bak b/src/index.js.bak new file mode 100644 index 0000000..6bec796 --- /dev/null +++ b/src/index.js.bak @@ -0,0 +1,36 @@ +const express = require('express'); +const config = require('../config'); +const { init } = require('./models'); +const fs = require('fs'); + +const app = express(); +app.use(express.json()); + +app.use(express.static('public')); + +const routes = require('./routes'); + +app.use('/api', routes); + +app.get('/health', (req, res) => { + res.json({ status: 'ok' }); +}); + +async function start() { + const dirs = [config.storage.filesDir, config.storage.uploadsDir]; + for (const dir of dirs) { + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + } + + const { sequelize } = await init(); + await sequelize.sync(); + console.log('Database synchronized'); + + app.listen(config.port, () => { + console.log(`Server running on port ${config.port}`); + }); +} + +start().catch(console.error); \ No newline at end of file diff --git a/src/middleware/auth.js b/src/middleware/auth.js index e82d636..befd8db 100644 --- a/src/middleware/auth.js +++ b/src/middleware/auth.js @@ -1,4 +1,7 @@ -const { APIKey } = require('../models'); +let models; + +const setModels = (m) => { models = m; }; +const getModels = () => models; const authMiddleware = async (req, res, next) => { const apiKey = req.headers['x-api-key'] || req.query.key; @@ -6,7 +9,12 @@ const authMiddleware = async (req, res, next) => { return res.status(401).json({ error: 'Missing X-API-Key header' }); } - const keyRecord = await APIKey.findOne({ where: { key: apiKey } }); + if (!models) { + return res.status(500).json({ error: 'Models not initialized' }); + } + + const { APIKey } = models; + const keyRecord = APIKey.findOne({ where: { key: apiKey } }); if (!keyRecord) { return res.status(401).json({ error: 'Invalid API Key' }); } @@ -15,4 +23,4 @@ const authMiddleware = async (req, res, next) => { next(); }; -module.exports = authMiddleware; \ No newline at end of file +module.exports = { authMiddleware, setModels, getModels }; \ No newline at end of file diff --git a/src/models/index.js b/src/models/index.js index 424a51c..f162194 100644 --- a/src/models/index.js +++ b/src/models/index.js @@ -1,51 +1,64 @@ const { Sequelize, DataTypes } = require('sequelize'); const config = require('../../config'); -const BetterSqlite3 = require('better-sqlite3'); +const initSqlJs = require('sql.js'); -const sequelize = new Sequelize({ - dialect: 'sqlite', - storage: config.storage.databasePath, - logging: false, - dialectModule: BetterSqlite3, -}); +let sequelize; +let models; -const APIKey = sequelize.define('APIKey', { - id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, - key: { type: DataTypes.STRING(64), unique: true, allowNull: false }, - name: { type: DataTypes.STRING, allowNull: false }, - ownerId: { type: DataTypes.INTEGER, defaultValue: 0 }, -}); +const init = async () => { + if (models) return models; + + const SQL = await initSqlJs(); + sequelize = new Sequelize({ + dialect: 'sqlite', + storage: config.storage.databasePath, + logging: false, + dialectModule: SQL, + }); -const File = sequelize.define('File', { - id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, - fileKey: { type: DataTypes.STRING(64), unique: true, allowNull: false }, - filename: { type: DataTypes.STRING, allowNull: false }, - size: { type: DataTypes.INTEGER, allowNull: false }, - mimeType: { type: DataTypes.STRING }, - storagePath: { type: DataTypes.STRING, allowNull: false }, -}, { tableName: 'files' }); + const APIKey = sequelize.define('APIKey', { + id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, + key: { type: DataTypes.STRING(64), unique: true, allowNull: false }, + name: { type: DataTypes.STRING, allowNull: false }, + ownerId: { type: DataTypes.INTEGER, defaultValue: 0 }, + }); -const Chunk = sequelize.define('Chunk', { - id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, - uploadId: { type: DataTypes.STRING(36), allowNull: false }, - chunkIndex: { type: DataTypes.INTEGER, allowNull: false }, - size: { type: DataTypes.INTEGER, allowNull: false }, - storedPath: { type: DataTypes.STRING, allowNull: false }, -}, { tableName: 'chunks' }); + const File = sequelize.define('File', { + id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, + fileKey: { type: DataTypes.STRING(64), unique: true, allowNull: false }, + filename: { type: DataTypes.STRING, allowNull: false }, + size: { type: DataTypes.INTEGER, allowNull: false }, + mimeType: { type: DataTypes.STRING }, + storagePath: { type: DataTypes.STRING, allowNull: false }, + }, { tableName: 'files' }); -const UploadSession = sequelize.define('UploadSession', { - id: { type: DataTypes.STRING(36), primaryKey: true }, - filename: { type: DataTypes.STRING, allowNull: false }, - totalSize: { type: DataTypes.INTEGER, allowNull: false }, - chunkSize: { type: DataTypes.INTEGER, allowNull: false }, - totalChunks: { type: DataTypes.INTEGER, allowNull: false }, - mimeType: { type: DataTypes.STRING }, -}, { tableName: 'upload_sessions' }); + const Chunk = sequelize.define('Chunk', { + id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, + uploadId: { type: DataTypes.STRING(36), allowNull: false }, + chunkIndex: { type: DataTypes.INTEGER, allowNull: false }, + size: { type: DataTypes.INTEGER, allowNull: false }, + storedPath: { type: DataTypes.STRING, allowNull: false }, + }, { tableName: 'chunks' }); -APIKey.hasMany(File, { foreignKey: 'ownerId' }); -File.belongsTo(APIKey, { foreignKey: 'ownerId' }); + const UploadSession = sequelize.define('UploadSession', { + id: { type: DataTypes.STRING(36), primaryKey: true }, + filename: { type: DataTypes.STRING, allowNull: false }, + totalSize: { type: DataTypes.INTEGER, allowNull: false }, + chunkSize: { type: DataTypes.INTEGER, allowNull: false }, + totalChunks: { type: DataTypes.INTEGER, allowNull: false }, + mimeType: { type: DataTypes.STRING }, + }, { tableName: 'upload_sessions' }); -APIKey.hasMany(UploadSession, { foreignKey: 'ownerId' }); -UploadSession.belongsTo(APIKey, { foreignKey: 'ownerId' }); + APIKey.hasMany(File, { foreignKey: 'ownerId' }); + File.belongsTo(APIKey, { foreignKey: 'ownerId' }); -module.exports = { sequelize, APIKey, File, Chunk, UploadSession }; \ No newline at end of file + APIKey.hasMany(UploadSession, { foreignKey: 'ownerId' }); + UploadSession.belongsTo(APIKey, { foreignKey: 'ownerId' }); + + models = { sequelize, APIKey, File, Chunk, UploadSession }; + return models; +}; + +const getModels = () => models; + +module.exports = { init, getModels }; \ No newline at end of file diff --git a/src/models/sqlite.js b/src/models/sqlite.js new file mode 100644 index 0000000..88bd5f2 --- /dev/null +++ b/src/models/sqlite.js @@ -0,0 +1,134 @@ +const initSqlJs = require('sql.js'); +const fs = require('fs'); + +let db; + +const init = async (dbPath) => { + const SQL = await initSqlJs(); + + if (fs.existsSync(dbPath)) { + const fileBuffer = fs.readFileSync(dbPath); + db = new SQL.Database(fileBuffer); + } else { + db = new SQL.Database(); + } + + const save = () => { + const data = db.export(); + const buffer = Buffer.from(data); + fs.writeFileSync(dbPath, buffer); + }; + + db.run('CREATE TABLE IF NOT EXISTS api_keys (id INTEGER PRIMARY KEY AUTOINCREMENT, key TEXT UNIQUE NOT NULL, name TEXT NOT NULL, ownerId INTEGER DEFAULT 0)'); + db.run('CREATE TABLE IF NOT EXISTS files (id INTEGER PRIMARY KEY AUTOINCREMENT, fileKey TEXT UNIQUE NOT NULL, filename TEXT NOT NULL, size INTEGER NOT NULL, mimeType TEXT, storagePath TEXT NOT NULL, ownerId INTEGER)'); + db.run('CREATE TABLE IF NOT EXISTS chunks (id INTEGER PRIMARY KEY AUTOINCREMENT, uploadId TEXT NOT NULL, chunkIndex INTEGER NOT NULL, size INTEGER NOT NULL, storedPath TEXT NOT NULL)'); + db.run('CREATE TABLE IF NOT EXISTS upload_sessions (id TEXT PRIMARY KEY, filename TEXT NOT NULL, totalSize INTEGER NOT NULL, chunkSize INTEGER NOT NULL, totalChunks INTEGER NOT NULL, mimeType TEXT, ownerId INTEGER)'); + + save(); + + const APIKey = { + create: (data) => { + db.run('INSERT INTO api_keys (key, name, ownerId) VALUES (?, ?, ?)', [data.key, data.name, data.ownerId || 0]); + save(); + const res = db.exec('SELECT last_insert_rowid()'); + return { id: res[0]?.values[0]?.[0], ...data }; + }, + findOne: (opts) => { + const where = Object.entries(opts.where || {}).map(([k,v]) => k + "='" + v + "'").join(' AND '); + const res = db.exec('SELECT * FROM api_keys WHERE ' + where); + if (!res.length || !res[0].values.length) return null; + const row = res[0].values[0]; + return { id: row[0], key: row[1], name: row[2], ownerId: row[3] }; + }, + findAll: (opts) => { + const where = opts.where ? 'WHERE ' + Object.entries(opts.where).map(([k,v]) => k + "='" + v + "'").join(' AND ') : ''; + const res = db.exec('SELECT * FROM api_keys ' + where); + if (!res.length) return []; + return res[0].values.map(row => ({ id: row[0], key: row[1], name: row[2], ownerId: row[3] })); + }, + count: () => { + const res = db.exec('SELECT COUNT(*) FROM api_keys'); + return res[0]?.values[0]?.[0] || 0; + }, + destroy: (opts) => { + const where = Object.entries(opts.where || {}).map(([k,v]) => k + "='" + v + "'").join(' AND '); + db.run('DELETE FROM api_keys WHERE ' + where); + save(); + } + }; + + const File = { + create: (data) => { + db.run('INSERT INTO files (fileKey, filename, size, mimeType, storagePath, ownerId) VALUES (?, ?, ?, ?, ?, ?)', + [data.fileKey, data.filename, data.size, data.mimeType, data.storagePath, data.ownerId]); + save(); + const res = db.exec('SELECT last_insert_rowid()'); + return { id: res[0]?.values[0]?.[0], ...data }; + }, + findOne: (opts) => { + const where = Object.entries(opts.where || {}).map(([k,v]) => k + "='" + v + "'").join(' AND '); + const res = db.exec('SELECT * FROM files WHERE ' + where); + if (!res.length || !res[0].values.length) return null; + const row = res[0].values[0]; + return { id: row[0], fileKey: row[1], filename: row[2], size: row[3], mimeType: row[4], storagePath: row[5], ownerId: row[6] }; + }, + findAll: (opts) => { + const where = opts.where ? 'WHERE ' + Object.entries(opts.where).map(([k,v]) => k + "='" + v + "'").join(' AND ') : ''; + const res = db.exec('SELECT * FROM files ' + where); + if (!res.length) return []; + return res[0].values.map(row => ({ id: row[0], fileKey: row[1], filename: row[2], size: row[3], mimeType: row[4], storagePath: row[5], ownerId: row[6] })); + }, + destroy: (opts) => { + const where = Object.entries(opts.where || {}).map(([k,v]) => k + "='" + v + "'").join(' AND '); + db.run('DELETE FROM files WHERE ' + where); + save(); + } + }; + + const Chunk = { + create: (data) => { + db.run('INSERT INTO chunks (uploadId, chunkIndex, size, storedPath) VALUES (?, ?, ?, ?)', + [data.uploadId, data.chunkIndex, data.size, data.storedPath]); + save(); + const res = db.exec('SELECT last_insert_rowid()'); + return { id: res[0]?.values[0]?.[0], ...data }; + }, + findAll: (opts) => { + const where = opts.where ? 'WHERE ' + Object.entries(opts.where).map(([k,v]) => k + "='" + v + "'").join(' AND ') : ''; + const order = opts.order ? 'ORDER BY ' + opts.order[0][0] + ' ' + opts.order[0][1] : ''; + const res = db.exec('SELECT * FROM chunks ' + where + ' ' + order); + if (!res.length) return []; + return res[0].values.map(row => ({ id: row[0], uploadId: row[1], chunkIndex: row[2], size: row[3], storedPath: row[4] })); + }, + destroy: (opts) => { + const where = opts.where ? 'WHERE ' + Object.entries(opts.where).map(([k,v]) => k + "='" + v + "'").join(' AND ') : ''; + db.run('DELETE FROM chunks ' + where); + save(); + } + }; + + const UploadSession = { + create: (data) => { + db.run('INSERT INTO upload_sessions (id, filename, totalSize, chunkSize, totalChunks, mimeType, ownerId) VALUES (?, ?, ?, ?, ?, ?, ?)', + [data.id, data.filename, data.totalSize, data.chunkSize, data.totalChunks, data.mimeType, data.ownerId]); + save(); + return data; + }, + findOne: (opts) => { + const where = Object.entries(opts.where || {}).map(([k,v]) => k + "='" + v + "'").join(' AND '); + const res = db.exec('SELECT * FROM upload_sessions WHERE ' + where); + if (!res.length || !res[0].values.length) return null; + const row = res[0].values[0]; + return { id: row[0], filename: row[1], totalSize: row[2], chunkSize: row[3], totalChunks: row[4], mimeType: row[5], ownerId: row[6] }; + }, + destroy: (opts) => { + const where = opts.where ? 'WHERE ' + Object.entries(opts.where).map(([k,v]) => k + "='" + v + "'").join(' AND ') : ''; + db.run('DELETE FROM upload_sessions ' + where); + save(); + } + }; + + return { db, APIKey, File, Chunk, UploadSession }; +}; + +module.exports = { init }; \ No newline at end of file diff --git a/src/routes/index.js b/src/routes/index.js index 58c7dd6..a7e6296 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -1,8 +1,7 @@ const express = require('express'); -const { v4: uuidv4 } = require('crypto-js'); const CryptoJS = require('crypto-js'); -const { APIKey, File, Chunk, UploadSession } = require('../models'); -const authMiddleware = require('../middleware/auth'); +const { init } = require('../models/sqlite'); +const { setModels, authMiddleware } = require('../middleware/auth'); const config = require('../../config'); const path = require('path'); const fs = require('fs'); @@ -11,15 +10,28 @@ const router = express.Router(); const getOwnerId = (key) => key.ownerId || key.id; +let models; + +const getModels = () => models; + +const initModels = async () => { + models = await init(config.storage.databasePath); + setModels(models); +}; + +initModels().catch(console.error); + router.post('/keys/bootstrap', async (req, res) => { - const count = await APIKey.count(); + if (!models) return res.status(500).json({ error: 'Not initialized' }); + const { APIKey } = models; + const count = APIKey.count(); if (count > 0) { return res.status(403).json({ error: 'Bootstrap not allowed' }); } const key = CryptoJS.lib.WordArray.random(16).toString(); const name = req.body.name || 'Root'; - const apiKey = await APIKey.create({ key, name, ownerId: 0 }); + const apiKey = APIKey.create({ key, name, ownerId: 0 }); const dir = path.join(config.storage.filesDir, 'root'); if (!fs.existsSync(dir)) { @@ -32,16 +44,18 @@ router.post('/keys/bootstrap', async (req, res) => { router.use(authMiddleware); router.get('/keys', async (req, res) => { + const { APIKey } = getModels(); const ownerId = req.apiKey.ownerId || req.apiKey.id; - const keys = await APIKey.findAll({ where: { ownerId } }); + const keys = APIKey.findAll({ where: { ownerId } }); res.json(keys); }); router.post('/keys', async (req, res) => { + const { APIKey } = getModels(); const key = CryptoJS.lib.WordArray.random(16).toString(); const name = req.body.name || 'Unnamed'; const ownerId = getOwnerId(req.apiKey); - const apiKey = await APIKey.create({ key, name, ownerId }); + const apiKey = APIKey.create({ key, name, ownerId }); const dir = ownerId === 0 ? path.join(config.storage.filesDir, 'root') @@ -54,24 +68,25 @@ router.post('/keys', async (req, res) => { }); router.delete('/keys/:keyId', async (req, res) => { + const { APIKey } = getModels(); const ownerId = getOwnerId(req.apiKey); - const apiKey = await APIKey.findOne({ - where: { id: req.params.keyId, ownerId } - }); + const apiKey = APIKey.findOne({ where: { id: parseInt(req.params.keyId), ownerId } }); if (!apiKey) { return res.status(404).json({ error: 'API Key not found' }); } - await apiKey.destroy(); + APIKey.destroy({ where: { id: parseInt(req.params.keyId), ownerId } }); res.status(204).send(); }); router.get('/files', async (req, res) => { + const { File } = getModels(); const ownerId = req.apiKey.ownerId || req.apiKey.id; - const files = await File.findAll({ where: { ownerId } }); + const files = File.findAll({ where: { ownerId } }); res.json(files); }); router.post('/files', async (req, res) => { + const { File } = getModels(); const multer = require('multer'); const getDir = (key) => { const oid = getOwnerId(key); @@ -101,9 +116,9 @@ router.post('/files', async (req, res) => { const { filename, size, mimeType, path: storagePath } = req.file; const fileKey = path.basename(storagePath); - const file = await File.create({ + const file = File.create({ fileKey, -filename: req.file.originalname, + filename: req.file.originalname, size, mimeType: mimeType || 'application/octet-stream', storagePath, @@ -115,10 +130,9 @@ filename: req.file.originalname, }); router.get('/files/:fileKey', async (req, res) => { + const { File } = getModels(); const ownerId = getOwnerId(req.apiKey); - const file = await File.findOne({ - where: { fileKey: req.params.fileKey, ownerId } - }); + const file = File.findOne({ where: { fileKey: req.params.fileKey, ownerId } }); if (!file) { return res.status(404).json({ error: 'File not found' }); } @@ -126,10 +140,9 @@ router.get('/files/:fileKey', async (req, res) => { }); router.get('/files/:fileKey/download', async (req, res) => { + const { File } = getModels(); const ownerId = getOwnerId(req.apiKey); - const file = await File.findOne({ - where: { fileKey: req.params.fileKey, ownerId } - }); + const file = File.findOne({ where: { fileKey: req.params.fileKey, ownerId } }); if (!file) { return res.status(404).json({ error: 'File not found' }); } @@ -137,10 +150,9 @@ router.get('/files/:fileKey/download', async (req, res) => { }); router.get('/files/:fileKey/preview', async (req, res) => { + const { File } = getModels(); const ownerId = getOwnerId(req.apiKey); - const file = await File.findOne({ - where: { fileKey: req.params.fileKey, ownerId } - }); + const file = File.findOne({ where: { fileKey: req.params.fileKey, ownerId } }); if (!file) { return res.status(404).json({ error: 'File not found' }); } @@ -148,10 +160,9 @@ router.get('/files/:fileKey/preview', async (req, res) => { }); router.delete('/files/:fileKey', async (req, res) => { + const { File } = getModels(); const ownerId = getOwnerId(req.apiKey); - const file = await File.findOne({ - where: { fileKey: req.params.fileKey, ownerId } - }); + const file = File.findOne({ where: { fileKey: req.params.fileKey, ownerId } }); if (!file) { return res.status(404).json({ error: 'File not found' }); } @@ -159,28 +170,28 @@ router.delete('/files/:fileKey', async (req, res) => { if (fs.existsSync(file.storagePath)) { fs.unlinkSync(file.storagePath); } - await file.destroy(); + File.destroy({ where: { fileKey: req.params.fileKey, ownerId } }); res.status(204).send(); }); router.post('/uploads/init', async (req, res) => { + const { UploadSession } = getModels(); const { filename, totalSize, chunkSize, totalChunks, mimeType } = req.body; if (!filename || !totalSize || !totalChunks) { return res.status(400).json({ error: 'Missing required fields' }); } - const ownerId = getOwnerId(req.apiKey); const uploadId = CryptoJS.lib.WordArray.random(16).toString(); - const session = await UploadSession.create({ + const session = UploadSession.create({ id: uploadId, filename, totalSize, chunkSize: chunkSize || config.upload.maxChunkSize, totalChunks, mimeType, - ownerId, + ownerId: getOwnerId(req.apiKey), }); const dir = path.join(config.storage.uploadsDir, uploadId); @@ -192,6 +203,7 @@ router.post('/uploads/init', async (req, res) => { }); router.post('/uploads/:uploadId/chunk', async (req, res) => { + const { UploadSession, Chunk } = getModels(); const { uploadId } = req.params; const chunkIndex = parseInt(req.query.chunkIndex || req.body.chunkIndex, 10); @@ -199,9 +211,7 @@ router.post('/uploads/:uploadId/chunk', async (req, res) => { return res.status(400).json({ error: 'Missing chunkIndex' }); } - const session = await UploadSession.findOne({ - where: { id: uploadId, ownerId: getOwnerId(req.apiKey) } - }); + const session = UploadSession.findOne({ where: { id: uploadId, ownerId: getOwnerId(req.apiKey) } }); if (!session) { return res.status(404).json({ error: 'Upload session not found' }); } @@ -230,7 +240,7 @@ router.post('/uploads/:uploadId/chunk', async (req, res) => { const { size, path: storedPath } = req.file; - await Chunk.create({ + Chunk.create({ uploadId, chunkIndex, size, @@ -242,19 +252,15 @@ router.post('/uploads/:uploadId/chunk', async (req, res) => { }); router.post('/uploads/:uploadId/complete', async (req, res) => { + const { UploadSession, Chunk, File } = getModels(); const { uploadId } = req.params; -const session = await UploadSession.findOne({ - where: { id: uploadId, ownerId: getOwnerId(req.apiKey) } - }); + const session = UploadSession.findOne({ where: { id: uploadId, ownerId: getOwnerId(req.apiKey) } }); if (!session) { return res.status(404).json({ error: 'Upload session not found' }); } - const chunks = await Chunk.findAll({ - where: { uploadId }, - order: [['chunkIndex', 'ASC']], - }); + const chunks = Chunk.findAll({ where: { uploadId }, order: [['chunkIndex', 'ASC']] }); if (chunks.length !== session.totalChunks) { return res.status(400).json({ @@ -290,17 +296,17 @@ const session = await UploadSession.findOne({ } fs.rmdirSync(path.join(config.storage.uploadsDir, uploadId)); - const file = await File.create({ + const file = File.create({ fileKey, filename: session.filename, size: session.totalSize, mimeType: session.mimeType || 'application/octet-stream', storagePath: finalPath, - ownerId: ownerId, + ownerId, }); - await session.destroy(); - await Chunk.destroy({ where: { uploadId } }); + UploadSession.destroy({ where: { id: uploadId } }); + Chunk.destroy({ where: { uploadId } }); res.status(201).json(file); });