docs(guide): 更新常见问题解答

- 新增 ping 域名的故障排查步骤
- 添加查看容器日志的方法
- 补充 IPv6网络配置的说明
pull/409/head
xiaojunnuo 2025-05-15 21:54:20 +08:00
parent d9a9f1c25c
commit 06f8514bc1
9 changed files with 85 additions and 80 deletions

View File

@ -18,6 +18,14 @@ services:
# - 8.8.4.4 # - 8.8.4.4
``` ```
如果仍然有问题,按如下步骤检查是否能够ping通域名
```shell
docker exec -it certd /bin/sh
ping www.baidu.com
ping gg.px.certd.handfree.work
ping app.handfree.work
```
## 2. 连接IPv6超时 ## 2. 连接IPv6超时
docker-compose 需要放开IPv6网络的配置 docker-compose 需要放开IPv6网络的配置
@ -44,3 +52,16 @@ networks:
## 4. 没有服务器配置文件,请检查是否开启了外网映射! ## 4. 没有服务器配置文件,请检查是否开启了外网映射!
宝塔网站证书部署报错:`Error: 没有服务器配置文件,请检查是否开启了外网映射!` 宝塔网站证书部署报错:`Error: 没有服务器配置文件,请检查是否开启了外网映射!`
解决方案:先手动在宝塔网站中设置一次证书 解决方案:先手动在宝塔网站中设置一次证书
## 5. 如何查看容器日志
```shell
docker logs -f --tail 200 certd
```

View File

@ -52,6 +52,10 @@ onErrorCaptured(e => {
onMounted(async () => { onMounted(async () => {
await settingStore.checkUrlBound(); await settingStore.checkUrlBound();
}); });
function goGithub() {
window.open("https://github.com/certd/certd");
}
</script> </script>
<template> <template>
@ -63,14 +67,14 @@ onMounted(async () => {
<LockScreen :avatar @to-login="handleLogout" /> <LockScreen :avatar @to-login="handleLogout" />
</template> </template>
<template #header-right-0> <template #header-right-0>
<div class="hover:bg-accent ml-1 mr-2 cursor-pointer rounded-full hidden md:block"> <div v-if="!settingStore.isComm" class="hover:bg-accent ml-1 mr-2 cursor-pointer rounded-full hidden md:block">
<tutorial-button v-if="!settingStore.isComm" class="flex-center header-btn" /> <tutorial-button class="flex-center header-btn" />
</div> </div>
<div class="hover:bg-accent ml-1 mr-2 cursor-pointer rounded-full"> <div class="hover:bg-accent ml-1 mr-2 cursor-pointer rounded-full">
<vip-button class="flex-center header-btn" mode="nav" /> <vip-button class="flex-center header-btn" mode="nav" />
</div> </div>
<div class="hover:bg-accent ml-1 mr-2 cursor-pointer rounded-full"> <div v-if="!settingStore.isComm" class="hover:bg-accent ml-1 mr-2 cursor-pointer rounded-full">
<fs-icon icon="ion:logo-github" /> <fs-button shape="circle" type="text" icon="ion:logo-github" :text="null" @click="goGithub" />
</div> </div>
</template> </template>
<template #footer> <template #footer>

View File

@ -18,11 +18,11 @@ interface Props {
} }
defineOptions({ defineOptions({
name: "LayoutHeader" name: "LayoutHeader",
}); });
withDefaults(defineProps<Props>(), { withDefaults(defineProps<Props>(), {
theme: "light" theme: "light",
}); });
const emit = defineEmits<{ clearPreferencesAndLogout: [] }>(); const emit = defineEmits<{ clearPreferencesAndLogout: [] }>();
@ -39,42 +39,42 @@ const rightSlots = computed(() => {
if (preferences.widget.globalSearch) { if (preferences.widget.globalSearch) {
list.push({ list.push({
index: REFERENCE_VALUE, index: REFERENCE_VALUE,
name: "global-search" name: "global-search",
}); });
} }
if (preferencesButtonPosition.value.header) { if (preferencesButtonPosition.value.header) {
list.push({ list.push({
index: REFERENCE_VALUE + 10, index: REFERENCE_VALUE + 10,
name: "preferences" name: "preferences",
}); });
} }
if (preferences.widget.themeToggle) { if (preferences.widget.themeToggle) {
list.push({ list.push({
index: REFERENCE_VALUE + 20, index: REFERENCE_VALUE + 20,
name: "theme-toggle" name: "theme-toggle",
}); });
} }
if (preferences.widget.languageToggle) { if (preferences.widget.languageToggle) {
list.push({ list.push({
index: REFERENCE_VALUE + 30, index: REFERENCE_VALUE + 30,
name: "language-toggle" name: "language-toggle",
}); });
} }
if (preferences.widget.fullscreen) { if (preferences.widget.fullscreen) {
list.push({ list.push({
index: REFERENCE_VALUE + 40, index: REFERENCE_VALUE + 40,
name: "fullscreen" name: "fullscreen",
}); });
} }
if (preferences.widget.notification) { if (preferences.widget.notification) {
list.push({ list.push({
index: REFERENCE_VALUE + 50, index: REFERENCE_VALUE + 50,
name: "notification" name: "notification",
}); });
} }
Object.keys(slots).forEach((key) => { Object.keys(slots).forEach(key => {
const name = key.split("-"); const name = key.split("-");
if (key.startsWith("header-right")) { if (key.startsWith("header-right")) {
list.push({ index: Number(name[2]), name: key }); list.push({ index: Number(name[2]), name: key });
@ -89,11 +89,11 @@ const leftSlots = computed(() => {
if (preferences.widget.refresh) { if (preferences.widget.refresh) {
list.push({ list.push({
index: 0, index: 0,
name: "refresh" name: "refresh",
}); });
} }
Object.keys(slots).forEach((key) => { Object.keys(slots).forEach(key => {
const name = key.split("-"); const name = key.split("-");
if (key.startsWith("header-left")) { if (key.startsWith("header-left")) {
list.push({ index: Number(name[2]), name: key }); list.push({ index: Number(name[2]), name: key });
@ -108,7 +108,7 @@ function clearPreferencesAndLogout() {
</script> </script>
<template> <template>
<template v-for="slot in leftSlots.filter((item) => item.index < REFERENCE_VALUE)" :key="slot.name"> <template v-for="slot in leftSlots.filter(item => item.index < REFERENCE_VALUE)" :key="slot.name">
<slot :name="slot.name"> <slot :name="slot.name">
<template v-if="slot.name === 'refresh'"> <template v-if="slot.name === 'refresh'">
<VbenIconButton class="my-0 mr-1 rounded-md" @click="refresh"> <VbenIconButton class="my-0 mr-1 rounded-md" @click="refresh">
@ -120,7 +120,7 @@ function clearPreferencesAndLogout() {
<div class="flex-center hidden lg:block"> <div class="flex-center hidden lg:block">
<slot name="breadcrumb"></slot> <slot name="breadcrumb"></slot>
</div> </div>
<template v-for="slot in leftSlots.filter((item) => item.index > REFERENCE_VALUE)" :key="slot.name"> <template v-for="slot in leftSlots.filter(item => item.index > REFERENCE_VALUE)" :key="slot.name">
<slot :name="slot.name"></slot> <slot :name="slot.name"></slot>
</template> </template>
<div :class="`menu-align-${preferences.header.menuAlign}`" class="flex h-full min-w-0 flex-1 items-center"> <div :class="`menu-align-${preferences.header.menuAlign}`" class="flex h-full min-w-0 flex-1 items-center">

View File

@ -9,15 +9,15 @@ import { preferences, updatePreferences } from "/@/vben/preferences";
import { VbenDropdownRadioMenu, VbenIconButton } from "/@/vben//shadcn-ui"; import { VbenDropdownRadioMenu, VbenIconButton } from "/@/vben//shadcn-ui";
defineOptions({ defineOptions({
name: "LanguageToggle" name: "LanguageToggle",
}); });
async function handleUpdate(value: string) { async function handleUpdate(value: string) {
const locale = value as SupportedLanguagesType; const locale = value as SupportedLanguagesType;
updatePreferences({ updatePreferences({
app: { app: {
locale locale,
} },
}); });
await loadLocaleMessages(locale); await loadLocaleMessages(locale);
} }

View File

@ -11,11 +11,11 @@ interface Props {
} }
defineOptions({ defineOptions({
name: "ThemeToggleButton" name: "ThemeToggleButton",
}); });
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
type: "normal" type: "normal",
}); });
const isDark = defineModel<boolean>(); const isDark = defineModel<boolean>();
@ -29,13 +29,13 @@ const bindProps = computed(() => {
return type === "normal" return type === "normal"
? { ? {
variant: "heavy" as const variant: "heavy" as const,
} }
: { : {
class: "rounded-full", class: "rounded-full",
size: "icon" as const, size: "icon" as const,
style: { padding: "7px" }, style: { padding: "7px" },
variant: "icon" as const variant: "icon" as const,
}; };
}); });
@ -59,12 +59,12 @@ function toggleTheme(event: MouseEvent) {
const clipPath = [`circle(0px at ${x}px ${y}px)`, `circle(${endRadius}px at ${x}px ${y}px)`]; const clipPath = [`circle(0px at ${x}px ${y}px)`, `circle(${endRadius}px at ${x}px ${y}px)`];
document.documentElement.animate( document.documentElement.animate(
{ {
clipPath: isDark.value ? [...clipPath].reverse() : clipPath clipPath: isDark.value ? [...clipPath].reverse() : clipPath,
}, },
{ {
duration: 450, duration: 450,
easing: "ease-in", easing: "ease-in",
pseudoElement: isDark.value ? "::view-transition-old(root)" : "::view-transition-new(root)" pseudoElement: isDark.value ? "::view-transition-old(root)" : "::view-transition-new(root)",
} }
); );
}); });

View File

@ -10,16 +10,16 @@ import { ToggleGroup, ToggleGroupItem, VbenTooltip } from "/@/vben//shadcn-ui";
import ThemeButton from "./theme-button.vue"; import ThemeButton from "./theme-button.vue";
defineOptions({ defineOptions({
name: "ThemeToggle" name: "ThemeToggle",
}); });
withDefaults(defineProps<{ shouldOnHover?: boolean }>(), { withDefaults(defineProps<{ shouldOnHover?: boolean }>(), {
shouldOnHover: false shouldOnHover: false,
}); });
function handleChange(isDark: boolean) { function handleChange(isDark: boolean) {
updatePreferences({ updatePreferences({
theme: { mode: isDark ? "dark" : "light" } theme: { mode: isDark ? "dark" : "light" },
}); });
} }
@ -29,18 +29,18 @@ const PRESETS = [
{ {
icon: Sun, icon: Sun,
name: "light", name: "light",
title: $t("preferences.theme.light") title: $t("preferences.theme.light"),
}, },
{ {
icon: MoonStar, icon: MoonStar,
name: "dark", name: "dark",
title: $t("preferences.theme.dark") title: $t("preferences.theme.dark"),
}, },
{ {
icon: SunMoon, icon: SunMoon,
name: "auto", name: "auto",
title: $t("preferences.followSystem") title: $t("preferences.followSystem"),
} },
]; ];
</script> </script>
<template> <template>
@ -49,7 +49,7 @@ const PRESETS = [
<template #trigger> <template #trigger>
<ThemeButton :model-value="isDark" type="icon" @update:model-value="handleChange" /> <ThemeButton :model-value="isDark" type="icon" @update:model-value="handleChange" />
</template> </template>
<ToggleGroup :model-value="preferences.theme.mode" class="gap-2" type="single" variant="outline" @update:model-value="(val) => updatePreferences({ theme: { mode: val as ThemeModeType } })"> <ToggleGroup :model-value="preferences.theme.mode" class="gap-2" type="single" variant="outline" @update:model-value="val => updatePreferences({ theme: { mode: val as ThemeModeType } })">
<ToggleGroupItem v-for="item in PRESETS" :key="item.name" :value="item.name"> <ToggleGroupItem v-for="item in PRESETS" :key="item.name" :value="item.name">
<component :is="item.icon" class="size-5" /> <component :is="item.icon" class="size-5" />
</ToggleGroupItem> </ToggleGroupItem>

View File

@ -18,7 +18,7 @@ const props = withDefaults(defineProps<Props>(), {
disabled: false, disabled: false,
loading: false, loading: false,
size: "default", size: "default",
variant: "default" variant: "default",
}); });
const isDisabled = computed(() => { const isDisabled = computed(() => {

View File

@ -1,13 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import type { ButtonVariants } from '../../ui'; import type { ButtonVariants } from "../../ui";
import type { VbenButtonProps } from './button'; import type { VbenButtonProps } from "./button";
import { computed, useSlots } from 'vue'; import { computed, useSlots } from "vue";
import { cn } from '/@/vben/shared/utils'; import { cn } from "/@/vben/shared/utils";
import { VbenTooltip } from '../tooltip'; import { VbenTooltip } from "../tooltip";
import VbenButton from './button.vue'; import VbenButton from "./button.vue";
interface Props extends VbenButtonProps { interface Props extends VbenButtonProps {
class?: any; class?: any;
@ -15,7 +15,7 @@ interface Props extends VbenButtonProps {
onClick?: () => void; onClick?: () => void;
tooltip?: string; tooltip?: string;
tooltipDelayDuration?: number; tooltipDelayDuration?: number;
tooltipSide?: 'bottom' | 'left' | 'right' | 'top'; tooltipSide?: "bottom" | "left" | "right" | "top";
variant?: ButtonVariants; variant?: ButtonVariants;
} }
@ -23,8 +23,8 @@ const props = withDefaults(defineProps<Props>(), {
disabled: false, disabled: false,
onClick: () => {}, onClick: () => {},
tooltipDelayDuration: 200, tooltipDelayDuration: 200,
tooltipSide: 'bottom', tooltipSide: "bottom",
variant: 'icon', variant: "icon",
}); });
const slots = useSlots(); const slots = useSlots();
@ -33,30 +33,13 @@ const showTooltip = computed(() => !!slots.tooltip || !!props.tooltip);
</script> </script>
<template> <template>
<VbenButton <VbenButton v-if="!showTooltip" :class="cn('rounded-full', props.class)" :disabled="disabled" :variant="variant" size="icon" @click="onClick">
v-if="!showTooltip"
:class="cn('rounded-full', props.class)"
:disabled="disabled"
:variant="variant"
size="icon"
@click="onClick"
>
<slot></slot> <slot></slot>
</VbenButton> </VbenButton>
<VbenTooltip <VbenTooltip v-else :delay-duration="tooltipDelayDuration" :side="tooltipSide">
v-else
:delay-duration="tooltipDelayDuration"
:side="tooltipSide"
>
<template #trigger> <template #trigger>
<VbenButton <VbenButton :class="cn('rounded-full', props.class)" :disabled="disabled" :variant="variant" size="icon" @click="onClick">
:class="cn('rounded-full', props.class)"
:disabled="disabled"
:variant="variant"
size="icon"
@click="onClick"
>
<slot></slot> <slot></slot>
</VbenButton> </VbenButton>
</template> </template>

View File

@ -254,9 +254,6 @@ export class PluginService extends BaseService<PluginEntity> {
}).constructor; }).constructor;
// const script = await this.compile(plugin.content); // const script = await this.compile(plugin.content);
const script = plugin.content; const script = plugin.content;
const {MaoyunClient} = await import("@certd/plugin-plus")
const a :any ={}
new MaoyunClient(a)
const getPluginClass = new AsyncFunction(script); const getPluginClass = new AsyncFunction(script);
return await getPluginClass({logger: logger}); return await getPluginClass({logger: logger});
} catch (e) { } catch (e) {