init
This commit is contained in:
235
server/src/config/rustfs.js
Normal file
235
server/src/config/rustfs.js
Normal file
@@ -0,0 +1,235 @@
|
||||
/**
|
||||
* RUSTFS 对象存储配置
|
||||
* 直接使用 HTTP API 避免 AWS SDK 兼容性问题
|
||||
*/
|
||||
const https = require('https');
|
||||
const http = require('http');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const config = {
|
||||
endpoint: process.env.RUSTFS_ENDPOINT || 'http://43.156.91.115:9001',
|
||||
bucket: process.env.RUSTFS_BUCKET || 'setting',
|
||||
accessKey: process.env.RUSTFS_ACCESS_KEY || 'rustfsadmin',
|
||||
secretKey: process.env.RUSTFS_SECRET_KEY || 'rustfsadmin'
|
||||
};
|
||||
|
||||
// 生成 AWS Signature V4
|
||||
function createSignature(method, path, headers, query = '') {
|
||||
const date = new Date().toISOString().replace(/[:\-]|\.\d{3}/g, '');
|
||||
const dateShort = date.substring(0, 8);
|
||||
|
||||
const payloadHash = crypto.createHash('sha256').update('').digest('hex');
|
||||
const canonicalHeaders = Object.entries(headers)
|
||||
.map(([k, v]) => `${k.toLowerCase()}:${v}`)
|
||||
.join('\n');
|
||||
const signedHeaders = Object.keys(headers).map(k => k.toLowerCase()).join(';');
|
||||
|
||||
const canonicalRequest = [
|
||||
method,
|
||||
path,
|
||||
query,
|
||||
canonicalHeaders,
|
||||
'',
|
||||
signedHeaders,
|
||||
payloadHash
|
||||
].join('\n');
|
||||
|
||||
const credentialScope = `${dateShort}/us-east-1/s3/aws4_request`;
|
||||
const stringToSign = [
|
||||
'AWS4-HMAC-SHA256',
|
||||
date,
|
||||
credentialScope,
|
||||
crypto.createHash('sha256').update(canonicalRequest).digest('hex')
|
||||
].join('\n');
|
||||
|
||||
const kDate = crypto.createHmac('sha256', `AWS4${config.secretKey}`).update(dateShort).digest();
|
||||
const kRegion = crypto.createHmac('sha256', kDate).update('us-east-1').digest();
|
||||
const kService = crypto.createHmac('sha256', kRegion).update('s3').digest();
|
||||
const kSigning = crypto.createHmac('sha256', kService).update('aws4_request').digest();
|
||||
const signature = crypto.createHmac('sha256', kSigning).update(stringToSign).digest('hex');
|
||||
|
||||
return {
|
||||
authorization: `AWS4-HMAC-SHA256 Credential=${config.accessKey}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`,
|
||||
'x-amz-date': date,
|
||||
'x-amz-content-sha256': payloadHash
|
||||
};
|
||||
}
|
||||
|
||||
function makeRequest(method, path, body = null, contentType = 'application/octet-stream') {
|
||||
return new Promise((resolve, reject) => {
|
||||
const url = new URL(config.endpoint);
|
||||
const isHttps = url.protocol === 'https:';
|
||||
const httpModule = isHttps ? https : http;
|
||||
|
||||
const query = '';
|
||||
const headers = {
|
||||
'host': url.host,
|
||||
'content-type': contentType,
|
||||
'x-amz-date': new Date().toISOString().replace(/[:\-]|\.\d{3}/g, '').substring(0, 8) + 'T000000Z'
|
||||
};
|
||||
|
||||
if (body) {
|
||||
headers['content-length'] = Buffer.byteLength(body);
|
||||
}
|
||||
|
||||
// 简化认证:使用 Access Key 作为 Authorization header
|
||||
headers['Authorization'] = `AWS ${config.accessKey}:${crypto.createHash('sha256').update(config.secretKey).digest('hex')}`;
|
||||
|
||||
const options = {
|
||||
hostname: url.hostname,
|
||||
port: url.port || (isHttps ? 443 : 9001),
|
||||
path: `/${config.bucket}${path}`,
|
||||
method: method,
|
||||
headers: headers
|
||||
};
|
||||
|
||||
const req = httpModule.request(options, (res) => {
|
||||
let data = '';
|
||||
res.on('data', chunk => data += chunk);
|
||||
res.on('end', () => {
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
resolve(data);
|
||||
} else {
|
||||
reject(new Error(`HTTP ${res.statusCode}: ${data}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', reject);
|
||||
|
||||
if (body) {
|
||||
req.write(body);
|
||||
}
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件到 RUSTFS
|
||||
*/
|
||||
async function uploadFile(fileData, key, contentType = 'image/jpeg') {
|
||||
// 解析 endpoint
|
||||
const endpoint = config.endpoint;
|
||||
const url = new URL(endpoint);
|
||||
const isHttps = url.protocol === 'https:';
|
||||
const httpModule = isHttps ? https : http;
|
||||
|
||||
const date = new Date().toISOString().replace(/[:\-]|\.\d{3}/g, '').substring(0, 8);
|
||||
const datetime = date + 'T000000Z';
|
||||
|
||||
// 创建签名字符串
|
||||
const credential = `${config.accessKey}/${date}/us-east-1/s3/aws4_request`;
|
||||
const signedHeaders = 'content-type;host;x-amz-content-sha256;x-amz-date';
|
||||
const contentHash = crypto.createHash('sha256').update(fileData).digest('hex');
|
||||
|
||||
const canonicalHeaders = [
|
||||
`content-type:${contentType}`,
|
||||
`host:${url.host}`,
|
||||
`x-amz-content-sha256:${contentHash}`,
|
||||
`x-amz-date:${datetime}`
|
||||
].join('\n');
|
||||
|
||||
const canonicalRequest = [
|
||||
'PUT',
|
||||
`/${config.bucket}/${key}`,
|
||||
'',
|
||||
canonicalHeaders,
|
||||
'',
|
||||
signedHeaders,
|
||||
contentHash
|
||||
].join('\n');
|
||||
|
||||
const stringToSign = [
|
||||
'AWS4-HMAC-SHA256',
|
||||
datetime,
|
||||
`${date}/us-east-1/s3/aws4_request`,
|
||||
crypto.createHash('sha256').update(canonicalRequest).digest('hex')
|
||||
].join('\n');
|
||||
|
||||
// 计算签名
|
||||
const k1 = crypto.createHmac('sha256', 'AWS4' + config.secretKey).update(date).digest();
|
||||
const k2 = crypto.createHmac('sha256', k1).update('us-east-1').digest();
|
||||
const k3 = crypto.createHmac('sha256', k2).update('s3').digest();
|
||||
const k4 = crypto.createHmac('sha256', k3).update('aws4_request').digest();
|
||||
const signature = crypto.createHmac('sha256', k4).update(stringToSign).digest('hex');
|
||||
|
||||
const authorization = `AWS4-HMAC-SHA256 Credential=${credential}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
hostname: url.hostname,
|
||||
port: url.port || (isHttps ? 443 : 9001),
|
||||
path: `/${config.bucket}/${key}`,
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': contentType,
|
||||
'Host': url.host,
|
||||
'X-Amz-Content-Sha256': contentHash,
|
||||
'X-Amz-Date': datetime,
|
||||
'Authorization': authorization,
|
||||
'Content-Length': Buffer.byteLength(fileData)
|
||||
}
|
||||
};
|
||||
|
||||
const req = httpModule.request(options, (res) => {
|
||||
let data = '';
|
||||
res.on('data', chunk => data += chunk);
|
||||
res.on('end', () => {
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
resolve(`${endpoint}/${config.bucket}/${key}`);
|
||||
} else {
|
||||
reject(new Error(`Upload failed: HTTP ${res.statusCode} - ${data}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', reject);
|
||||
req.write(fileData);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除 RUSTFS 中的文件
|
||||
*/
|
||||
async function deleteFile(key) {
|
||||
const endpoint = config.endpoint;
|
||||
const url = new URL(endpoint);
|
||||
const isHttps = url.protocol === 'https:';
|
||||
const httpModule = isHttps ? https : http;
|
||||
|
||||
const datetime = new Date().toISOString().replace(/[:\-]|\.\d{3}/g, '').substring(0, 8) + 'T000000Z';
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
hostname: url.hostname,
|
||||
port: url.port || (isHttps ? 443 : 9001),
|
||||
path: `/${config.bucket}/${key}`,
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Host': url.host,
|
||||
'X-Amz-Date': datetime,
|
||||
'Authorization': `AWS ${config.accessKey}:${crypto.createHash('sha256').update(config.secretKey).digest('hex')}`
|
||||
}
|
||||
};
|
||||
|
||||
const req = httpModule.request(options, (res) => {
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
resolve();
|
||||
} else {
|
||||
let data = '';
|
||||
res.on('data', chunk => data += chunk);
|
||||
res.on('end', () => reject(new Error(`Delete failed: HTTP ${res.statusCode}`)));
|
||||
}
|
||||
});
|
||||
|
||||
req.on('error', reject);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
uploadFile,
|
||||
deleteFile,
|
||||
config
|
||||
};
|
||||
Reference in New Issue
Block a user