mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-05-06 20:32:55 +08:00
feat: add auth mode and refactor frontend (#21)
This commit is contained in:
@@ -106,6 +106,8 @@ node_compat = true
|
||||
|
||||
[vars]
|
||||
PREFIX = "tmp" # 要处理的邮箱名称前缀
|
||||
# 如果你想要你的网站私有,取消下面的注释,并修改密码
|
||||
# PASSWORDS = ["123", "456"]
|
||||
DOMAINS = ["xxx.xxx1" , "xxx.xxx2"] # 你的域名
|
||||
JWT_SECRET = "xxx" # 用于生成 jwt 的密钥
|
||||
BLACK_LIST = "" # 黑名单,用于过滤发件人,逗号分隔
|
||||
|
||||
@@ -41,6 +41,8 @@ pnpm install
|
||||
# copy wrangler.toml.template to wrangler.toml
|
||||
# and add your d1 config and these config
|
||||
# PREFIX = "tmp" - the email create will be like tmp<xxxxx>@DOMAIN
|
||||
# IF YOU WANT TO MAKE YOUR SITE PRIVATE, UNCOMMENT THE FOLLOWING LINES
|
||||
# PASSWORDS = ["123", "456"]
|
||||
# DOMAINS = ["xxx.xxx1" , "xxx.xxx2"] you domain name
|
||||
# JWT_SECRET = "xxx"
|
||||
# BLACK_LIST = ""
|
||||
|
||||
@@ -1,124 +1,59 @@
|
||||
<script setup>
|
||||
import { NMessageProvider, NGrid, NBackTop, NLayoutHeader, NInput } from 'naive-ui'
|
||||
import { NGi, NSpace, NButton, NConfigProvider, NSelect, NModal } from 'naive-ui'
|
||||
import { darkTheme, NSwitch, NGlobalStyle, NPopconfirm } from 'naive-ui'
|
||||
import { NMessageProvider, NGrid, NBackTop, NSpin } from 'naive-ui'
|
||||
import { NGi, NSpace, NButton, NConfigProvider } from 'naive-ui'
|
||||
import { darkTheme, NGlobalStyle } from 'naive-ui'
|
||||
import { zhCN } from 'naive-ui'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import { computed, onMounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import Content from './Content.vue'
|
||||
import Content from './views/Content.vue'
|
||||
import Header from './views/Header.vue'
|
||||
import { useGlobalState } from './store'
|
||||
|
||||
const jwt = useStorage('jwt')
|
||||
const localeCache = useStorage('locale', 'zhCN')
|
||||
const themeSwitch = useStorage('themeSwitch', false)
|
||||
const { localeCache, themeSwitch, loading } = useGlobalState()
|
||||
const theme = computed(() => themeSwitch.value ? darkTheme : null)
|
||||
const showLogin = ref(false)
|
||||
const password = ref('')
|
||||
const localeConfig = computed(() => localeCache.value == 'zh' ? zhCN : null)
|
||||
|
||||
const login = () => {
|
||||
jwt.value = password.value;
|
||||
location.reload()
|
||||
}
|
||||
const logout = () => {
|
||||
jwt.value = '';
|
||||
location.reload()
|
||||
}
|
||||
const changeLocale = (locale) => {
|
||||
localeCache.value = locale;
|
||||
location.reload()
|
||||
}
|
||||
const { t, locale } = useI18n({
|
||||
const { locale } = useI18n({
|
||||
useScope: 'global',
|
||||
locale: localeCache.value || 'zh',
|
||||
messages: {
|
||||
en: {
|
||||
title: 'Cloudflare Temp Email',
|
||||
dark: 'Dark',
|
||||
light: 'Light',
|
||||
login: 'Login',
|
||||
logout: 'Logout',
|
||||
logoutConfirm: 'Are you sure to logout?',
|
||||
},
|
||||
zh: {
|
||||
title: 'Cloudflare 临时邮件',
|
||||
dark: '暗色',
|
||||
light: '亮色',
|
||||
login: '登录',
|
||||
logout: '登出',
|
||||
logoutConfirm: '确定要登出吗?',
|
||||
}
|
||||
}
|
||||
});
|
||||
locale.value = localeCache.value;
|
||||
|
||||
onMounted(async () => {
|
||||
const token = import.meta.env.VITE_CF_WEB_ANALY_TOKEN;
|
||||
|
||||
const exist = document.querySelector('script[src="https://static.cloudflareinsights.com/beacon.min.js"]') !== null
|
||||
if (token && !exist) {
|
||||
const script = document.createElement('script');
|
||||
script.defer = true;
|
||||
script.src = 'https://static.cloudflareinsights.com/beacon.min.js';
|
||||
script.dataset.cfBeacon = `{ token: ${token} }`;
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-config-provider :locale="localeConfig" :theme="theme">
|
||||
<n-global-style />
|
||||
<n-message-provider>
|
||||
<n-grid x-gap="12" :cols="8">
|
||||
<n-gi span="1"></n-gi>
|
||||
<n-gi span="6">
|
||||
<div class="main">
|
||||
<n-space vertical>
|
||||
<n-layout-header>
|
||||
<div>
|
||||
<h2>{{ t('title') }}</h2>
|
||||
</div>
|
||||
<div>
|
||||
<n-button v-if="localeCache == 'zh'" @click="changeLocale('en')">English</n-button>
|
||||
<n-button v-else @click="changeLocale('zh')">中文</n-button>
|
||||
<n-switch v-model:value="themeSwitch">
|
||||
<template #checked>
|
||||
{{ t('dark') }}
|
||||
</template>
|
||||
<template #unchecked>
|
||||
{{ t('light') }}
|
||||
</template>
|
||||
</n-switch>
|
||||
<n-popconfirm v-if="jwt" @positive-click="logout">
|
||||
<template #trigger>
|
||||
<n-button tertiary round type="primary">
|
||||
{{ t('logout') }}
|
||||
</n-button>
|
||||
</template>
|
||||
<template #default>
|
||||
<span>
|
||||
{{ t('logoutConfirm') }}
|
||||
</span>
|
||||
</template>
|
||||
</n-popconfirm>
|
||||
<n-button v-else tertiary @click="showLogin = true" round type="primary">
|
||||
{{ t('login') }}
|
||||
</n-button>
|
||||
<n-button tag="a" target="_blank" tertiary type="primary" round
|
||||
href="https://github.com/dreamhunter2333/cloudflare_temp_email">Star on Github
|
||||
</n-button>
|
||||
</div>
|
||||
</n-layout-header>
|
||||
<Content />
|
||||
</n-space>
|
||||
</div>
|
||||
</n-gi>
|
||||
<n-gi span="1"></n-gi>
|
||||
</n-grid>
|
||||
<n-back-top :right="100" />
|
||||
<n-modal v-model:show="showLogin" preset="dialog" title="Dialog">
|
||||
<template #header>
|
||||
<div>{{ t('login') }}</div>
|
||||
</template>
|
||||
<n-input v-model:value="password" type="textarea" :autosize="{
|
||||
minRows: 3
|
||||
}" />
|
||||
<template #action>
|
||||
<n-button @click="login" size="small" tertiary round type="primary">
|
||||
{{ t('login') }}
|
||||
</n-button>
|
||||
</template>
|
||||
</n-modal>
|
||||
</n-message-provider>
|
||||
<n-spin description="loading..." :show="loading">
|
||||
<n-message-provider>
|
||||
<n-grid x-gap="12" :cols="8">
|
||||
<n-gi span="1"></n-gi>
|
||||
<n-gi span="6">
|
||||
<div class="main">
|
||||
<n-space vertical>
|
||||
<Header />
|
||||
<Content />
|
||||
</n-space>
|
||||
</div>
|
||||
</n-gi>
|
||||
<n-gi span="1"></n-gi>
|
||||
</n-grid>
|
||||
<n-back-top :right="100" />
|
||||
</n-message-provider>
|
||||
</n-spin>
|
||||
</n-config-provider>
|
||||
</template>
|
||||
|
||||
@@ -156,10 +91,4 @@ locale.value = localeCache.value;
|
||||
.n-space {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.n-layout-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
||||
|
||||
66
frontend/src/api/index.js
Normal file
66
frontend/src/api/index.js
Normal file
@@ -0,0 +1,66 @@
|
||||
import { useGlobalState } from '../store'
|
||||
|
||||
const API_BASE = import.meta.env.VITE_API_BASE || "";
|
||||
const { loading, auth, jwt, openSettings, showAuth } = useGlobalState();
|
||||
|
||||
const apiFetch = async (path, options = {}) => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}${path}`, {
|
||||
method: options.method || 'GET',
|
||||
headers: {
|
||||
'x-custom-auth': auth.value,
|
||||
'Authorization': `Bearer ${jwt.value}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
if (response.status === 401 && openSettings.value.auth) {
|
||||
showAuth.value = true;
|
||||
throw new Error("Unauthorized");
|
||||
}
|
||||
if (!response.ok) {
|
||||
throw new Error(`${response.status} ${await response.text()}` || "error");
|
||||
}
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const getOpenSettings = async (message) => {
|
||||
try {
|
||||
const res = await api.fetch("/open_api/settings");
|
||||
openSettings.value = {
|
||||
prefix: res["prefix"] || "",
|
||||
auth: res["auth"] || false,
|
||||
domains: res["domains"].map((domain) => {
|
||||
return {
|
||||
label: domain,
|
||||
value: domain
|
||||
}
|
||||
})
|
||||
};
|
||||
if (openSettings.value.auth && !auth.value) {
|
||||
showAuth.value = true;
|
||||
}
|
||||
} catch (error) {
|
||||
message.error(error.message || "error");
|
||||
}
|
||||
}
|
||||
|
||||
const getSettings = async () => {
|
||||
if (typeof jwt.value != 'string' || jwt.value.trim() === '' || jwt.value === 'undefined') {
|
||||
return "";
|
||||
}
|
||||
loading.value = true;
|
||||
const res = await apiFetch("/api/settings");;
|
||||
return res["address"];
|
||||
}
|
||||
|
||||
|
||||
export const api = {
|
||||
fetch: apiFetch,
|
||||
getSettings: getSettings,
|
||||
getOpenSettings: getOpenSettings,
|
||||
}
|
||||
30
frontend/src/store/index.js
Normal file
30
frontend/src/store/index.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import { ref } from "vue";
|
||||
import { createGlobalState, useStorage } from '@vueuse/core'
|
||||
|
||||
export const useGlobalState = createGlobalState(
|
||||
() => {
|
||||
const loading = ref(false);
|
||||
const openSettings = ref({
|
||||
prefix: '',
|
||||
auth: false,
|
||||
domains: [{
|
||||
label: 'test.com',
|
||||
value: 'test.com'
|
||||
}]
|
||||
})
|
||||
const showAuth = ref(false);
|
||||
const auth = useStorage('auth', '');
|
||||
const jwt = useStorage('jwt', '');
|
||||
const localeCache = useStorage('locale', 'zhCN');
|
||||
const themeSwitch = useStorage('themeSwitch', false);
|
||||
return {
|
||||
loading,
|
||||
openSettings,
|
||||
showAuth,
|
||||
auth,
|
||||
jwt,
|
||||
localeCache,
|
||||
themeSwitch
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -1,19 +1,19 @@
|
||||
<script setup>
|
||||
import { NSpace, NAlert, NSwitch, NCard, NInput, NInputGroupLabel } from 'naive-ui'
|
||||
import { NSpin, NButton, NLayout, NInputGroup, NModal, NSelect } from 'naive-ui'
|
||||
import { NList, NListItem, NThing, NTag, NNumberAnimation } from 'naive-ui'
|
||||
import { NButton, NLayout, NInputGroup, NModal, NSelect } from 'naive-ui'
|
||||
import { NList, NListItem, NThing, NTag } from 'naive-ui'
|
||||
import { watch, onMounted, ref } from "vue";
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import useClipboard from 'vue-clipboard3'
|
||||
import { useMessage } from 'naive-ui'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useGlobalState } from '../store'
|
||||
import { api } from '../api'
|
||||
|
||||
const { toClipboard } = useClipboard()
|
||||
const message = useMessage()
|
||||
|
||||
const jwt = useStorage('jwt')
|
||||
const address = ref("")
|
||||
const loading = ref(false)
|
||||
const { jwt, loading, openSettings } = useGlobalState()
|
||||
const autoRefresh = ref(false)
|
||||
const data = ref([])
|
||||
const API_BASE = import.meta.env.VITE_API_BASE || "";
|
||||
@@ -22,16 +22,8 @@ const showPassword = ref(false)
|
||||
const showNewEmail = ref(false)
|
||||
const emailName = ref("")
|
||||
const emailDomain = ref("")
|
||||
const openSettings = ref({
|
||||
prefix: 'test',
|
||||
domains: [{
|
||||
label: 'test.com',
|
||||
value: 'test.com'
|
||||
}]
|
||||
})
|
||||
|
||||
const { t, locale } = useI18n({
|
||||
useScope: 'global',
|
||||
const { t } = useI18n({
|
||||
locale: 'zh',
|
||||
messages: {
|
||||
en: {
|
||||
@@ -86,27 +78,11 @@ const refresh = async () => {
|
||||
if (typeof address.value != 'string' || address.value.trim() === '') {
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/api/mails`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": `Bearer ${jwt.value}`,
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
message.error(`${response.status} ${await response.text()}` || "error");
|
||||
throw new Error(`${response.status} ${await response.text()}` || "error");
|
||||
}
|
||||
let res = await response.json();
|
||||
data.value = res;
|
||||
data.value = await api.fetch("/api/mails");
|
||||
} catch (error) {
|
||||
message.error(error.message || "error");
|
||||
console.error(error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -121,117 +97,31 @@ const copy = async () => {
|
||||
|
||||
const newEmail = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
let url = `${API_BASE}/api/new_address`;
|
||||
url = `${url}?name=${emailName.value || ''}`;
|
||||
url = `${url}&domain=${emailDomain.value || ''}`;
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`${response.status} ${await response.text()}` || "error");
|
||||
}
|
||||
let res = await response.json();
|
||||
const res = await api.fetch(
|
||||
`/api/new_address`
|
||||
+ `?name=${emailName.value || ''}`
|
||||
+ `&domain=${emailDomain.value || ''}`
|
||||
);
|
||||
jwt.value = res["jwt"];
|
||||
address.value = await api.getSettings();
|
||||
await refresh();
|
||||
showNewEmail.value = false;
|
||||
showPassword.value = true;
|
||||
} catch (error) {
|
||||
message.error(error.message || "error");
|
||||
console.error(error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const getOpenSettings = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/open_api/settings`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
message.error(`${response.status} ${await response.text()}` || "error");
|
||||
return;
|
||||
}
|
||||
let res = await response.json();
|
||||
openSettings.value = {
|
||||
prefix: res["prefix"] || "",
|
||||
domains: res["domains"].map((domain) => {
|
||||
return {
|
||||
label: domain,
|
||||
value: domain
|
||||
}
|
||||
})
|
||||
};
|
||||
emailDomain.value = openSettings.value.domains[0].value;
|
||||
} catch (error) {
|
||||
message.error(error.message || "error");
|
||||
console.error(error);
|
||||
}
|
||||
finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const getSettings = async (jwt) => {
|
||||
if (typeof jwt != 'string' || jwt.trim() === '' || jwt === 'undefined') {
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/api/settings`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": `Bearer ${jwt}`,
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
message.error(`${response.status} ${await response.text()}` || "error");
|
||||
console.error(response);
|
||||
address.value = "";
|
||||
return;
|
||||
}
|
||||
let res = await response.json();
|
||||
address.value = res["address"];
|
||||
await refresh();
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
watch(jwt, async (jwt, old) => getSettings(jwt))
|
||||
|
||||
onMounted(async () => {
|
||||
getOpenSettings()
|
||||
getSettings(jwt.value)
|
||||
await api.getOpenSettings(message);
|
||||
emailDomain.value = openSettings.value.domains ? openSettings.value.domains[0].value : "";
|
||||
address.value = await api.getSettings();
|
||||
await refresh();
|
||||
const token = import.meta.env.VITE_CF_WEB_ANALY_TOKEN;
|
||||
|
||||
const exist = document.querySelector('script[src="https://static.cloudflareinsights.com/beacon.min.js"]') !== null
|
||||
if (token && !exist) {
|
||||
const script = document.createElement('script');
|
||||
script.defer = true;
|
||||
script.src = 'https://static.cloudflareinsights.com/beacon.min.js';
|
||||
script.dataset.cfBeacon = `{ token: ${token} }`;
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-spin description="loading..." :show="loading">
|
||||
<div>
|
||||
<n-layout>
|
||||
<n-alert :type='address ? "info" : "warning"' show-icon>
|
||||
<span v-if="address">
|
||||
@@ -316,7 +206,7 @@ onMounted(async () => {
|
||||
<template #action>
|
||||
</template>
|
||||
</n-modal>
|
||||
</n-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
141
frontend/src/views/Header.vue
Normal file
141
frontend/src/views/Header.vue
Normal file
@@ -0,0 +1,141 @@
|
||||
<script setup>
|
||||
import { NGrid, NLayoutHeader, NInput } from 'naive-ui'
|
||||
import { NButton, NSelect, NModal } from 'naive-ui'
|
||||
import { NSwitch, NPopconfirm } from 'naive-ui'
|
||||
import { ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { useGlobalState } from '../store'
|
||||
import { api } from '../api'
|
||||
|
||||
const { jwt, localeCache, themeSwitch, showAuth, auth } = useGlobalState()
|
||||
const showLogin = ref(false)
|
||||
const password = ref('')
|
||||
|
||||
const login = async () => {
|
||||
try {
|
||||
await api.getSettings()
|
||||
jwt.value = password.value;
|
||||
location.reload()
|
||||
} catch (error) {
|
||||
message.error(error.message || "error");
|
||||
}
|
||||
}
|
||||
|
||||
const logout = () => {
|
||||
jwt.value = '';
|
||||
location.reload()
|
||||
}
|
||||
|
||||
const authFunc = async () => {
|
||||
try {
|
||||
location.reload()
|
||||
} catch (error) {
|
||||
message.error(error.message || "error");
|
||||
}
|
||||
}
|
||||
|
||||
const changeLocale = (locale) => {
|
||||
localeCache.value = locale;
|
||||
location.reload()
|
||||
}
|
||||
|
||||
const { t } = useI18n({
|
||||
locale: localeCache.value || 'zh',
|
||||
messages: {
|
||||
en: {
|
||||
title: 'Cloudflare Temp Email',
|
||||
dark: 'Dark',
|
||||
light: 'Light',
|
||||
login: 'Login',
|
||||
logout: 'Logout',
|
||||
logoutConfirm: 'Are you sure to logout?',
|
||||
auth: 'Auth',
|
||||
authTip: 'Please enter the correct auth code',
|
||||
},
|
||||
zh: {
|
||||
title: 'Cloudflare 临时邮件',
|
||||
dark: '暗色',
|
||||
light: '亮色',
|
||||
login: '登录',
|
||||
logout: '登出',
|
||||
logoutConfirm: '确定要登出吗?',
|
||||
auth: '授权',
|
||||
authTip: '请输入正确的授权码',
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-layout-header>
|
||||
<div>
|
||||
<h2>{{ t('title') }}</h2>
|
||||
</div>
|
||||
<div>
|
||||
<n-button v-if="localeCache == 'zh'" @click="changeLocale('en')">English</n-button>
|
||||
<n-button v-else @click="changeLocale('zh')">中文</n-button>
|
||||
<n-switch v-model:value="themeSwitch">
|
||||
<template #checked>
|
||||
{{ t('dark') }}
|
||||
</template>
|
||||
<template #unchecked>
|
||||
{{ t('light') }}
|
||||
</template>
|
||||
</n-switch>
|
||||
<n-popconfirm v-if="jwt" @positive-click="logout">
|
||||
<template #trigger>
|
||||
<n-button tertiary round type="primary">
|
||||
{{ t('logout') }}
|
||||
</n-button>
|
||||
</template>
|
||||
<template #default>
|
||||
<span>
|
||||
{{ t('logoutConfirm') }}
|
||||
</span>
|
||||
</template>
|
||||
</n-popconfirm>
|
||||
<n-button v-else tertiary @click="showLogin = true" round type="primary">
|
||||
{{ t('login') }}
|
||||
</n-button>
|
||||
<n-button tag="a" target="_blank" tertiary type="primary" round
|
||||
href="https://github.com/dreamhunter2333/cloudflare_temp_email">Star on Github
|
||||
</n-button>
|
||||
</div>
|
||||
<n-modal v-model:show="showLogin" preset="dialog" title="Dialog">
|
||||
<template #header>
|
||||
<div>{{ t('login') }}</div>
|
||||
</template>
|
||||
<n-input v-model:value="password" type="textarea" :autosize="{
|
||||
minRows: 3
|
||||
}" />
|
||||
<template #action>
|
||||
<n-button @click="login" size="small" tertiary round type="primary">
|
||||
{{ t('login') }}
|
||||
</n-button>
|
||||
</template>
|
||||
</n-modal>
|
||||
<n-modal v-model:show="showAuth" preset="dialog" title="Dialog">
|
||||
<template #header>
|
||||
<div>{{ t('auth') }}</div>
|
||||
</template>
|
||||
<p>{{ t('authTip') }}</p>
|
||||
<n-input v-model:value="auth" type="textarea" :autosize="{
|
||||
minRows: 3
|
||||
}" />
|
||||
<template #action>
|
||||
<n-button @click="authFunc" size="small" tertiary round type="primary">
|
||||
{{ t('auth') }}
|
||||
</n-button>
|
||||
</template>
|
||||
</n-modal>
|
||||
</n-layout-header>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.n-layout-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
||||
@@ -22,6 +22,9 @@ api.get('/open_api/settings', async (c) => {
|
||||
return c.json({
|
||||
"prefix": c.env.PREFIX,
|
||||
"domains": c.env.DOMAINS,
|
||||
"auth": (
|
||||
c.env.PASSWORDS && c.env.PASSWORDS.length > 0
|
||||
) ? true : false,
|
||||
});
|
||||
})
|
||||
|
||||
|
||||
@@ -8,6 +8,13 @@ import { email } from './email';
|
||||
const app = new Hono()
|
||||
app.use('/*', cors());
|
||||
app.use('/api/*', async (c, next) => {
|
||||
// check header x-custom-auth
|
||||
if (c.env.PASSWORDS && c.env.PASSWORDS.length > 0) {
|
||||
const auth = c.req.headers.get("x-custom-auth");
|
||||
if (!auth || !c.env.PASSWORDS.includes(auth)) {
|
||||
return c.text("Need Password", 401)
|
||||
}
|
||||
}
|
||||
if (c.req.path.startsWith("/api/new_address")) {
|
||||
await next();
|
||||
return;
|
||||
|
||||
@@ -5,6 +5,8 @@ node_compat = true
|
||||
|
||||
[vars]
|
||||
PREFIX = "tmp"
|
||||
# IF YOU WANT TO MAKE YOUR SITE PRIVATE, UNCOMMENT THE FOLLOWING LINES
|
||||
# PASSWORDS = ["123", "456"]
|
||||
DOMAINS = ["xxx.xxx1" , "xxx.xxx2"]
|
||||
JWT_SECRET = "xxx"
|
||||
BLACK_LIST = ""
|
||||
|
||||
Reference in New Issue
Block a user