Spaces:
Sleeping
Sleeping
File size: 4,696 Bytes
07af8f3 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
import { Request, Response } from 'express';
import multer from 'multer';
import path from 'path';
import fs from 'fs';
import AdmZip from 'adm-zip';
import { ApiResponse } from '../types/index.js';
// Configure multer for file uploads
const storage = multer.diskStorage({
destination: (req, file, cb) => {
const uploadDir = path.join(process.cwd(), 'data/uploads/dxt');
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true });
}
cb(null, uploadDir);
},
filename: (req, file, cb) => {
const timestamp = Date.now();
const originalName = path.parse(file.originalname).name;
cb(null, `${originalName}-${timestamp}.dxt`);
},
});
const upload = multer({
storage,
fileFilter: (req, file, cb) => {
if (file.originalname.endsWith('.dxt')) {
cb(null, true);
} else {
cb(new Error('Only .dxt files are allowed'));
}
},
limits: {
fileSize: 500 * 1024 * 1024, // 500MB limit
},
});
export const uploadMiddleware = upload.single('dxtFile');
// Clean up old DXT server files when installing a new version
const cleanupOldDxtServer = (serverName: string): void => {
try {
const uploadDir = path.join(process.cwd(), 'data/uploads/dxt');
const serverPattern = `server-${serverName}`;
if (fs.existsSync(uploadDir)) {
const files = fs.readdirSync(uploadDir);
files.forEach((file) => {
if (file.startsWith(serverPattern)) {
const filePath = path.join(uploadDir, file);
if (fs.statSync(filePath).isDirectory()) {
fs.rmSync(filePath, { recursive: true, force: true });
console.log(`Cleaned up old DXT server directory: ${filePath}`);
}
}
});
}
} catch (error) {
console.warn('Failed to cleanup old DXT server files:', error);
// Don't fail the installation if cleanup fails
}
};
export const uploadDxtFile = async (req: Request, res: Response): Promise<void> => {
try {
if (!req.file) {
res.status(400).json({
success: false,
message: 'No DXT file uploaded',
});
return;
}
const dxtFilePath = req.file.path;
const timestamp = Date.now();
const tempExtractDir = path.join(path.dirname(dxtFilePath), `temp-extracted-${timestamp}`);
try {
// Extract the DXT file (which is a ZIP archive) to a temporary directory first
const zip = new AdmZip(dxtFilePath);
zip.extractAllTo(tempExtractDir, true);
// Read and validate the manifest.json
const manifestPath = path.join(tempExtractDir, 'manifest.json');
if (!fs.existsSync(manifestPath)) {
throw new Error('manifest.json not found in DXT file');
}
const manifestContent = fs.readFileSync(manifestPath, 'utf-8');
const manifest = JSON.parse(manifestContent);
// Validate required fields in manifest
if (!manifest.dxt_version) {
throw new Error('Invalid manifest: missing dxt_version');
}
if (!manifest.name) {
throw new Error('Invalid manifest: missing name');
}
if (!manifest.version) {
throw new Error('Invalid manifest: missing version');
}
if (!manifest.server) {
throw new Error('Invalid manifest: missing server configuration');
}
// Use server name as the final extract directory for automatic version management
const finalExtractDir = path.join(path.dirname(dxtFilePath), `server-${manifest.name}`);
// Clean up any existing version of this server
cleanupOldDxtServer(manifest.name);
if (!fs.existsSync(finalExtractDir)) {
fs.mkdirSync(finalExtractDir, { recursive: true });
}
// Move the temporary directory to the final location
fs.renameSync(tempExtractDir, finalExtractDir);
console.log(`DXT server extracted to: ${finalExtractDir}`);
// Clean up the uploaded DXT file
fs.unlinkSync(dxtFilePath);
const response: ApiResponse = {
success: true,
data: {
manifest,
extractDir: finalExtractDir,
},
};
res.json(response);
} catch (extractError) {
// Clean up files on error
if (fs.existsSync(dxtFilePath)) {
fs.unlinkSync(dxtFilePath);
}
if (fs.existsSync(tempExtractDir)) {
fs.rmSync(tempExtractDir, { recursive: true, force: true });
}
throw extractError;
}
} catch (error) {
console.error('DXT upload error:', error);
let message = 'Failed to process DXT file';
if (error instanceof Error) {
message = error.message;
}
res.status(500).json({
success: false,
message,
});
}
};
|