feat: im图片存储
This commit is contained in:
parent
44ac5d3a02
commit
eb58dfdbbe
2
.env.dev
2
.env.dev
@ -10,8 +10,6 @@ CONFIG_NODE_PORT=8080
|
||||
CONFIG_ZYB_YB_APPID=7D8D8E29924048539165F5E2E08C4616
|
||||
CONFIG_ZYB_YB_SECRET=77F9610253B94263BCF8DBB61BF90D92
|
||||
|
||||
## 支付宝回调地址
|
||||
CONFIG_ALIPAY_PAY_NOTIFY=https://patient.youcan365.com/zyt/alipay-pay-notify
|
||||
## 支付宝小程序appId
|
||||
CONFIG_ALIPAY_MINI_APPID = 2021004129669493
|
||||
CONFIG_ALIPAY_MINI_PUBLIC_KEY=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo7Ulswxr/3dJpX0RrPylV9rR3STdwGtcbV4ppNEiR0R37/9L/JpO/Y5gbEaaPn9S/xALzGKwI4N3/v1H2x566LJIQvzh5szCviy20NNGc+TbyxLJ9gLBxcsBDT8+o+a8CdaHbU/xvutZvLRa34D5pa6lcDCONC6q8flwWmXemUZ5widtW10F+4Niyufr+8hKkIpRfQMWYbsuA3EpXwjhztJglxPnjMN7r33vq9Pd5cU+KOOGuelXdB8aMwWcaXbffb7OKaTtZYnbUezJKbqZ+LtYgGF8+E5CKP7tzlEnZvQZwlPgS516EG3WRcCqjnEYO7RpG/eZ/0569d4TW8UPrwIDAQAB
|
||||
|
||||
2
.env.pro
2
.env.pro
@ -11,7 +11,7 @@ CONFIG_ZYB_YB_SECRET=77F9610253B94263BCF8DBB61BF90D92
|
||||
CONFIG_SMS_GATEWAY_DISABLED=false
|
||||
|
||||
## 支付宝小程序appId
|
||||
CONFIG_ALIPAY_MINI_APPID = 2021005110656858
|
||||
CONFIG_ALIPAY_MINI_APPID = 2021004129669493
|
||||
CONFIG_ALIPAY_MINI_PUBLIC_KEY=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo7Ulswxr/3dJpX0RrPylV9rR3STdwGtcbV4ppNEiR0R37/9L/JpO/Y5gbEaaPn9S/xALzGKwI4N3/v1H2x566LJIQvzh5szCviy20NNGc+TbyxLJ9gLBxcsBDT8+o+a8CdaHbU/xvutZvLRa34D5pa6lcDCONC6q8flwWmXemUZ5widtW10F+4Niyufr+8hKkIpRfQMWYbsuA3EpXwjhztJglxPnjMN7r33vq9Pd5cU+KOOGuelXdB8aMwWcaXbffb7OKaTtZYnbUezJKbqZ+LtYgGF8+E5CKP7tzlEnZvQZwlPgS516EG3WRcCqjnEYO7RpG/eZ/0569d4TW8UPrwIDAQAB
|
||||
CONFIG_ALIPAY_MINI_PRIVATE_KEY=MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCirWNE4gjlUOV6+PNo6CPyLUnJOe/yw8ODZInBun6P2h3rs33WBLFBGdbV9Ct5pXXou48l2icKPsFAWVI5MmtZUIHGBkdvxzQeLNeKpKlJvpujWS/oLQq232MeM7YmWa4tjr40SHqlhVzxcgYST55jBXmVo9KJNrBj5i5zlR9N49x5EJZrUXsVUprsavk1z1JJyUKthw/FrycGsS47YQ5jHyQUFsqEviyOY5Bgz4qZ4xhHJ7p7zhwkiXHqscyeF0+Eu1xf4jS23jlbOiV+5yPo4eWcHElf0aD+k+ngPVpgVqNHMYBTSW1u8pFCpUVDv02QUNg1NRJpBcTAtnLY7yyhAgMBAAECggEAKl54ZV9tq7QnIKEoXGFhw1iSSCuj2BmumVEdI+zbu8G2W0YzdwNf2zHMp4rhq9wxWn1HKjeoRV3U3VqmNKlyFiAGvLmz72P1qgpJ1R1Pzhor1R6MgK8ChGCoIxyay2A+IjV0So+6CdQzQdtNw4UjHm01noi9c+lfhZVU8dub/J/62cGCfcBh/NX+juyI+5Vb9BLnG0CbFZ1SbssK8FN9w6MiKNq/fgIdMyvZXWt9Zoj21Bc9eyxd0EUPp800P6eeThzwNVyeSOETehIDSGwndrbxbLX1nCmievnEYZ/pT6T0J+UuPkdLjZq+VQQry0IxAlVvRim33JS4w99gBYIHcQKBgQDSQsEL5QtSLNg3xs5R3vMG+C8wJbm0eYiPlpSLcFgLxedoxRPdjzeT/Wfi245mA1L8NADRn37gfgPukc5TgWzTV340KzlScAv0Y2HHiQV9HII6b5I/f2S3AsM2IY4jDN+PryBDT5mTYUwW7LhtxC97LqCozPhY6aPozoD6VKBhFQKBgQDGEMAcrPSmXNjxhg/1lPJoe7fe4A3uyLPfHIVqZTFSxrfzwteL3bMuhsx8eSNnmT35trQfKXYT8Pz85IWGcoBPuhDkrjDQy+w5u4QeRcyJQRyUqtDxruIwNbvN/EL6FME24u6bIng6FjHW+0j2SFrFIWpUo1v3B5mSRGzvTWZIXQKBgAXI1lzIaV5SY4RtUMHJ22/Gpi2x7AD+Eva4Us/fQFzajfpA6cH/razBnPkNx0YhM0hC+f7KX4g3j04h7sY3WlIaJxuyULpm4L1ReqAtsdv2S9LlvOVIUPID/nDfzXRDZ2chj0aqyvPWCmzbCFvkuNWvu7XJJc/0ZZEDFWNNrrfdAoGAB/2dAosnw55zUisqcan0rapwHJnFdiC6wQGRkF1MxB4oRblSoN3s4K+niE4K058nFO/+DFPL47oROtKtYGM2MgE7tT4h0gqoGRwrlF0Uh2UNFiJeE6IOlgtU7FaFDTnRIbm+DpwxZYaHtDinFOiOYO2cMJX2m9y+oVwJWQ4yVjECgYEAg01aEYSOYQVeeub8g1e1wHKj3L4v/xECpZNzoQWF/WhZDHiKCqN2VD54XYl0dUHFLqBXn01tfg0gt4kO/roiWJdH273DBYu52ttwNdcRVlud6RlTBDO//M8dglrxOWz51HhSTepOXmJflF1dept9OPVddzQAJdFN65umF27DIVI=
|
||||
|
||||
|
||||
@ -28,6 +28,15 @@ CONFIG_IM_IMAGE_ALLOWED_HOSTS=.my-imcloud.com,.im.qcloud.com,.myqcloud.com
|
||||
`private-files` 不会通过 Express 静态目录公开。该目录不能在发布时被清理;
|
||||
Docker 部署时必须挂载持久化卷。
|
||||
|
||||
服务启动时会主动创建并检查该目录是否可读写。启动日志中应出现:
|
||||
|
||||
```text
|
||||
腾讯 IM 图片归档目录已就绪,/opt/apps/private-files/im-chat
|
||||
```
|
||||
|
||||
如果日志中出现 `EACCES`,需要给实际运行 Node/PM2 的 Linux 用户授予该目录的
|
||||
读写权限。
|
||||
|
||||
归档成功后可通过下面的接口读取图片:
|
||||
|
||||
```text
|
||||
|
||||
@ -28,6 +28,14 @@ function getImageRoot() {
|
||||
);
|
||||
}
|
||||
|
||||
async function initializeStorage() {
|
||||
const imageRoot = getImageRoot();
|
||||
await fsp.mkdir(imageRoot, { recursive: true });
|
||||
await fsp.access(imageRoot, fs.constants.R_OK | fs.constants.W_OK);
|
||||
logger.info("腾讯 IM 图片归档目录已就绪", imageRoot);
|
||||
return imageRoot;
|
||||
}
|
||||
|
||||
function getMaxFileSize() {
|
||||
const configured = Number(process.env.CONFIG_IM_IMAGE_MAX_BYTES);
|
||||
return Number.isFinite(configured) && configured > 0
|
||||
@ -398,6 +406,7 @@ async function sendArchivedImage(req, res, db) {
|
||||
module.exports = {
|
||||
findOriginalImage,
|
||||
prepareMessageForArchive,
|
||||
initializeStorage,
|
||||
enqueueArchive,
|
||||
resumePendingArchives,
|
||||
sendArchivedImage,
|
||||
|
||||
@ -107,6 +107,18 @@ describe("IM image archive", () => {
|
||||
).toBe(path.resolve("/opt/apps", "private-files", "im-chat"));
|
||||
});
|
||||
|
||||
test("creates and validates the configured storage directory", async () => {
|
||||
const configuredRoot = path.join(temporaryRoot, "nested", "im-chat");
|
||||
process.env.CONFIG_IM_IMAGE_ROOT = configuredRoot;
|
||||
|
||||
await expect(archive.initializeStorage()).resolves.toBe(
|
||||
path.resolve(configuredRoot)
|
||||
);
|
||||
await expect(fsp.access(configuredRoot, fs.constants.W_OK)).resolves.toBe(
|
||||
undefined
|
||||
);
|
||||
});
|
||||
|
||||
test("downloads an image and marks the database record successful", async () => {
|
||||
const jpeg = Buffer.concat([
|
||||
Buffer.from([0xff, 0xd8, 0xff, 0xe0]),
|
||||
|
||||
10
index.js
10
index.js
@ -201,6 +201,12 @@ const databaseReadyPromise = connectToMongoDB().catch((err) => {
|
||||
return null;
|
||||
});
|
||||
|
||||
// 启动时立即创建并校验 IM 图片归档目录,避免等到首张图片下载时才暴露路径或权限问题。
|
||||
const imageStorageReadyPromise = imImageArchive.initializeStorage().catch((err) => {
|
||||
logger.error("腾讯 IM 图片归档目录初始化失败:", err);
|
||||
return null;
|
||||
});
|
||||
|
||||
app.get(
|
||||
"/zyt/stats",
|
||||
asyncHandler(async (req, res) => {
|
||||
@ -727,6 +733,10 @@ trigger.initSchedule();
|
||||
databaseReadyPromise.then(async (connected) => {
|
||||
if (!connected) return;
|
||||
try {
|
||||
const imageStorageRoot = await imageStorageReadyPromise;
|
||||
if (!imageStorageRoot) {
|
||||
throw new Error("腾讯 IM 图片归档目录不可用");
|
||||
}
|
||||
const internetHospitalDb = await getDatabase("Internet-hospital");
|
||||
await consultTrigger(
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user