mirror of
https://github.com/schroinerxy/cloud-mail.git
synced 2026-06-21 19:35:50 +08:00
186 lines
5.0 KiB
JavaScript
186 lines
5.0 KiB
JavaScript
import orm from '../entity/orm';
|
|
import { att } from '../entity/att';
|
|
import { and, eq, isNull, inArray } from 'drizzle-orm';
|
|
import r2Service from './r2-service';
|
|
import constant from '../const/constant';
|
|
import fileUtils from '../utils/file-utils';
|
|
import { attConst } from '../const/entity-const';
|
|
import { parseHTML } from 'linkedom';
|
|
import domainUtils from '../utils/domain-uitls';
|
|
|
|
const attService = {
|
|
|
|
async addAtt(c, attachments) {
|
|
|
|
for (let attachment of attachments) {
|
|
|
|
let metadate = {
|
|
contentType: attachment.mimeType,
|
|
}
|
|
|
|
if (!attachment.contentId) {
|
|
metadate.contentDisposition = `attachment; filename="${attachment.filename}"`
|
|
} else {
|
|
metadate.contentDisposition = `inline; filename="${attachment.filename}"`
|
|
metadate.cacheControl = `max-age=259200`
|
|
}
|
|
|
|
await r2Service.putObj(c, attachment.key, attachment.content, metadate);
|
|
}
|
|
|
|
await orm(c).insert(att).values(attachments).run();
|
|
},
|
|
|
|
list(c, params, userId) {
|
|
const { emailId } = params;
|
|
|
|
return orm(c).select().from(att).where(
|
|
and(
|
|
eq(att.emailId, emailId),
|
|
eq(att.userId, userId),
|
|
eq(att.type, attConst.type.ATT),
|
|
isNull(att.contentId)
|
|
)
|
|
).all();
|
|
},
|
|
|
|
async toImageUrlHtml(c, content, r2Domain) {
|
|
|
|
const { document } = parseHTML(content);
|
|
|
|
const images = Array.from(document.querySelectorAll('img'));
|
|
|
|
const attDataList = [];
|
|
|
|
for (const img of images) {
|
|
|
|
const src = img.getAttribute('src');
|
|
if (src && src.startsWith('data:image')) {
|
|
const file = fileUtils.base64ToFile(src);
|
|
const buff = await file.arrayBuffer();
|
|
const key = constant.ATTACHMENT_PREFIX + await fileUtils.getBuffHash(buff) + fileUtils.getExtFileName(file.name);
|
|
img.setAttribute('src', domainUtils.toOssDomain(r2Domain) + '/' + key);
|
|
|
|
const attData = {};
|
|
attData.key = key;
|
|
attData.filename = file.name;
|
|
attData.mimeType = file.type;
|
|
attData.size = file.size;
|
|
attData.buff = buff;
|
|
|
|
attDataList.push(attData);
|
|
}
|
|
|
|
const hasInlineWidth = img.hasAttribute('width');
|
|
const style = img.getAttribute('style') || '';
|
|
const hasStyleWidth = /(^|\s)width\s*:\s*[^;]+/.test(style);
|
|
|
|
if (!hasInlineWidth && !hasStyleWidth) {
|
|
const newStyle = (style ? style.trim().replace(/;$/, '') + '; ' : '') + 'max-width: 100%;';
|
|
img.setAttribute('style', newStyle);
|
|
}
|
|
}
|
|
return { attDataList, html: document.toString() };
|
|
},
|
|
|
|
async saveSendAtt(c, attList, userId, accountId, emailId) {
|
|
|
|
const attDataList = [];
|
|
|
|
for (let att of attList) {
|
|
att.buff = fileUtils.base64ToUint8Array(att.content);
|
|
att.key = constant.ATTACHMENT_PREFIX + await fileUtils.getBuffHash(att.buff) + fileUtils.getExtFileName(att.filename);
|
|
const attData = { userId, accountId, emailId };
|
|
attData.key = att.key;
|
|
attData.size = att.buff.length;
|
|
attData.filename = att.filename;
|
|
attData.mimeType = att.type;
|
|
attData.type = attConst.type.ATT;
|
|
attDataList.push(attData);
|
|
}
|
|
|
|
await orm(c).insert(att).values(attDataList).run();
|
|
|
|
for (let att of attList) {
|
|
await r2Service.putObj(c, att.key, att.buff, {
|
|
contentType: att.type,
|
|
contentDisposition: `attachment; filename="${att.filename}"`
|
|
});
|
|
}
|
|
|
|
},
|
|
|
|
async saveArticleAtt(c, attDataList, userId, accountId, emailId) {
|
|
|
|
for (let attData of attDataList) {
|
|
attData.userId = userId;
|
|
attData.emailId = emailId;
|
|
attData.accountId = accountId;
|
|
attData.type = attConst.type.EMBED;
|
|
await r2Service.putObj(c, attData.key, attData.buff, {
|
|
contentType: attData.mimeType,
|
|
cacheControl: `max-age=259200`,
|
|
contentDisposition: `inline; filename="${attData.filename}"`
|
|
});
|
|
}
|
|
|
|
await orm(c).insert(att).values(attDataList).run();
|
|
|
|
},
|
|
|
|
async removeByUserIds(c, userIds) {
|
|
await this.removeAttByField(c, 'user_id', userIds);
|
|
},
|
|
|
|
async removeByEmailIds(c, emailIds) {
|
|
await this.removeAttByField(c, 'email_id', emailIds);
|
|
},
|
|
|
|
selectByEmailIds(c, emailIds) {
|
|
return orm(c).select().from(att).where(
|
|
and(
|
|
inArray(att.emailId, emailIds),
|
|
eq(att.type, attConst.type.ATT)
|
|
))
|
|
.all();
|
|
},
|
|
|
|
async removeAttByField(c, fieldName, fieldValues) {
|
|
|
|
const queryAttSql = fieldValues.map(value =>
|
|
c.env.db.prepare(`SELECT a.key, a.att_id
|
|
FROM attachments a
|
|
JOIN (SELECT key
|
|
FROM attachments
|
|
GROUP BY key
|
|
HAVING COUNT (*) = 1) t
|
|
ON a.key = t.key
|
|
WHERE a.${fieldName} = ?;`).bind(value));
|
|
|
|
const attListResult = await c.env.db.batch(queryAttSql);
|
|
|
|
const delKeyList = attListResult.flatMap(r => r.results.map(row => row.key));
|
|
|
|
if (delKeyList.length > 0) {
|
|
await this.batchDelete(c, delKeyList);
|
|
}
|
|
|
|
const delAttSql = fieldValues.map(value => c.env.db.prepare(`DELETE FROM attachments WHERE ${fieldName} = ?`).bind(value));
|
|
await c.env.db.batch(delAttSql);
|
|
},
|
|
|
|
async batchDelete(c, keys) {
|
|
if (!keys.length) return;
|
|
|
|
const BATCH_SIZE = 1000;
|
|
|
|
for (let i = 0; i < keys.length; i += BATCH_SIZE) {
|
|
const batch = keys.slice(i, i + BATCH_SIZE);
|
|
await r2Service.delete(c, batch);
|
|
}
|
|
|
|
}
|
|
};
|
|
|
|
export default attService;
|