mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-09-05 23:47:50 +08:00
feature: support init db in admin portal (#658)
This commit is contained in:
@@ -30,7 +30,7 @@
|
|||||||
"naive-ui": "^2.41.0",
|
"naive-ui": "^2.41.0",
|
||||||
"postal-mime": "^2.4.3",
|
"postal-mime": "^2.4.3",
|
||||||
"vooks": "^0.2.12",
|
"vooks": "^0.2.12",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.14",
|
||||||
"vue-clipboard3": "^2.0.0",
|
"vue-clipboard3": "^2.0.0",
|
||||||
"vue-i18n": "^11.1.3",
|
"vue-i18n": "^11.1.3",
|
||||||
"vue-router": "^4.5.1"
|
"vue-router": "^4.5.1"
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vicons/fa": "^0.13.0",
|
"@vicons/fa": "^0.13.0",
|
||||||
"@vicons/material": "^0.13.0",
|
"@vicons/material": "^0.13.0",
|
||||||
"@vitejs/plugin-vue": "^5.2.3",
|
"@vitejs/plugin-vue": "^5.2.4",
|
||||||
"unplugin-auto-import": "^19.2.0",
|
"unplugin-auto-import": "^19.2.0",
|
||||||
"unplugin-vue-components": "^28.5.0",
|
"unplugin-vue-components": "^28.5.0",
|
||||||
"vite": "^6.3.5",
|
"vite": "^6.3.5",
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
"vite-plugin-wasm": "^3.4.1",
|
"vite-plugin-wasm": "^3.4.1",
|
||||||
"workbox-build": "^7.3.0",
|
"workbox-build": "^7.3.0",
|
||||||
"workbox-window": "^7.3.0",
|
"workbox-window": "^7.3.0",
|
||||||
"wrangler": "^4.14.2"
|
"wrangler": "^4.15.2"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
|
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+310
-567
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,7 @@ import Mails from './admin/Mails.vue';
|
|||||||
import MailsUnknow from './admin/MailsUnknow.vue';
|
import MailsUnknow from './admin/MailsUnknow.vue';
|
||||||
import About from './common/About.vue';
|
import About from './common/About.vue';
|
||||||
import Maintenance from './admin/Maintenance.vue';
|
import Maintenance from './admin/Maintenance.vue';
|
||||||
|
import DatabaseManager from './admin/DatabaseManager.vue';
|
||||||
import Appearance from './common/Appearance.vue';
|
import Appearance from './common/Appearance.vue';
|
||||||
import Telegram from './admin/Telegram.vue';
|
import Telegram from './admin/Telegram.vue';
|
||||||
import Webhook from './admin/Webhook.vue';
|
import Webhook from './admin/Webhook.vue';
|
||||||
@@ -67,6 +68,7 @@ const { t } = useI18n({
|
|||||||
webhookSettings: 'Webhook Settings',
|
webhookSettings: 'Webhook Settings',
|
||||||
statistics: 'Statistics',
|
statistics: 'Statistics',
|
||||||
maintenance: 'Maintenance',
|
maintenance: 'Maintenance',
|
||||||
|
database: 'Database',
|
||||||
workerconfig: 'Worker Config',
|
workerconfig: 'Worker Config',
|
||||||
appearance: 'Appearance',
|
appearance: 'Appearance',
|
||||||
about: 'About',
|
about: 'About',
|
||||||
@@ -93,6 +95,7 @@ const { t } = useI18n({
|
|||||||
webhookSettings: 'Webhook 设置',
|
webhookSettings: 'Webhook 设置',
|
||||||
statistics: '统计',
|
statistics: '统计',
|
||||||
maintenance: '维护',
|
maintenance: '维护',
|
||||||
|
database: '数据库',
|
||||||
workerconfig: 'Worker 配置',
|
workerconfig: 'Worker 配置',
|
||||||
appearance: '外观',
|
appearance: '外观',
|
||||||
about: '关于',
|
about: '关于',
|
||||||
@@ -126,6 +129,9 @@ onMounted(async () => {
|
|||||||
<n-tabs v-if="showAdminPage" type="card" v-model:value="adminTab" :placement="globalTabplacement">
|
<n-tabs v-if="showAdminPage" type="card" v-model:value="adminTab" :placement="globalTabplacement">
|
||||||
<n-tab-pane name="qucickSetup" :tab="t('qucickSetup')">
|
<n-tab-pane name="qucickSetup" :tab="t('qucickSetup')">
|
||||||
<n-tabs type="bar" justify-content="center" animated>
|
<n-tabs type="bar" justify-content="center" animated>
|
||||||
|
<n-tab-pane name="database" :tab="t('database')">
|
||||||
|
<DatabaseManager />
|
||||||
|
</n-tab-pane>
|
||||||
<n-tab-pane name="account_settings" :tab="t('account_settings')">
|
<n-tab-pane name="account_settings" :tab="t('account_settings')">
|
||||||
<AccountSettings />
|
<AccountSettings />
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
@@ -196,6 +202,9 @@ onMounted(async () => {
|
|||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
<n-tab-pane name="maintenance" :tab="t('maintenance')">
|
<n-tab-pane name="maintenance" :tab="t('maintenance')">
|
||||||
<n-tabs type="bar" justify-content="center" animated>
|
<n-tabs type="bar" justify-content="center" animated>
|
||||||
|
<n-tab-pane name="database" :tab="t('database')">
|
||||||
|
<DatabaseManager />
|
||||||
|
</n-tab-pane>
|
||||||
<n-tab-pane name="workerconfig" :tab="t('workerconfig')">
|
<n-tab-pane name="workerconfig" :tab="t('workerconfig')">
|
||||||
<WorkerConfig />
|
<WorkerConfig />
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
|
|||||||
@@ -0,0 +1,126 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { CleaningServicesFilled } from '@vicons/material'
|
||||||
|
|
||||||
|
import { api } from '../../api'
|
||||||
|
import { init } from 'vooks/lib/on-fonts-ready';
|
||||||
|
|
||||||
|
const message = useMessage()
|
||||||
|
const dbVersionData = ref({
|
||||||
|
need_initialization: false,
|
||||||
|
need_migration: false,
|
||||||
|
current_db_version: '',
|
||||||
|
code_db_version: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const { t } = useI18n({
|
||||||
|
messages: {
|
||||||
|
en: {
|
||||||
|
need_initialization_tip: 'Database initialization is required. Please initialize the database.',
|
||||||
|
need_migration_tip: 'Database migration is required. Please migrate the database.',
|
||||||
|
current_db_version: 'Current DB Version',
|
||||||
|
code_db_version: 'Code Needed DB Version',
|
||||||
|
init: 'Initialize Database',
|
||||||
|
migration: 'Migrate Database',
|
||||||
|
initializationSuccess: 'Database initialized successfully',
|
||||||
|
migrationSuccess: 'Database migrated successfully',
|
||||||
|
},
|
||||||
|
zh: {
|
||||||
|
need_initialization_tip: '需要初始化数据库,请初始化数据库',
|
||||||
|
need_migration_tip: '需要迁移数据库,请迁移数据库',
|
||||||
|
current_db_version: '当前数据库版本',
|
||||||
|
code_db_version: '需要的数据库版本',
|
||||||
|
init: '初始化数据库',
|
||||||
|
migration: '升级数据库 Schema',
|
||||||
|
initializationSuccess: '数据库初始化成功',
|
||||||
|
migrationSuccess: '数据库升级成功',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const fetchData = async () => {
|
||||||
|
try {
|
||||||
|
const res = await api.fetch('/admin/db_version');
|
||||||
|
if (res) Object.assign(dbVersionData.value, res);
|
||||||
|
} catch (error) {
|
||||||
|
message.error(error.message || "error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialization = async () => {
|
||||||
|
try {
|
||||||
|
await api.fetch('/admin/db_initialize', {
|
||||||
|
method: 'POST'
|
||||||
|
});
|
||||||
|
await fetchData();
|
||||||
|
message.success(t('initializationSuccess'));
|
||||||
|
} catch (error) {
|
||||||
|
message.error(error.message || "error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const migration = async () => {
|
||||||
|
try {
|
||||||
|
await api.fetch('/admin/db_migration', {
|
||||||
|
method: 'POST'
|
||||||
|
});
|
||||||
|
await fetchData();
|
||||||
|
message.success(t('migrationSuccess'));
|
||||||
|
} catch (error) {
|
||||||
|
message.error(error.message || "error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await fetchData();
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="center">
|
||||||
|
<n-card :bordered="false" embedded>
|
||||||
|
<n-alert v-if="dbVersionData.need_initialization" type="warning" :show-icon="false" :bordered="false">
|
||||||
|
<span>{{ t('need_initialization_tip') }}</span>
|
||||||
|
<n-button @click="initialization" type="primary" secondary block :loading="loading">
|
||||||
|
{{ t('init') }}
|
||||||
|
</n-button>
|
||||||
|
</n-alert>
|
||||||
|
<n-alert v-if="dbVersionData.need_migration" type="warning" :show-icon="false" :bordered="false">
|
||||||
|
<span>{{ t('need_migration_tip') }}</span>
|
||||||
|
<n-button @click="migration" type="primary" secondary block :loading="loading">
|
||||||
|
{{ t('migration') }}
|
||||||
|
</n-button>
|
||||||
|
</n-alert>
|
||||||
|
<n-alert type="info" :show-icon="false" :bordered="false">
|
||||||
|
<span>
|
||||||
|
{{ t('current_db_version') }}: {{ dbVersionData.current_db_version || "unknown" }},
|
||||||
|
{{ t('code_db_version') }}: {{ dbVersionData.code_db_version }}
|
||||||
|
</span>
|
||||||
|
</n-alert>
|
||||||
|
|
||||||
|
</n-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.n-card {
|
||||||
|
max-width: 800px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.n-alert {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
display: flex;
|
||||||
|
text-align: center;
|
||||||
|
place-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.n-button {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
+1
-1
@@ -11,7 +11,7 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"wrangler": "^4.14.2"
|
"wrangler": "^4.15.2"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
|
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
# 初始化/更新 D1 数据库
|
# 初始化/更新 D1 数据库
|
||||||
|
|
||||||
|
## 创建数据库
|
||||||
|
|
||||||
|
打开 cloudflare 控制台,选择 `Workers & Pages` -> `D1` -> `Create Database`,点击创建数据库
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
创建完成后,我们在 cloudflare 的控制台可以看到 D1 数据库,并获取到数据库的 `名称` 和 `数据库 ID`
|
||||||
|
|
||||||
|
## 初始化数据库
|
||||||
|
|
||||||
|
在部署完成后,在 admin 页面的 `快速设置` -> `数据库` 中,点击 `初始化数据库` 按钮来初始化数据库
|
||||||
|
|
||||||
|
## 更新数据库 schema
|
||||||
|
|
||||||
参考 [命令行更新 d1](/zh/guide/cli/d1) 或者 [用户界面更新 d1](/zh/guide/ui/d1)
|
参考 [命令行更新 d1](/zh/guide/cli/d1) 或者 [用户界面更新 d1](/zh/guide/ui/d1)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# 初始化/更新 D1 数据库
|
# 初始化/更新 D1 数据库
|
||||||
|
|
||||||
## 初始化数据库
|
## 创建数据库
|
||||||
|
|
||||||
打开 cloudflare 控制台,选择 `Workers & Pages` -> `D1` -> `Create Database`,点击创建数据库
|
打开 cloudflare 控制台,选择 `Workers & Pages` -> `D1` -> `Create Database`,点击创建数据库
|
||||||
|
|
||||||
@@ -8,6 +8,10 @@
|
|||||||
|
|
||||||
创建完成后,我们在 cloudflare 的控制台可以看到 D1 数据库
|
创建完成后,我们在 cloudflare 的控制台可以看到 D1 数据库
|
||||||
|
|
||||||
|
## 初始化数据库
|
||||||
|
|
||||||
|
你也可以跳过初始化数据库,在部署完成后,在 admin 页面的 `快速设置` -> `数据库` 中,点击 `初始化数据库` 按钮来初始化数据库
|
||||||
|
|
||||||
::: warning 注意
|
::: warning 注意
|
||||||
下面输入的是 `db/schema.sql` 的内容
|
下面输入的是 `db/schema.sql` 的内容
|
||||||
:::
|
:::
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
"version": "0.10.0",
|
"version": "0.10.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.15.12",
|
"@types/node": "^22.15.19",
|
||||||
"vitepress": "^1.6.3",
|
"vitepress": "^1.6.3",
|
||||||
"wrangler": "^4.14.2"
|
"wrangler": "^4.15.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vitepress dev docs",
|
"dev": "vitepress dev docs",
|
||||||
|
|||||||
Generated
+435
-435
File diff suppressed because it is too large
Load Diff
+7
-6
@@ -11,19 +11,20 @@
|
|||||||
"build": "wrangler deploy --dry-run --outdir dist --minify"
|
"build": "wrangler deploy --dry-run --outdir dist --minify"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cloudflare/workers-types": "^4.20250506.0",
|
"@cloudflare/workers-types": "^4.20250520.0",
|
||||||
"@eslint/js": "9.18.0",
|
"@eslint/js": "9.18.0",
|
||||||
"@simplewebauthn/types": "10.0.0",
|
"@simplewebauthn/types": "10.0.0",
|
||||||
|
"@types/node": "^22.15.19",
|
||||||
"eslint": "9.18.0",
|
"eslint": "9.18.0",
|
||||||
"globals": "^15.15.0",
|
"globals": "^15.15.0",
|
||||||
"typescript-eslint": "^8.32.0",
|
"typescript-eslint": "^8.32.1",
|
||||||
"wrangler": "^4.14.2"
|
"wrangler": "^4.15.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.803.0",
|
"@aws-sdk/client-s3": "^3.812.0",
|
||||||
"@aws-sdk/s3-request-presigner": "^3.803.0",
|
"@aws-sdk/s3-request-presigner": "^3.812.0",
|
||||||
"@simplewebauthn/server": "10.0.1",
|
"@simplewebauthn/server": "10.0.1",
|
||||||
"hono": "^4.7.8",
|
"hono": "^4.7.10",
|
||||||
"jsonpath-plus": "^10.3.0",
|
"jsonpath-plus": "^10.3.0",
|
||||||
"mimetext": "^3.0.27",
|
"mimetext": "^3.0.27",
|
||||||
"postal-mime": "^2.4.3",
|
"postal-mime": "^2.4.3",
|
||||||
|
|||||||
Generated
+815
-793
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,156 @@
|
|||||||
|
import { Context } from "hono";
|
||||||
|
import { CONSTANTS } from "../constants";
|
||||||
|
import utils from "../utils";
|
||||||
|
|
||||||
|
const DB_INIT_QUERIES = `
|
||||||
|
CREATE TABLE IF NOT EXISTS raw_mails (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
message_id TEXT,
|
||||||
|
source TEXT,
|
||||||
|
address TEXT,
|
||||||
|
raw TEXT,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_raw_mails_address ON raw_mails(address);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS address (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT UNIQUE,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_address_name ON address(name);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS auto_reply_mails (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
source_prefix TEXT,
|
||||||
|
name TEXT,
|
||||||
|
address TEXT UNIQUE,
|
||||||
|
subject TEXT,
|
||||||
|
message TEXT,
|
||||||
|
enabled INTEGER DEFAULT 1,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_auto_reply_mails_address ON auto_reply_mails(address);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS address_sender (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
address TEXT UNIQUE,
|
||||||
|
balance INTEGER DEFAULT 0,
|
||||||
|
enabled INTEGER DEFAULT 1,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_address_sender_address ON address_sender(address);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS sendbox (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
address TEXT,
|
||||||
|
raw TEXT,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_sendbox_address ON sendbox(address);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS settings (
|
||||||
|
key TEXT PRIMARY KEY,
|
||||||
|
value TEXT,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
user_email TEXT UNIQUE NOT NULL,
|
||||||
|
password TEXT NOT NULL,
|
||||||
|
user_info TEXT,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_users_user_email ON users(user_email);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS users_address (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
user_id INTEGER,
|
||||||
|
address_id INTEGER UNIQUE,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_users_address_user_id ON users_address(user_id);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_users_address_address_id ON users_address(address_id);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS user_roles (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
user_id INTEGER UNIQUE NOT NULL,
|
||||||
|
role_text TEXT,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_user_roles_user_id ON user_roles(user_id);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS user_passkeys (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
passkey_name TEXT NOT NULL,
|
||||||
|
passkey_id TEXT NOT NULL,
|
||||||
|
passkey TEXT NOT NULL,
|
||||||
|
counter INTEGER DEFAULT 0,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_user_passkeys_user_id ON user_passkeys(user_id);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_user_passkeys_user_id_passkey_id ON user_passkeys(user_id, passkey_id);
|
||||||
|
`
|
||||||
|
|
||||||
|
export default {
|
||||||
|
initialize: async (c: Context<HonoCustomType>) => {
|
||||||
|
// remove all \r and \n characters from the query string
|
||||||
|
// split by ; and join with a ;\n
|
||||||
|
const query = DB_INIT_QUERIES.replace(/[\r\n]/g, "")
|
||||||
|
.split(";")
|
||||||
|
.map((query) => query.trim())
|
||||||
|
.join(";\n");
|
||||||
|
await c.env.DB.exec(query);
|
||||||
|
|
||||||
|
const version = await utils.getSetting(c, CONSTANTS.DB_VERSION_KEY);
|
||||||
|
if (version) {
|
||||||
|
return c.json({ message: "Database already initialized" });
|
||||||
|
}
|
||||||
|
await utils.saveSetting(c, CONSTANTS.DB_VERSION_KEY, CONSTANTS.DB_VERSION);
|
||||||
|
return c.json({ message: "Database initialized" });
|
||||||
|
},
|
||||||
|
migrate: async (c: Context<HonoCustomType>) => {
|
||||||
|
const version = await utils.getSetting(c, CONSTANTS.DB_VERSION_KEY);
|
||||||
|
if (version != CONSTANTS.DB_VERSION) {
|
||||||
|
// TODO: Perform migration logic here
|
||||||
|
|
||||||
|
// Update the version in the settings table
|
||||||
|
await utils.saveSetting(c, CONSTANTS.DB_VERSION_KEY, CONSTANTS.DB_VERSION);
|
||||||
|
return c.json({
|
||||||
|
success: true,
|
||||||
|
message: "Database migrated"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return c.json({
|
||||||
|
success: true,
|
||||||
|
message: "Database does not need migration"
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getVersion: async (c: Context<HonoCustomType>) => {
|
||||||
|
const version = await utils.getSetting(c, CONSTANTS.DB_VERSION_KEY);
|
||||||
|
return c.json({
|
||||||
|
need_initialization: !version,
|
||||||
|
need_migration: version && version != CONSTANTS.DB_VERSION,
|
||||||
|
current_db_version: version,
|
||||||
|
code_db_version: CONSTANTS.DB_VERSION
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ import oauth2_settings from './oauth2_settings'
|
|||||||
import worker_config from './worker_config'
|
import worker_config from './worker_config'
|
||||||
import admin_mail_api from './admin_mail_api'
|
import admin_mail_api from './admin_mail_api'
|
||||||
import { sendMailbyAdmin } from './send_mail'
|
import { sendMailbyAdmin } from './send_mail'
|
||||||
|
import db_api from './db_api'
|
||||||
|
|
||||||
export const api = new Hono<HonoCustomType>()
|
export const api = new Hono<HonoCustomType>()
|
||||||
|
|
||||||
@@ -304,3 +305,8 @@ api.get("/admin/worker/configs", worker_config.getConfig);
|
|||||||
|
|
||||||
// send mail by admin
|
// send mail by admin
|
||||||
api.post("/admin/send_mail", sendMailbyAdmin);
|
api.post("/admin/send_mail", sendMailbyAdmin);
|
||||||
|
|
||||||
|
// db api
|
||||||
|
api.get('admin/db_version', db_api.getVersion);
|
||||||
|
api.post('admin/db_initialize', db_api.initialize);
|
||||||
|
api.post('admin/db_migration', db_api.migrate);
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
export const CONSTANTS = {
|
export const CONSTANTS = {
|
||||||
VERSION: 'v' + '0.10.0',
|
VERSION: 'v' + '0.10.0',
|
||||||
|
|
||||||
|
// DB Version
|
||||||
|
DB_VERSION_KEY: 'db_version',
|
||||||
|
DB_VERSION: "v0.0.1",
|
||||||
|
|
||||||
// DB settings
|
// DB settings
|
||||||
ADDRESS_BLOCK_LIST_KEY: 'address_block_list',
|
ADDRESS_BLOCK_LIST_KEY: 'address_block_list',
|
||||||
SEND_BLOCK_LIST_KEY: 'send_block_list',
|
SEND_BLOCK_LIST_KEY: 'send_block_list',
|
||||||
|
|||||||
@@ -101,7 +101,10 @@ export function newTelegramBot(c: Context<HonoCustomType>, token: string): Teleg
|
|||||||
const res = await tgUserNewAddress(c, userId.toString(), address);
|
const res = await tgUserNewAddress(c, userId.toString(), address);
|
||||||
return await ctx.reply(`创建地址成功:\n`
|
return await ctx.reply(`创建地址成功:\n`
|
||||||
+ `地址: ${res.address}\n`
|
+ `地址: ${res.address}\n`
|
||||||
+ `凭证: ${res.jwt}\n`
|
+ `凭证: \`${res.jwt}\`\n`,
|
||||||
|
{
|
||||||
|
parse_mode: "Markdown"
|
||||||
|
}
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return await ctx.reply(`创建地址失败: ${(e as Error).message}`);
|
return await ctx.reply(`创建地址失败: ${(e as Error).message}`);
|
||||||
|
|||||||
Reference in New Issue
Block a user