新增KV数据库存储附件

This commit is contained in:
eoao
2025-10-21 20:45:20 +08:00
parent a42e8fce8e
commit 140d451472
18 changed files with 213 additions and 73 deletions
+2 -2
View File
@@ -1,8 +1,8 @@
import r2Service from '../service/r2-service';
import app from '../hono/hono';
app.get('/file/*', async (c) => {
const key = c.req.path.split('/file/')[1];
app.get('/oss/*', async (c) => {
const key = c.req.path.split('/oss/')[1];
const obj = await r2Service.getObj(c, key);
return new Response(obj.body, {
headers: {
+6
View File
@@ -21,3 +21,9 @@ app.put('/setting/setBackground', async (c) => {
const key = await settingService.setBackground(c, await c.req.json());
return c.json(result.ok(key));
});
app.delete('/setting/deleteBackground', async (c) => {
await settingService.deleteBackground(c);
return c.json(result.ok());
});
+8
View File
@@ -107,6 +107,14 @@ export const settingConst = {
noRecipient: {
OPEN: 0,
CLOSE: 1,
},
kvStorage: {
OPEN: 0,
CLOSE: 1
},
forcePathStyle: {
OPEN: 0,
CLOSE: 1
}
}
+3 -1
View File
@@ -39,6 +39,8 @@ export const setting = sqliteTable('setting', {
region: text('region').default('').notNull(),
endpoint: text('endpoint').default('').notNull(),
s3AccessKey: text('s3_access_key').default('').notNull(),
s3SecretKey: text('s3_secret_key').default('').notNull()
s3SecretKey: text('s3_secret_key').default('').notNull(),
kvStorage: integer('kv_storage').default(1).notNull(),
forcePathStyle: integer('force_path_style').default(1).notNull()
});
export default setting
+1
View File
@@ -63,6 +63,7 @@ const en = {
publicTokenFail: 'Token validation failed',
notAdmin: 'The entered email is not an administrator email',
emailExistDatabase: 'Email already exists in the database',
notConfigOss: 'Object storage not configured',
perms: {
"邮件": "Email",
"邮件发送": "Send Email",
+1
View File
@@ -63,6 +63,7 @@ const zh = {
publicTokenFail: 'token验证失败',
notAdmin: '输入的邮箱不是管理员邮箱',
emailExistDatabase: '有邮箱已存在数据库中',
notConfigOss: '对象存储未配置',
perms: {
"邮件": "邮件",
"邮件发送": "邮件发送",
+6 -2
View File
@@ -3,10 +3,11 @@ import { email } from './email/email';
import userService from './service/user-service';
import verifyRecordService from './service/verify-record-service';
import emailService from './service/email-service';
import kvObjService from './service/kv-obj-service';
export default {
async fetch(req, env, ctx) {
const url = new URL(req.url)
const url = new URL(req.url)
if (url.pathname.startsWith('/api/')) {
url.pathname = url.pathname.replace('/api', '')
@@ -14,12 +15,15 @@ export default {
return app.fetch(req, env, ctx);
}
if (['/static/','/attachments/'].some(p => url.pathname.startsWith(p))) {
return await kvObjService.toObjResp( { env }, url.pathname.substring(1));
}
return env.assets.fetch(req);
},
email: email,
async scheduled(c, env, ctx) {
await verifyRecordService.clearRecord({env})
await verifyRecordService.clearRecord({ env })
await userService.resetDaySendCount({ env })
await emailService.completeReceiveAll({ env })
},
+12
View File
@@ -22,10 +22,22 @@ const init = {
await this.v1_6DB(c);
await this.v1_7DB(c);
await this.v2DB(c);
await this.v2_3DB(c);
await settingService.refresh(c);
return c.text(t('initSuccess'));
},
async v2_3DB(c) {
try {
await c.env.db.batch([
c.env.db.prepare(`ALTER TABLE setting ADD COLUMN force_path_style INTEGER NOT NULL DEFAULT 1;`),
c.env.db.prepare(`ALTER TABLE setting ADD COLUMN kv_storage INTEGER NOT NULL DEFAULT 1;`)
]);
} catch (e) {
console.error(e.message)
}
},
async v2DB(c) {
try {
await c.env.db.batch([
+3 -2
View File
@@ -11,7 +11,7 @@ import app from '../hono/hono';
const exclude = [
'/login',
'/register',
'/file',
'/oss',
'/setting/websiteConfig',
'/webhooks',
'/init',
@@ -34,6 +34,7 @@ const requirePerms = [
'/allEmail/list',
'/allEmail/delete',
'/setting/setBackground',
'/setting/deleteBackground',
'/setting/set',
'/setting/query',
'/user/delete',
@@ -73,7 +74,7 @@ const premKey = {
'all-email:query': ['/allEmail/list'],
'all-email:delete': ['/allEmail/delete','/allEmail/batchDelete'],
'setting:query': ['/setting/query'],
'setting:set': ['/setting/set', '/setting/setBackground'],
'setting:set': ['/setting/set', '/setting/setBackground','/setting/deleteBackground'],
'analysis:query': ['/analysis/echarts'],
'reg-key:add': ['/regKey/add'],
'reg-key:query': ['/regKey/list','/regKey/history'],
+36
View File
@@ -0,0 +1,36 @@
const kvObjService = {
async putObj(c, key, content, metadata) {
await c.env.kv.put(key, content, { metadata: metadata });
},
async deleteObj(c, keys) {
if (typeof keys === 'string') {
keys = [keys];
}
if (keys.length === 0) {
return;
}
await Promise.all(keys.map( key => c.env.kv.delete(key)));
},
async toObjResp(c, key) {
const obj = await c.env.kv.getWithMetadata(key, { type: "arrayBuffer"});
return new Response(obj.value, {
headers: {
'Content-Type': obj.metadata?.contentType || 'application/octet-stream',
'Content-Disposition': obj.metadata?.contentDisposition || null,
'Cache-Control': obj.metadata?.cacheControl || null
}
});
}
};
export default kvObjService;
+23 -5
View File
@@ -1,23 +1,35 @@
import s3Service from './s3-service';
import settingService from './setting-service';
import kvObjService from './kv-obj-service';
import { settingConst } from '../const/entity-const';
const r2Service = {
async hasOSS(c) {
if (c.env.r2) {
const setting = await settingService.query(c);
const { kvStorage, bucket, endpoint, s3AccessKey, s3SecretKey } = setting;
if (kvStorage === settingConst.kvStorage.OPEN) {
return true;
}
const setting = await settingService.query(c);
const { bucket, endpoint, s3AccessKey, s3SecretKey } = setting;
if (c.env.r2) {
return true;
}
return !!(bucket && endpoint && s3AccessKey && s3SecretKey);
},
async putObj(c, key, content, metadata) {
if (c.env.r2) {
const { kvStorage } = await settingService.query(c);
if (kvStorage === settingConst.kvStorage.OPEN) {
await kvObjService.putObj(c, key, content, metadata);
} else if (c.env.r2) {
await c.env.r2.put(key, content, {
httpMetadata: { ...metadata }
@@ -37,7 +49,13 @@ const r2Service = {
async delete(c, key) {
if (c.env.r2) {
const { kvStorage } = await settingService.query(c);
if (kvStorage === settingConst.kvStorage.OPEN) {
await kvObjService.deleteObj(c, key);
} else if (c.env.r2) {
await c.env.r2.delete(key);
+27 -12
View File
@@ -1,12 +1,9 @@
import KvConst from '../const/kv-const';
import setting from '../entity/setting';
import orm from '../entity/orm';
import { settingConst, verifyRecordType } from '../const/entity-const';
import { verifyRecordType } from '../const/entity-const';
import fileUtils from '../utils/file-utils';
import r2Service from './r2-service';
import emailService from './email-service';
import accountService from './account-service';
import userService from './user-service';
import constant from '../const/constant';
import BizError from '../error/biz-error';
import { t } from '../i18n/i18n'
@@ -100,12 +97,38 @@ const settingService = {
await this.refresh(c);
},
async deleteBackground(c) {
const { background } = await this.query(c);
if (!background) return
if (background.startsWith('http')) {
await orm(c).update(setting).set({ background: '' }).run();
await this.refresh(c)
return;
}
const hasOss = await r2Service.hasOSS(c);
if (hasOss) {
if (background) {
await r2Service.delete(c,background)
await orm(c).update(setting).set({ background: '' }).run();
await this.refresh(c)
}
}
},
async setBackground(c, params) {
const settingRow = await this.query(c);
let { background } = params
await this.deleteBackground(c);
if (background && !background.startsWith('http')) {
if (!await r2Service.hasOSS(c)) {
@@ -130,14 +153,6 @@ const settingService = {
}
if (settingRow.background) {
try {
await r2Service.delete(c, settingRow.background);
} catch (e) {
console.error(e)
}
}
await orm(c).update(setting).set({ background }).run();
await this.refresh(c);
return background;