feat: admin search mailbox && fix generateName multi dot && user jwt exp in 30 days && UI globalTabplacement && useSideMargin (#214)

* fix: generateName multi dot && user jwt exp in 30 days

* feat: support admin search mailbox

* fix: DELETE mail bug(should be raw_mails)

* feat: UI add globalTabplacement

* feat: UI add useSideMargin option
This commit is contained in:
Dream Hunter
2024-05-09 18:43:09 +08:00
committed by GitHub
parent 1fa56dfe98
commit b7308587c6
18 changed files with 231 additions and 147 deletions

View File

@@ -1,7 +1,7 @@
import { Hono } from 'hono'
import { Jwt } from 'hono/utils/jwt'
import { sendAdminInternalMail, getJsonSetting, saveSetting } from '../utils'
import { newAddress } from '../common'
import { newAddress, handleListQuery } from '../common'
import { CONSTANTS } from '../constants'
import cleanup_api from './cleanup_api'
import admin_user_api from './admin_user_api'
@@ -74,7 +74,7 @@ api.delete('/admin/delete_address/:id', async (c) => {
return c.text("Failed to delete address", 500)
}
const { success: mailSuccess } = await c.env.DB.prepare(
`DELETE FROM mails WHERE address IN`
`DELETE FROM raw_mails WHERE address IN`
+ ` (select name from address where id = ?) `
).bind(id).run();
if (!mailSuccess) {
@@ -107,111 +107,58 @@ api.get('/admin/show_password/:id', async (c) => {
})
api.get('/admin/mails', async (c) => {
const { address, limit, offset } = c.req.query();
if (!limit || limit < 0 || limit > 100) {
return c.text("Invalid limit", 400)
const { address, limit, offset, keyword } = c.req.query();
if (address && keyword) {
return await handleListQuery(c,
`SELECT * FROM raw_mails where address = ? and raw like ? `,
`SELECT count(*) as count FROM raw_mails where address = ? and raw like ? `,
[address, `%${keyword}%`], limit, offset
);
} else if (keyword) {
return await handleListQuery(c,
`SELECT * FROM raw_mails where raw like ? `,
`SELECT count(*) as count FROM raw_mails where raw like ? `,
[`%${keyword}%`], limit, offset
);
} else if (address) {
return await handleListQuery(c,
`SELECT * FROM raw_mails where address = ? `,
`SELECT count(*) as count FROM raw_mails where address = ? `,
[address], limit, offset
);
} else {
return await handleListQuery(c,
`SELECT * FROM raw_mails `,
`SELECT count(*) as count FROM raw_mails `,
[], limit, offset
);
}
if (!offset || offset < 0) {
return c.text("Invalid offset", 400)
}
if (!address) {
const { results } = await c.env.DB.prepare(
`SELECT * FROM raw_mails order by id desc limit ? offset ?`
).bind(limit, offset).all();
let count = 0;
if (offset == 0) {
const { count: mailCount } = await c.env.DB.prepare(
`SELECT count(*) as count FROM raw_mails`
).first();
count = mailCount;
}
return c.json({
results: results,
count: count
})
}
const { results } = await c.env.DB.prepare(
`SELECT * FROM raw_mails where address = ? order by id desc limit ? offset ?`
).bind(address, limit, offset).all();
let count = 0;
if (offset == 0) {
const { count: mailCount } = await c.env.DB.prepare(
`SELECT count(*) as count FROM raw_mails where address = ? `
).bind(address).first();
count = mailCount;
}
return c.json({
results: results,
count: count
})
});
api.get('/admin/mails_unknow', async (c) => {
const { limit, offset } = c.req.query();
if (!limit || limit < 0 || limit > 100) {
return c.text("Invalid limit", 400)
}
if (!offset || offset < 0) {
return c.text("Invalid offset", 400)
}
const { results } = await c.env.DB.prepare(`
SELECT * FROM raw_mails
where address NOT IN (select name from address)
order by id desc limit ? offset ? `
).bind(limit, offset).all();
let count = 0;
if (offset == 0) {
const { count: mailCount } = await c.env.DB.prepare(`
SELECT count(*) as count FROM raw_mails
where address NOT IN
(select name from address)`
).first();
count = mailCount;
}
return c.json({
results: results,
count: count
})
return await handleListQuery(c,
`SELECT * FROM raw_mails where address NOT IN (select name from address) `,
`SELECT count(*) as count FROM raw_mails`
+ ` where address NOT IN (select name from address) `,
[], limit, offset
);
});
api.get('/admin/address_sender', async (c) => {
const { address, limit, offset } = c.req.query();
if (!limit || limit < 0 || limit > 100) {
return c.text("Invalid limit", 400)
}
if (!offset || offset < 0) {
return c.text("Invalid offset", 400)
}
if (address) {
const { results } = await c.env.DB.prepare(
`SELECT * FROM address_sender where address = ? order by id desc limit ? offset ?`
).bind(address, limit, offset).all();
let count = 0;
if (offset == 0) {
const { count: addressCount } = await c.env.DB.prepare(
`SELECT count(*) as count FROM address_sender where address = ?`
).bind(address).first();
count = addressCount;
}
return c.json({
results: results,
count: count
})
return await handleListQuery(c,
`SELECT * FROM address_sender where address = ? `,
`SELECT count(*) as count FROM address_sender where address = ? `,
[address], limit, offset
);
}
const { results } = await c.env.DB.prepare(
`SELECT * FROM address_sender order by id desc limit ? offset ? `
).bind(limit, offset).all();
let count = 0;
if (offset == 0) {
const { count: addressCount } = await c.env.DB.prepare(
`SELECT count(*) as count FROM address_sender`
).first();
count = addressCount;
}
return c.json({
results: results,
count: count
})
return await handleListQuery(c,
`SELECT * FROM address_sender `,
`SELECT count(*) as count FROM address_sender `,
[], limit, offset
);
})
api.post('/admin/address_sender', async (c) => {
@@ -276,9 +223,6 @@ api.get('/admin/sendbox', async (c) => {
})
api.get('/admin/statistics', async (c) => {
const { count: mailCountV1 } = await c.env.DB.prepare(`
SELECT count(*) as count FROM mails`
).first();
const { count: mailCount } = await c.env.DB.prepare(`
SELECT count(*) as count FROM raw_mails`
).first();
@@ -292,7 +236,7 @@ api.get('/admin/statistics', async (c) => {
SELECT count(*) as count FROM sendbox`
).first();
return c.json({
mailCount: (mailCountV1 || 0) + (mailCount || 0),
mailCount: mailCount,
userCount: addressCount,
activeUserCount7days: activeUserCount7days,
sendMailCount: sendMailCount

View File

@@ -74,6 +74,7 @@ export const cleanup = async (c, cleanType, cleanDays) => {
case "address":
await c.env.DB.prepare(`
DELETE FROM address WHERE updated_at < datetime('now', '-${cleanDays} day')`
+ ` AND id NOT IN (SELECT address_id FROM users_address)`
).run();
break;
case "sendbox":
@@ -86,3 +87,31 @@ export const cleanup = async (c, cleanType, cleanDays) => {
}
return true;
}
/**
*
* @param {*} c context
* @param {*} query @type {string} query
* @param {*} countQuery @type {string} countQuery
* @param {*} limit @type {number} limit
* @param {*} offset @type {number} offset
* @returns {Promise} Promise
*/
export const handleListQuery = async (
c, query, countQuery, params, limit, offset
) => {
if (!limit || limit < 0 || limit > 100) {
return c.text("Invalid limit", 400)
}
if (!offset || offset < 0) {
return c.text("Invalid offset", 400)
}
const resultsQuery = `${query} order by id desc limit ? offset ?`;
const { results } = await c.env.DB.prepare(resultsQuery).bind(
...params, limit, offset
).all();
const count = offset == 0 ? await c.env.DB.prepare(
countQuery
).bind(...params).first("count") : 0;
return c.json({ results, count });
}

View File

@@ -155,7 +155,7 @@ api.post('/api/send_mail', async (c) => {
api.post('/external/api/send_mail', async (c) => {
const { token } = await c.req.json();
try {
const { address } = await Jwt.verify(token, c.env.JWT_SECRET);
const { address } = await Jwt.verify(token, c.env.JWT_SECRET, "HS256");
if (!address) {
return c.text("No address", 400)
}

View File

@@ -162,7 +162,7 @@ api.delete('/api/delete_address', async (c) => {
return c.text("Failed to delete address", 500)
}
const { success: mailSuccess } = await c.env.DB.prepare(
`DELETE FROM mails WHERE address = ? `
`DELETE FROM raw_mails WHERE address = ? `
).bind(address).run();
if (!mailSuccess) {
return c.text("Failed to delete mails", 500)

View File

@@ -132,7 +132,10 @@ export default {
// create jwt
const jwt = await Jwt.sign({
user_email: email,
user_id: user_id
user_id: user_id,
// 30 days expire in seconds
exp: Math.floor(Date.now() / 1000) + 30 * 24 * 60 * 60,
iat: Math.floor(Date.now() / 1000),
}, c.env.JWT_SECRET)
return c.json({
jwt: jwt

View File

@@ -52,7 +52,7 @@ app.use('/api/*', async (c, next) => {
await next();
return;
}
return jwt({ secret: c.env.JWT_SECRET })(c, next);
return jwt({ secret: c.env.JWT_SECRET, alg: "HS256" })(c, next);
});
// user_api auth
app.use('/user_api/*', async (c, next) => {
@@ -67,7 +67,13 @@ app.use('/user_api/*', async (c, next) => {
}
try {
const token = c.req.raw.headers.get("x-user-token");
const payload = await Jwt.verify(token, c.env.JWT_SECRET);
const payload = await Jwt.verify(token, c.env.JWT_SECRET, "HS256");
// check expired
if (!payload.exp) return c.text("Invalid Token", 401);
// exp is in seconds
if (payload.exp < Math.floor(Date.now() / 1000)) {
return c.text("Token Expired", 401)
}
c.set("userPayload", payload);
} catch (e) {
console.error(e);
@@ -76,7 +82,7 @@ app.use('/user_api/*', async (c, next) => {
if (c.req.path.startsWith('/user_api/bind_address')
&& c.req.method === 'POST'
) {
return jwt({ secret: c.env.JWT_SECRET })(c, next);
return jwt({ secret: c.env.JWT_SECRET, alg: "HS256" })(c, next);
}
await next();
});