Skip to content

Commit dac8102

Browse files
committed
feat(siyuan): add clear all shares functionality
- Add clear all shares feature to remove all shared documents - Implement clearSharePages function in useStaticShare.ts - Update Share.vue to include clear all shares button and confirmation- Add relevant translations in en_US.json and zh_CN.json - Improve static assets validation for Windows filenames - Update nuxt.config.ts for SPA mode and routing
1 parent 0352a31 commit dac8102

File tree

6 files changed

+138
-31
lines changed

6 files changed

+138
-31
lines changed

apps/app/nuxt.config.ts

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const generateDynamicV = () => {
1313
}
1414

1515
const isDev = process.env.NODE_ENV === "development"
16-
const appBase = "/"
16+
const appBase = "/plugins/siyuan-blog/app/"
1717
const staticV = generateDynamicV()
1818

1919
// https://nuxt.com/docs/api/configuration/nuxt-config
@@ -30,6 +30,37 @@ export default defineNuxtConfig({
3030
vueI18n: "./i18n.ts"
3131
},
3232

33+
// https://nuxt.com/docs/guide/going-further/custom-routing#hash-mode-spa
34+
ssr: false,
35+
router: {
36+
options: {
37+
hashMode: true,
38+
},
39+
},
40+
41+
vite: {
42+
define: {
43+
"process.env.DEV_MODE": `"${isDev}"`,
44+
"process.env.APP_BASE": `"${appBase}"`,
45+
"process.env.SSR": "\"false\"",
46+
},
47+
plugins: [
48+
AutoImport({
49+
resolvers: [ElementPlusResolver()],
50+
}),
51+
Components({
52+
resolvers: [ElementPlusResolver()],
53+
}),
54+
]
55+
},
56+
57+
css: ["~/assets/css/index.styl"],
58+
59+
elementPlus: {
60+
/** Options */
61+
themes: ["dark"],
62+
},
63+
3364
app: {
3465
baseURL: appBase,
3566
head: {
@@ -85,36 +116,13 @@ export default defineNuxtConfig({
85116
},
86117
},
87118

88-
vite: {
89-
define: {
90-
"process.env.DEV_MODE": `"${isDev}"`,
91-
"process.env.APP_BASE": `"${appBase}"`,
92-
"process.env.SSR": "\"true\"",
93-
},
94-
plugins: [
95-
AutoImport({
96-
resolvers: [ElementPlusResolver()],
97-
}),
98-
Components({
99-
resolvers: [ElementPlusResolver()],
100-
}),
101-
]
102-
},
103-
104-
css: ["~/assets/css/index.styl"],
105-
106-
elementPlus: {
107-
/** Options */
108-
themes: ["dark"],
109-
},
110-
111119
// 环境变量
112120
runtimeConfig: {
113121
public: {
114-
defaultType: process.env.NUXT_PUBLIC_DEFAULT_TYPE ?? "node",
115-
siyuanApiUrl: process.env.NUXT_PUBLIC_SIYUAN_API_URL ?? "http://127.0.0.1:6806",
116-
providerMode: process.env.NUXT_PUBLIC_PROVIDER_MODE ?? "false",
117-
providerUrl: process.env.NUXT_PUBLIC_PROVIDER_URL ?? "http://127.0.0.1:8086",
122+
defaultType: "siyuan",
123+
siyuanApiUrl: "",
124+
providerMode: "false",
125+
providerUrl: "",
118126
},
119127
},
120128

apps/siyuan/src/composables/useStaticAssets.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,18 @@ export const useStaticAssets = () => {
2626
const src = image.attribs["src"]
2727
const imageUrl = [baseUrl, src].join("/")
2828
const toFilepath = [saveFolder, src].join("/")
29+
30+
// 验证文件名
31+
if (!isValidWindowsFilename(src)) {
32+
logger.warn(`Invalid filename detected: ${src}, skipping...`)
33+
continue
34+
}
35+
36+
// 跳过网络图片
37+
if (src.startsWith("http")) {
38+
continue
39+
}
40+
2941
logger.info(`download image ${imageUrl} to ${toFilepath}`)
3042

3143
// eslint-disable-next-line no-await-in-loop
@@ -38,6 +50,38 @@ export const useStaticAssets = () => {
3850
}
3951
}
4052

53+
// 验证 Windows 文件名是否合法
54+
const isValidWindowsFilename = (filename: string): boolean => {
55+
// 检查长度
56+
if (filename.length > 255) {
57+
return false
58+
}
59+
60+
// 检查非法字符
61+
const invalidChars = /[:*?"<>|]/
62+
if (invalidChars.test(filename)) {
63+
return false
64+
}
65+
66+
// 检查是否以点号结尾
67+
if (filename.endsWith(".")) {
68+
return false
69+
}
70+
71+
// 检查保留的设备名称
72+
const reservedNames = [
73+
"CON", "PRN", "AUX", "NUL",
74+
"COM1", "COM2", "COM3", "COM4",
75+
"LPT1", "LPT2", "LPT3", "LPT4"
76+
]
77+
const nameWithoutExt = filename.split(".")[0].toUpperCase()
78+
if (reservedNames.includes(nameWithoutExt)) {
79+
return false
80+
}
81+
82+
return true
83+
}
84+
4185
// =========================================================================
4286
const getImageBlob = async (url: string) => {
4387
try {

apps/siyuan/src/composables/useStaticShare.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,5 +82,15 @@ export const useStaticShare = () => {
8282
await removeSharePage(pageId)
8383
}
8484

85-
return {openStaticShare, closeStaticShare, updateStaticShare}
85+
/**
86+
* 清除所有静态分享页面
87+
* 用于修复和清理所有分享数据
88+
*/
89+
const clearSharePages = async () => {
90+
const publicBlogFolder = "/data/public/siyuan-blog"
91+
await kernelApi.removeFile(publicBlogFolder)
92+
logger.info("all static share data cleared success")
93+
}
94+
95+
return {openStaticShare, closeStaticShare, updateStaticShare, clearSharePages}
8696
}

apps/siyuan/src/i18n/en_US.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@
2828
"share.link.expires.time.placeholder": "Units/sec, up to 7 days. 0 means permanent",
2929
"share.set.home": "Set home",
3030
"share.click.view": "Click to view",
31+
"share.clear.all": "Clear all shares",
32+
"share.clear.all.tip": "This operation will clear all shared documents, please be careful",
33+
"share.clear.all.warning": "This operation will clear all shared documents and cannot be undone, continue?",
34+
"share.clear.all.success": "Successfully cleared all shared documents",
3135
"share.setting.basic": "Basic setting",
3236
"share.setting.advanced": "Preference setting",
3337
"main.label.siteUrl": "Custom Domain",

apps/siyuan/src/i18n/zh_CN.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@
2828
"share.link.expires.time.placeholder": "单位/秒,最多支持7天。0表示永久生效",
2929
"share.set.home": "设为主页",
3030
"share.click.view": "点击查看",
31+
"share.clear.all": "清除所有分享",
32+
"share.clear.all.tip": "此操作将清除所有已分享的文档,请谨慎操作",
33+
"share.clear.all.warning": "此操作将清除所有已分享的文档,且不可恢复,是否继续?",
34+
"share.clear.all.success": "已成功清除所有分享的文档",
3135
"share.setting.basic": "基本设置",
3236
"share.setting.advanced": "偏好设置",
3337
"main.label.siteUrl": "自定义域名",

apps/siyuan/src/pages/Share.vue

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import {onBeforeMount, reactive, toRaw} from "vue"
1212
import copy from "copy-to-clipboard"
1313
import {JsonUtil, StrUtil} from "zhi-common"
14-
import {showMessage} from "siyuan"
14+
import {showMessage,confirm} from "siyuan"
1515
import {createAppLogger} from "../utils/appLogger.ts"
1616
import {useSiyuanApi} from "../composables/useSiyuanApi.ts"
1717
import {getAllIps} from "../utils/urlUtil.ts"
@@ -31,7 +31,7 @@ const {getSetting, updateSetting} = useSettingStore()
3131
const {blogApi, kernelApi} = useSiyuanApi()
3232
const {handleMethod} = useMethod(props.pluginInstance)
3333
const {handleMethodAsync} = useMethodAsync(props.pluginInstance)
34-
const {openStaticShare, closeStaticShare, updateStaticShare} = useStaticShare()
34+
const {openStaticShare, closeStaticShare, updateStaticShare, clearSharePages} = useStaticShare()
3535
3636
const basePath = "/plugins/siyuan-blog/app/#"
3737
// datas
@@ -201,6 +201,21 @@ const handleExpiresTime = async () => {
201201
)
202202
}
203203
204+
const handleClearAllShares = async (event: Event) => {
205+
// 阻止事件冒泡
206+
event.stopPropagation()
207+
// 清除所有分享
208+
await clearSharePages()
209+
// 更新当前页面的分享状态
210+
formData.shared = false
211+
await kernelApi.setBlockAttrs(props.id, {
212+
"custom-publish-status": "draft",
213+
"custom-publish-time": "",
214+
})
215+
// 操作完成后再显示成功提示
216+
showMessage(props.pluginInstance.i18n["share.clear.all.success"])
217+
}
218+
204219
onBeforeMount(async () => {
205220
formData.setting = await getSetting()
206221
formData.post = await blogApi.getPost(props.id, false, false)
@@ -284,6 +299,18 @@ logger.debug("share inited", props)
284299
<input class="b3-switch fn__flex-center" type="checkbox" v-model="formData.isHome" @change="handleSetHome">
285300
</div>
286301
</div>
302+
303+
<div class="divider"/>
304+
305+
<div class="setting-row">
306+
<span class="setting-label">
307+
{{ pluginInstance.i18n["share.clear.all"] }}
308+
</span>
309+
<div class="input-group">
310+
<div class="danger-text">{{ pluginInstance.i18n["share.clear.all.tip"] }}</div>
311+
<button class="danger-button" @click="handleClearAllShares">{{ pluginInstance.i18n["share.clear.all"] }}</button>
312+
</div>
313+
</div>
287314
</div>
288315
</template>
289316

@@ -458,4 +485,14 @@ html[data-theme-mode="dark"]
458485
459486
.info-text
460487
color #ff7875
488+
489+
.danger-button
490+
background-color #ff4d4f
491+
&:hover
492+
background-color #ff7875
493+
494+
.danger-text
495+
color #ff4d4f
496+
font-size 12px
497+
margin-right 8px
461498
</style>

0 commit comments

Comments
 (0)