feat: 利用AI更新文档

This commit is contained in:
Lan
2025-12-01 14:10:06 +08:00
parent d7200f6b9b
commit a20a0beb79
20 changed files with 4598 additions and 313 deletions
+2
View File
@@ -160,3 +160,5 @@ node_modules/
AGENTS.md AGENTS.md
dist/
-1
View File
@@ -35,7 +35,6 @@ DEFAULT_CONFIG = {
"onedrive_password": "", "onedrive_password": "",
"onedrive_root_path": "filebox_storage", "onedrive_root_path": "filebox_storage",
"onedrive_proxy": 0, "onedrive_proxy": 0,
"webdav_hostname": "",
"webdav_root_path": "filebox_storage", "webdav_root_path": "filebox_storage",
"webdav_proxy": 0, "webdav_proxy": 0,
"admin_token": "FileCodeBox2023", "admin_token": "FileCodeBox2023",
-275
View File
@@ -1,275 +0,0 @@
import {
useMediaQuery
} from "./chunk-KT7LHMJ2.js";
import {
computed,
ref,
shallowRef,
watch
} from "./chunk-CQOUZRMK.js";
// node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/index.js
import "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/styles/fonts.css";
// node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/without-fonts.js
import "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/styles/vars.css";
import "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/styles/base.css";
import "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/styles/icons.css";
import "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/styles/utils.css";
import "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/styles/components/custom-block.css";
import "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/styles/components/vp-code.css";
import "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/styles/components/vp-code-group.css";
import "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/styles/components/vp-doc.css";
import "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/styles/components/vp-sponsor.css";
import VPBadge from "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/components/VPBadge.vue";
import Layout from "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/Layout.vue";
import { default as default2 } from "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/components/VPBadge.vue";
import { default as default3 } from "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/components/VPButton.vue";
import { default as default4 } from "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/components/VPDocAsideSponsors.vue";
import { default as default5 } from "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/components/VPFeatures.vue";
import { default as default6 } from "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/components/VPHomeContent.vue";
import { default as default7 } from "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/components/VPHomeFeatures.vue";
import { default as default8 } from "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/components/VPHomeHero.vue";
import { default as default9 } from "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/components/VPHomeSponsors.vue";
import { default as default10 } from "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/components/VPImage.vue";
import { default as default11 } from "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/components/VPLink.vue";
import { default as default12 } from "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/components/VPNavBarSearch.vue";
import { default as default13 } from "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/components/VPSocialLink.vue";
import { default as default14 } from "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/components/VPSocialLinks.vue";
import { default as default15 } from "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/components/VPSponsors.vue";
import { default as default16 } from "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/components/VPTeamMembers.vue";
import { default as default17 } from "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/components/VPTeamPage.vue";
import { default as default18 } from "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/components/VPTeamPageSection.vue";
import { default as default19 } from "/Users/lan/Project/Python/FileCodeBox/docs/node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/components/VPTeamPageTitle.vue";
// node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/composables/local-nav.js
import { onContentUpdated } from "vitepress";
// node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/composables/outline.js
import { getScrollOffset } from "vitepress";
// node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/support/utils.js
import { withBase } from "vitepress";
// node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/composables/data.js
import { useData as useData$ } from "vitepress";
var useData = useData$;
// node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/support/utils.js
function ensureStartingSlash(path) {
return path.startsWith("/") ? path : `/${path}`;
}
// node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/support/sidebar.js
function getSidebar(_sidebar, path) {
if (Array.isArray(_sidebar))
return addBase(_sidebar);
if (_sidebar == null)
return [];
path = ensureStartingSlash(path);
const dir = Object.keys(_sidebar).sort((a, b) => {
return b.split("/").length - a.split("/").length;
}).find((dir2) => {
return path.startsWith(ensureStartingSlash(dir2));
});
const sidebar = dir ? _sidebar[dir] : [];
return Array.isArray(sidebar) ? addBase(sidebar) : addBase(sidebar.items, sidebar.base);
}
function getSidebarGroups(sidebar) {
const groups = [];
let lastGroupIndex = 0;
for (const index in sidebar) {
const item = sidebar[index];
if (item.items) {
lastGroupIndex = groups.push(item);
continue;
}
if (!groups[lastGroupIndex]) {
groups.push({ items: [] });
}
groups[lastGroupIndex].items.push(item);
}
return groups;
}
function addBase(items, _base) {
return [...items].map((_item) => {
const item = { ..._item };
const base = item.base || _base;
if (base && item.link)
item.link = base + item.link;
if (item.items)
item.items = addBase(item.items, base);
return item;
});
}
// node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/composables/sidebar.js
function useSidebar() {
const { frontmatter, page, theme: theme2 } = useData();
const is960 = useMediaQuery("(min-width: 960px)");
const isOpen = ref(false);
const _sidebar = computed(() => {
const sidebarConfig = theme2.value.sidebar;
const relativePath = page.value.relativePath;
return sidebarConfig ? getSidebar(sidebarConfig, relativePath) : [];
});
const sidebar = ref(_sidebar.value);
watch(_sidebar, (next, prev) => {
if (JSON.stringify(next) !== JSON.stringify(prev))
sidebar.value = _sidebar.value;
});
const hasSidebar = computed(() => {
return frontmatter.value.sidebar !== false && sidebar.value.length > 0 && frontmatter.value.layout !== "home";
});
const leftAside = computed(() => {
if (hasAside)
return frontmatter.value.aside == null ? theme2.value.aside === "left" : frontmatter.value.aside === "left";
return false;
});
const hasAside = computed(() => {
if (frontmatter.value.layout === "home")
return false;
if (frontmatter.value.aside != null)
return !!frontmatter.value.aside;
return theme2.value.aside !== false;
});
const isSidebarEnabled = computed(() => hasSidebar.value && is960.value);
const sidebarGroups = computed(() => {
return hasSidebar.value ? getSidebarGroups(sidebar.value) : [];
});
function open() {
isOpen.value = true;
}
function close() {
isOpen.value = false;
}
function toggle() {
isOpen.value ? close() : open();
}
return {
isOpen,
sidebar,
sidebarGroups,
hasSidebar,
hasAside,
leftAside,
isSidebarEnabled,
open,
close,
toggle
};
}
// node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/composables/outline.js
var ignoreRE = /\b(?:VPBadge|header-anchor|footnote-ref|ignore-header)\b/;
var resolvedHeaders = [];
function getHeaders(range) {
const headers = [
...document.querySelectorAll(".VPDoc :where(h1,h2,h3,h4,h5,h6)")
].filter((el) => el.id && el.hasChildNodes()).map((el) => {
const level = Number(el.tagName[1]);
return {
element: el,
title: serializeHeader(el),
link: "#" + el.id,
level
};
});
return resolveHeaders(headers, range);
}
function serializeHeader(h) {
let ret = "";
for (const node of h.childNodes) {
if (node.nodeType === 1) {
if (ignoreRE.test(node.className))
continue;
ret += node.textContent;
} else if (node.nodeType === 3) {
ret += node.textContent;
}
}
return ret.trim();
}
function resolveHeaders(headers, range) {
if (range === false) {
return [];
}
const levelsRange = (typeof range === "object" && !Array.isArray(range) ? range.level : range) || 2;
const [high, low] = typeof levelsRange === "number" ? [levelsRange, levelsRange] : levelsRange === "deep" ? [2, 6] : levelsRange;
return buildTree(headers, high, low);
}
function buildTree(data, min, max) {
resolvedHeaders.length = 0;
const result = [];
const stack = [];
data.forEach((item) => {
const node = { ...item, children: [] };
let parent = stack[stack.length - 1];
while (parent && parent.level >= node.level) {
stack.pop();
parent = stack[stack.length - 1];
}
if (node.element.classList.contains("ignore-header") || parent && "shouldIgnore" in parent) {
stack.push({ level: node.level, shouldIgnore: true });
return;
}
if (node.level > max || node.level < min)
return;
resolvedHeaders.push({ element: node.element, link: node.link });
if (parent)
parent.children.push(node);
else
result.push(node);
stack.push(node);
});
return result;
}
// node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/composables/local-nav.js
function useLocalNav() {
const { theme: theme2, frontmatter } = useData();
const headers = shallowRef([]);
const hasLocalNav = computed(() => {
return headers.value.length > 0;
});
onContentUpdated(() => {
headers.value = getHeaders(frontmatter.value.outline ?? theme2.value.outline);
});
return {
headers,
hasLocalNav
};
}
// node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/without-fonts.js
var theme = {
Layout,
enhanceApp: ({ app }) => {
app.component("Badge", VPBadge);
}
};
var without_fonts_default = theme;
export {
default2 as VPBadge,
default3 as VPButton,
default4 as VPDocAsideSponsors,
default5 as VPFeatures,
default6 as VPHomeContent,
default7 as VPHomeFeatures,
default8 as VPHomeHero,
default9 as VPHomeSponsors,
default10 as VPImage,
default11 as VPLink,
default12 as VPNavBarSearch,
default13 as VPSocialLink,
default14 as VPSocialLinks,
default15 as VPSponsors,
default16 as VPTeamMembers,
default17 as VPTeamPage,
default18 as VPTeamPageSection,
default19 as VPTeamPageTitle,
without_fonts_default as default,
useLocalNav,
useSidebar
};
//# sourceMappingURL=@theme_index.js.map
File diff suppressed because one or more lines are too long
+9 -15
View File
@@ -1,49 +1,43 @@
{ {
"hash": "d07f6dee", "hash": "8f855eaf",
"configHash": "dee2d3bc", "configHash": "1b3ca22f",
"lockfileHash": "bd28b2c2", "lockfileHash": "bd28b2c2",
"browserHash": "b96651ac", "browserHash": "29e84937",
"optimized": { "optimized": {
"vue": { "vue": {
"src": "../../../node_modules/.pnpm/vue@3.5.13/node_modules/vue/dist/vue.runtime.esm-bundler.js", "src": "../../../node_modules/.pnpm/vue@3.5.13/node_modules/vue/dist/vue.runtime.esm-bundler.js",
"file": "vue.js", "file": "vue.js",
"fileHash": "bc5c7f34", "fileHash": "3215885f",
"needsInterop": false "needsInterop": false
}, },
"vitepress > @vue/devtools-api": { "vitepress > @vue/devtools-api": {
"src": "../../../node_modules/.pnpm/@vue+devtools-api@7.7.1/node_modules/@vue/devtools-api/dist/index.js", "src": "../../../node_modules/.pnpm/@vue+devtools-api@7.7.1/node_modules/@vue/devtools-api/dist/index.js",
"file": "vitepress___@vue_devtools-api.js", "file": "vitepress___@vue_devtools-api.js",
"fileHash": "00859881", "fileHash": "5a5f95ef",
"needsInterop": false "needsInterop": false
}, },
"vitepress > @vueuse/core": { "vitepress > @vueuse/core": {
"src": "../../../node_modules/.pnpm/@vueuse+core@12.5.0/node_modules/@vueuse/core/index.mjs", "src": "../../../node_modules/.pnpm/@vueuse+core@12.5.0/node_modules/@vueuse/core/index.mjs",
"file": "vitepress___@vueuse_core.js", "file": "vitepress___@vueuse_core.js",
"fileHash": "1c6da938", "fileHash": "0fbf66f0",
"needsInterop": false "needsInterop": false
}, },
"vitepress > @vueuse/integrations/useFocusTrap": { "vitepress > @vueuse/integrations/useFocusTrap": {
"src": "../../../node_modules/.pnpm/@vueuse+integrations@12.5.0_focus-trap@7.6.4/node_modules/@vueuse/integrations/useFocusTrap.mjs", "src": "../../../node_modules/.pnpm/@vueuse+integrations@12.5.0_focus-trap@7.6.4/node_modules/@vueuse/integrations/useFocusTrap.mjs",
"file": "vitepress___@vueuse_integrations_useFocusTrap.js", "file": "vitepress___@vueuse_integrations_useFocusTrap.js",
"fileHash": "11c08fcf", "fileHash": "91b03896",
"needsInterop": false "needsInterop": false
}, },
"vitepress > mark.js/src/vanilla.js": { "vitepress > mark.js/src/vanilla.js": {
"src": "../../../node_modules/.pnpm/mark.js@8.11.1/node_modules/mark.js/src/vanilla.js", "src": "../../../node_modules/.pnpm/mark.js@8.11.1/node_modules/mark.js/src/vanilla.js",
"file": "vitepress___mark__js_src_vanilla__js.js", "file": "vitepress___mark__js_src_vanilla__js.js",
"fileHash": "b93dbfe7", "fileHash": "99e4f81c",
"needsInterop": false "needsInterop": false
}, },
"vitepress > minisearch": { "vitepress > minisearch": {
"src": "../../../node_modules/.pnpm/minisearch@7.1.1/node_modules/minisearch/dist/es/index.js", "src": "../../../node_modules/.pnpm/minisearch@7.1.1/node_modules/minisearch/dist/es/index.js",
"file": "vitepress___minisearch.js", "file": "vitepress___minisearch.js",
"fileHash": "bfb6c3ac", "fileHash": "cc176c9c",
"needsInterop": false
},
"@theme/index": {
"src": "../../../node_modules/.pnpm/vitepress@1.6.3_@algolia+client-search@5.20.2_postcss@8.5.2_search-insights@2.17.3/node_modules/vitepress/dist/client/theme-default/index.js",
"file": "@theme_index.js",
"fileHash": "07512287",
"needsInterop": false "needsInterop": false
} }
}, },
+473
View File
@@ -0,0 +1,473 @@
:root {
/* --- 核心色板清晨薄雾 (Teal -> Sky) --- */
--vp-c-brand-1: #14b8a6; /* Teal 500 */
--vp-c-brand-2: #0ea5e9; /* Sky 500 */
--vp-c-brand-3: #0f766e; /* Teal 700 */
--vp-c-brand-soft: rgba(20, 184, 166, 0.14);
/* --- 背景色极简与通透 --- */
--vp-c-bg: #ffffff;
--vp-c-bg-alt: #f8fafc; /* Slate 50 */
--vp-c-bg-elv: #ffffff;
/* --- 文字颜色柔和的深灰而非纯黑 --- */
--vp-c-text-1: #334155; /* Slate 700 */
--vp-c-text-2: #64748b; /* Slate 500 */
--vp-c-text-3: #94a3b8; /* Slate 400 */
/* --- 边框与分隔 --- */
--vp-c-border: #e2e8f0;
--vp-c-divider: #f1f5f9;
/* --- 阴影扩散柔光 --- */
--vp-shadow-1: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03);
--vp-shadow-2: 0 10px 15px -3px rgba(0, 0, 0, 0.05), 0 4px 6px -2px rgba(0, 0, 0, 0.025);
--vp-shadow-brand: 0 4px 14px 0 rgba(20, 184, 166, 0.3);
/* --- 布局参数 --- */
--vp-nav-height: 64px;
--vp-sidebar-width: 280px;
--vp-radius-default: 12px;
--vp-radius-small: 6px;
}
/* * ==========================================
* 暗黑模式重构 (Deep Ocean Theme)
* ==========================================
*/
.dark {
/* 背景更深邃的蓝黑色更有沉浸感 */
--vp-c-bg: #020617; /* Slate 950 */
--vp-c-bg-alt: #0f172a; /* Slate 900 */
--vp-c-bg-elv: #1e293b; /* Slate 800 */
/* 文字降低纯白刺眼感提高舒适度 */
--vp-c-text-1: #f8fafc; /* Slate 50 */
--vp-c-text-2: #94a3b8; /* Slate 400 */
--vp-c-text-3: #64748b; /* Slate 500 */
/* 边框更细腻的深色线条 */
--vp-c-border: #1e293b;
--vp-c-divider: #0f172a;
/* 品牌色调亮产生荧光感 */
--vp-c-brand-1: #2dd4bf; /* Teal 400 */
--vp-c-brand-2: #38bdf8; /* Sky 400 */
--vp-c-brand-3: #14b8a6; /* Teal 500 */
--vp-c-brand-soft: rgba(45, 212, 191, 0.15); /* 淡淡的荧光底色 */
/* 阴影暗色环境下的微光 */
--vp-shadow-1: 0 4px 6px -1px rgba(0, 0, 0, 0.5);
--vp-shadow-2: 0 10px 15px -3px rgba(0, 0, 0, 0.5);
--vp-shadow-brand: 0 0 20px rgba(45, 212, 191, 0.3); /* 品牌色发光 */
}
/* 全局基础设置 */
body {
font-family: 'Noto Sans SC', sans-serif;
background-color: var(--vp-c-bg-alt);
color: var(--vp-c-text-1);
margin: 0;
transition: background-color 0.3s, color 0.3s;
overflow-x: hidden;
}
a { text-decoration: none; color: inherit; transition: color 0.2s; }
/* -------------------------------------------
布局结构 (模拟 VitePress 布局)
------------------------------------------- */
.app-container {
display: flex;
flex-direction: column;
min-height: 100vh;
}
/* 1. 导航栏毛玻璃 + 悬浮感 */
.VPNavBar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: var(--vp-nav-height);
background-color: rgba(255, 255, 255, 0.85); /* 稍微增加不透明度 */
backdrop-filter: blur(20px) saturate(180%);
-webkit-backdrop-filter: blur(20px) saturate(180%);
border-bottom: 1px solid rgba(226, 232, 240, 0.6);
z-index: 50;
display: flex;
align-items: center;
padding: 0 24px;
justify-content: space-between;
}
.dark .VPNavBar {
background-color: rgba(2, 6, 23, 0.85); /* 深色背景半透 */
border-bottom: 1px solid rgba(30, 41, 59, 0.7);
}
.nav-logo {
font-weight: 700;
font-size: 1.2rem;
background: linear-gradient(120deg, var(--vp-c-brand-1), var(--vp-c-brand-2));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
display: flex;
align-items: center;
gap: 8px;
}
.nav-links {
display: flex;
gap: 24px;
font-size: 0.9rem;
font-weight: 500;
color: var(--vp-c-text-2);
}
.nav-link:hover {
color: var(--vp-c-brand-1);
}
.nav-link.active {
color: var(--vp-c-brand-1);
position: relative;
}
.nav-link.active::after {
content: '';
position: absolute;
bottom: -22px;
left: 0;
width: 100%;
height: 2px;
background: var(--vp-c-brand-1);
border-radius: 2px 2px 0 0;
box-shadow: 0 -2px 10px var(--vp-c-brand-1); /* 激活状态发光 */
}
/* 2. 侧边栏极简卡片式 */
.VPSidebar {
position: fixed;
top: var(--vp-nav-height);
left: 0;
bottom: 0;
width: var(--vp-sidebar-width);
background-color: var(--vp-c-bg); /* 实色背景 */
border-right: 1px solid var(--vp-c-border);
padding: 32px 24px;
overflow-y: auto;
transform: translateX(0);
transition: transform 0.3s ease;
z-index: 40;
}
.dark .VPSidebar {
background-color: var(--vp-c-bg-alt); /* 侧边栏比内容区稍微亮一点点/或者暗一点 */
}
@media (max-width: 960px) {
.VPSidebar { transform: translateX(-100%); }
.VPSidebar.open { transform: translateX(0); }
}
.sidebar-group-title {
font-weight: 700;
color: var(--vp-c-text-1);
margin-bottom: 12px;
margin-top: 24px;
font-size: 1rem;
}
.sidebar-group-title:first-child { margin-top: 0; }
.sidebar-link {
display: block;
padding: 8px 12px;
margin: 4px 0;
border-radius: var(--vp-radius-small);
color: var(--vp-c-text-2);
font-size: 0.95rem;
transition: all 0.2s;
}
.sidebar-link:hover {
background-color: var(--vp-c-bg-alt);
color: var(--vp-c-brand-1);
}
.dark .sidebar-link:hover {
background-color: var(--vp-c-bg-elv);
}
.sidebar-link.active {
background-color: var(--vp-c-brand-soft);
color: var(--vp-c-brand-3);
font-weight: 600;
}
.dark .sidebar-link.active {
color: var(--vp-c-brand-1);
background-color: rgba(45, 212, 191, 0.1); /* 暗色模式下更通透 */
box-shadow: inset 2px 0 0 0 var(--vp-c-brand-1); /* 左侧高亮条 */
}
/* 3. 主内容区居中与留白 */
.VPContent {
margin-top: var(--vp-nav-height);
margin-left: var(--vp-sidebar-width);
padding: 48px 64px;
min-height: calc(100vh - var(--vp-nav-height));
background-color: var(--vp-c-bg);
transition: margin-left 0.3s ease;
}
@media (max-width: 960px) {
.VPContent { margin-left: 0; padding: 32px 24px; }
}
/* 内容排版 (Typography) */
.vp-doc h1 {
font-size: 2.5rem;
font-weight: 800;
margin-bottom: 1.5rem;
line-height: 1.2;
background: linear-gradient(120deg, var(--vp-c-brand-1), var(--vp-c-brand-2));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
display: inline-block;
/* 暗色模式下加一点文字阴影增强可读性 */
filter: drop-shadow(0 0 2em rgba(20, 184, 166, 0.2));
}
.dark .vp-doc h1 {
filter: drop-shadow(0 0 25px rgba(45, 212, 191, 0.2));
}
.vp-doc h2 {
font-size: 1.8rem;
margin-top: 3rem;
margin-bottom: 1rem;
padding-bottom: 0.5rem;
border-bottom: 1px solid var(--vp-c-border);
color: var(--vp-c-text-1);
}
.vp-doc h3 {
font-size: 1.4rem;
margin-top: 2rem;
margin-bottom: 0.8rem;
color: var(--vp-c-text-1);
}
.vp-doc p {
line-height: 1.8;
margin-bottom: 1.2rem;
color: var(--vp-c-text-2);
font-size: 1.05rem;
}
/* 代码块美化 */
.vp-doc pre {
background-color: #1e293b; /* Dark Slate */
border-radius: var(--vp-radius-default);
padding: 20px;
overflow-x: auto;
color: #f8fafc;
margin: 24px 0;
border: 1px solid rgba(0,0,0,0.1);
box-shadow: var(--vp-shadow-1);
}
.dark .vp-doc pre {
background-color: #0f172a; /* 更深一层的背景 */
border: 1px solid #1e293b;
}
.vp-doc code {
font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
font-size: 0.9em;
}
.vp-doc p code {
background-color: var(--vp-c-brand-soft);
color: var(--vp-c-brand-3);
padding: 2px 6px;
border-radius: 4px;
font-size: 0.9em;
}
.dark .vp-doc p code {
color: var(--vp-c-brand-1);
background-color: rgba(45, 212, 191, 0.1);
}
/* 引用块 (Blockquote) */
.vp-doc blockquote {
margin: 1.5rem 0;
padding: 16px 24px;
background-color: var(--vp-c-bg-alt);
border-left: 4px solid var(--vp-c-brand-1);
border-radius: 0 var(--vp-radius-default) var(--vp-radius-default) 0;
color: var(--vp-c-text-2);
}
.dark .vp-doc blockquote {
background-color: rgba(30, 41, 59, 0.5);
}
/* 按钮与卡片 (Features) */
.vp-button {
display: inline-block;
padding: 0 24px;
height: 48px;
line-height: 48px;
border-radius: 24px; /* Pill shape */
font-weight: 600;
font-size: 1rem;
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
cursor: pointer;
border: none;
}
.vp-button.brand {
background: linear-gradient(135deg, var(--vp-c-brand-1), var(--vp-c-brand-2));
color: white;
box-shadow: var(--vp-shadow-brand);
}
.dark .vp-button.brand {
color: #020617; /* 暗色模式下按钮文字用深色,因为背景亮 */
}
.vp-button.brand:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px -4px rgba(20, 184, 166, 0.5);
}
.dark .vp-button.brand:hover {
box-shadow: 0 0 30px -4px rgba(45, 212, 191, 0.6); /* 发光增强 */
}
.vp-button.alt {
background-color: var(--vp-c-bg-alt);
color: var(--vp-c-text-1);
border: 1px solid var(--vp-c-border);
}
.dark .vp-button.alt {
background-color: transparent;
border-color: var(--vp-c-text-2);
color: var(--vp-c-text-1);
}
.vp-button.alt:hover {
border-color: var(--vp-c-brand-1);
color: var(--vp-c-brand-1);
}
/* 工具类 */
.theme-toggle {
background: none;
border: none;
cursor: pointer;
font-size: 1.5rem;
color: var(--vp-c-text-2);
display: flex;
align-items: center;
justify-content: center;
padding: 8px;
border-radius: 50%;
transition: background 0.2s;
}
.theme-toggle:hover {
background-color: var(--vp-c-bg-alt);
color: var(--vp-c-brand-1);
}
.dark .theme-toggle:hover {
background-color: var(--vp-c-bg-elv);
}
.mobile-menu-btn {
display: none;
background: none;
border: none;
font-size: 1.5rem;
color: var(--vp-c-text-1);
cursor: pointer;
}
@media (max-width: 960px) {
.mobile-menu-btn { display: block; }
.nav-links { display: none; }
}
/* 首页 Hero 样式模拟 */
.hero-section {
text-align: center;
padding: 80px 20px;
max-width: 800px;
margin: 0 auto;
}
.hero-title {
font-size: 3.5rem;
font-weight: 900;
line-height: 1.1;
margin-bottom: 24px;
}
.hero-title span {
background: linear-gradient(120deg, var(--vp-c-brand-1), var(--vp-c-brand-2));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.hero-desc {
font-size: 1.5rem;
color: var(--vp-c-text-2);
margin-bottom: 40px;
}
.hero-actions {
display: flex;
gap: 16px;
justify-content: center;
}
/* Features Grid */
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 24px;
margin-top: 64px;
}
.feature-card {
background-color: var(--vp-c-bg-alt);
padding: 24px;
border-radius: var(--vp-radius-default);
transition: transform 0.3s, box-shadow 0.3s;
border: 1px solid transparent;
}
.dark .feature-card {
background-color: rgba(30, 41, 59, 0.3); /* 微微透明 */
border: 1px solid rgba(255,255,255,0.05);
}
.feature-card:hover {
background-color: var(--vp-c-bg);
transform: translateY(-4px);
box-shadow: var(--vp-shadow-2);
border-color: var(--vp-c-brand-soft);
}
.dark .feature-card:hover {
background-color: var(--vp-c-bg-elv);
border-color: var(--vp-c-brand-1);
box-shadow: 0 0 15px rgba(45, 212, 191, 0.1);
}
.feature-icon {
font-size: 2rem;
margin-bottom: 16px;
color: var(--vp-c-brand-1);
background-color: var(--vp-c-brand-soft);
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 12px;
}
.feature-title {
font-weight: 700;
margin-bottom: 8px;
color: var(--vp-c-text-1);
}
.feature-details {
color: var(--vp-c-text-2);
font-size: 0.95rem;
line-height: 1.6;
}
+6
View File
@@ -0,0 +1,6 @@
import DefaultTheme from 'vitepress/theme'
import './custom.css'
export default {
...DefaultTheme,
}
+207
View File
@@ -0,0 +1,207 @@
# Configuration Guide
FileCodeBox provides rich configuration options that can be customized through the admin panel or by directly modifying the configuration. This document details all available configuration options.
## Configuration Methods
FileCodeBox supports two configuration methods:
1. **Admin Panel Configuration** (Recommended): Access `/admin` to enter the admin panel and modify settings on the settings page
2. **Database Configuration**: Configuration is stored in the `data/filecodebox.db` database
::: tip Note
On first startup, the system uses default configuration from `core/settings.py`. Modified configurations are saved to the database.
:::
## Basic Settings
### Site Information
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `name` | string | `文件快递柜 - FileCodeBox` | Site name, displayed in page title and navigation bar |
| `description` | string | `开箱即用的文件快传系统` | Site description, used for SEO |
| `keywords` | string | `FileCodeBox, 文件快递柜...` | Site keywords, used for SEO |
| `port` | int | `12345` | Service listening port |
### Notification Settings
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `notify_title` | string | `系统通知` | Notification title |
| `notify_content` | string | Welcome message | Notification content, supports HTML |
| `page_explain` | string | Legal disclaimer | Footer explanation text |
| `robotsText` | string | `User-agent: *\nDisallow: /` | robots.txt content |
## Upload Settings
### File Upload Limits
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `openUpload` | int | `1` | Enable upload functionality (1=enabled, 0=disabled) |
| `uploadSize` | int | `10485760` | Maximum single file upload size (bytes), default 10MB |
| `enableChunk` | int | `0` | Enable chunked upload (1=enabled, 0=disabled) |
::: warning Note
`uploadSize` is in bytes. 10MB = 10 * 1024 * 1024 = 10485760 bytes
:::
### Upload Rate Limiting
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `uploadMinute` | int | `1` | Upload limit time window (minutes) |
| `uploadCount` | int | `10` | Maximum uploads allowed within the time window |
Example: Default configuration allows up to 10 uploads per minute.
### File Expiration Settings
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `expireStyle` | list | `["day","hour","minute","forever","count"]` | Available expiration methods |
| `max_save_seconds` | int | `0` | Maximum file retention time (seconds), 0 means no limit |
Expiration methods explained:
- `day` - Expire by days
- `hour` - Expire by hours
- `minute` - Expire by minutes
- `forever` - Never expire
- `count` - Expire by download count
## Theme Settings
### Theme Selection
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `themesSelect` | string | `themes/2024` | Currently active theme |
| `themesChoices` | list | See below | Available themes list |
Default available themes:
```json
[
{
"name": "2023",
"key": "themes/2023",
"author": "Lan",
"version": "1.0"
},
{
"name": "2024",
"key": "themes/2024",
"author": "Lan",
"version": "1.0"
}
]
```
### Interface Style
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `opacity` | float | `0.9` | Interface opacity (0-1) |
| `background` | string | `""` | Custom background image URL, empty uses default background |
## Admin Settings
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `admin_token` | string | `FileCodeBox2023` | Admin login password |
| `showAdminAddr` | int | `0` | Show admin panel entry on homepage (1=show, 0=hide) |
::: danger Security Warning
Always change the default `admin_token` in production environments! Using the default password poses serious security risks.
:::
## Security Settings
### Error Rate Limiting
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `errorMinute` | int | `1` | Error limit time window (minutes) |
| `errorCount` | int | `1` | Maximum errors allowed within the time window |
This setting prevents brute-force attacks on extraction codes.
## Storage Settings
### Storage Type
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `file_storage` | string | `local` | Storage backend type |
| `storage_path` | string | `""` | Custom storage path |
Supported storage types:
- `local` - Local storage
- `s3` - S3-compatible storage (AWS S3, Aliyun OSS, MinIO, etc.)
- `onedrive` - OneDrive storage
- `webdav` - WebDAV storage
- `opendal` - OpenDAL storage
For detailed storage configuration, see [Storage Configuration](/en/guide/storage).
## Configuration Examples
### Example 1: Small Personal Use
Suitable for personal or small team use with relaxed limits:
```python
{
"name": "My File Share",
"uploadSize": 52428800, # 50MB
"uploadMinute": 5, # 5 minutes
"uploadCount": 20, # Max 20 uploads
"expireStyle": ["day", "hour", "forever"],
"admin_token": "your-secure-password",
"showAdminAddr": 1
}
```
### Example 2: Public Service
Suitable for public services requiring stricter limits:
```python
{
"name": "Public File Box",
"uploadSize": 10485760, # 10MB
"uploadMinute": 1, # 1 minute
"uploadCount": 5, # Max 5 uploads
"errorMinute": 5, # 5 minutes
"errorCount": 3, # Max 3 errors
"expireStyle": ["hour", "minute", "count"],
"max_save_seconds": 86400, # Max retention 1 day
"admin_token": "very-secure-password-123",
"showAdminAddr": 0
}
```
### Example 3: Enterprise Internal Use
Suitable for enterprise internal use with large file and chunked upload support:
```python
{
"name": "Enterprise File Transfer",
"uploadSize": 1073741824, # 1GB
"enableChunk": 1, # Enable chunked upload
"uploadMinute": 10, # 10 minutes
"uploadCount": 100, # Max 100 uploads
"expireStyle": ["day", "forever"],
"file_storage": "s3", # Use S3 storage
"admin_token": "enterprise-secure-token",
"showAdminAddr": 1
}
```
## Next Steps
- [Storage Configuration](/en/guide/storage) - Learn how to configure different storage backends
- [Security Settings](/en/guide/security) - Learn how to enhance system security
- [File Sharing](/en/guide/share) - Learn about file sharing features
+413
View File
@@ -0,0 +1,413 @@
# Admin Panel
FileCodeBox provides a fully-featured admin panel that allows administrators to conveniently manage files, view system status, and modify configurations. This document introduces the various features and usage of the admin panel.
## Accessing the Admin Panel
### Login Method
The admin panel is located at the `/admin` path. Access method:
1. Visit `http://your-domain.com/admin` in your browser
2. Enter the admin password (the value of the `admin_token` configuration)
3. Click the login button
::: tip Tip
The default admin password is `FileCodeBox2023`. Be sure to change this password in production environments. See [Security Settings](/en/guide/security) for details.
:::
### Show Admin Entry
By default, the admin panel entry is not shown on the homepage. You can control whether to show it via configuration:
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `showAdminAddr` | int | `0` | Show admin entry on homepage (1=show, 0=hide) |
::: warning Security Recommendation
For public services, it's recommended to keep `showAdminAddr` at `0` and access the admin panel directly via the `/admin` path to reduce the risk of malicious scanning.
:::
### Authentication Mechanism
The admin panel uses JWT (JSON Web Token) for authentication:
1. After successful login, the server returns a Token containing admin identity
2. Subsequent requests carry the Token via `Authorization: Bearer <token>` header
3. Token is used to verify admin identity, ensuring only authorized users can access admin functions
## Dashboard
After logging in, you first see the dashboard page, which displays the overall system status.
### Statistics
The dashboard displays the following key metrics:
| Metric | Description |
|--------|-------------|
| **Total Files** (`totalFiles`) | Total number of files stored in the system |
| **Storage Used** (`storageUsed`) | Total storage space occupied by all files (bytes) |
| **System Uptime** (`sysUptime`) | Time when the system was first started |
| **Yesterday's Uploads** (`yesterdayCount`) | Number of files uploaded yesterday |
| **Yesterday's Upload Size** (`yesterdaySize`) | Total size of files uploaded yesterday (bytes) |
| **Today's Uploads** (`todayCount`) | Number of files uploaded today so far |
| **Today's Upload Size** (`todaySize`) | Total size of files uploaded today (bytes) |
### Metric Notes
- **Total Files**: Includes all unexpired files and text shares
- **Storage Used**: Shows actual storage space occupied by files, excluding database and other system files
- **Yesterday/Today Statistics**: Calculated based on file creation time, useful for understanding system usage trends
::: tip Tip
Storage usage is displayed in bytes. For example, `10485760` represents approximately 10MB.
:::
## File Management
### File List
The file management page displays all shared files in the system, supporting pagination and search.
**List information includes:**
- File ID
- Extraction code (code)
- Filename prefix (prefix)
- File extension (suffix)
- File size
- Creation time
- Expiration time
- Remaining download count
### Search Files
Use the search function to quickly find specific files:
1. Enter keywords in the search box
2. System performs fuzzy matching based on filename prefix (prefix)
3. Search results update in real-time
**Search examples:**
- Enter `report` to find all files with "report" in the filename
- Enter `.pdf` to find all PDF files (if the filename contains this string)
### Pagination
The file list supports paginated display:
| Parameter | Default | Description |
|-----------|---------|-------------|
| `page` | `1` | Current page number |
| `size` | `10` | Items per page |
### Delete Files
Administrators can delete any file:
1. Find the file to delete in the file list
2. Click the delete button
3. Confirm the delete operation
::: danger Warning
Delete operations are irreversible! Files will be permanently deleted from the storage backend, and database records will also be removed.
:::
**Delete process:**
1. System first deletes the actual file from the storage backend (local/S3/OneDrive, etc.)
2. Then deletes the file record from the database
3. After deletion, the corresponding extraction code becomes invalid
### Download Files
Administrators can directly download any file:
1. Find the target file in the file list
2. Click the download button
3. File will be downloaded via browser
For text shares, the system returns text content directly instead of downloading a file.
### Modify File Information
Administrators can modify some information of shared files:
| Modifiable Field | Description |
|------------------|-------------|
| `code` | Extraction code (must be unique, cannot duplicate other files) |
| `prefix` | Filename prefix |
| `suffix` | File extension |
| `expired_at` | Expiration time |
| `expired_count` | Remaining download count |
**Modify extraction code:**
```
Original code: abc123
New code: myfile2024
```
::: warning Note
When modifying extraction codes, the system checks if the new code is already in use. If an identical extraction code exists, the modification will fail.
:::
## Local File Management
In addition to managing shared files, the admin panel also provides local file management functionality for managing files in the `data/local` directory.
### View Local Files
The local file list displays all files in the `data/local` directory:
| Information | Description |
|-------------|-------------|
| Filename | Complete filename |
| Creation Time | File creation time |
| File Size | File size (bytes) |
### Share Local Files
You can quickly share local files:
1. Select the file to share in the local file list
2. Set expiration method and value
3. Click the share button
4. System generates extraction code
**Share parameters:**
| Parameter | Description |
|-----------|-------------|
| `filename` | Filename to share |
| `expire_style` | Expiration method (day/hour/minute/forever/count) |
| `expire_value` | Expiration value (days/hours/minutes/download count) |
### Delete Local Files
You can delete files in the `data/local` directory:
1. Find the file to delete in the local file list
2. Click the delete button
3. Confirm deletion
::: tip Use Cases
Local file management is useful for:
- Sharing files after batch uploading to the server
- Managing files uploaded to the server through other means
- Cleaning up unnecessary local files
:::
## System Settings
### View Configuration
On the system settings page, you can view the current values of all configuration items. Configuration items are grouped by category:
- Basic settings (site name, description, etc.)
- Upload settings (file size limits, rate limits, etc.)
- Storage settings (storage type, path, etc.)
- Theme settings (theme selection, opacity, etc.)
- Security settings (admin password, error limits, etc.)
### Modify Configuration
Administrators can modify most configurations through the admin panel:
1. Go to the system settings page
2. Find the configuration item to modify
3. Enter the new value
4. Click the save button
**Modifiable configuration items:**
| Category | Example Settings |
|----------|------------------|
| Basic Settings | `name`, `description`, `keywords`, `notify_title`, `notify_content` |
| Upload Settings | `uploadSize`, `uploadMinute`, `uploadCount`, `openUpload`, `enableChunk` |
| Expiration Settings | `expireStyle`, `max_save_seconds` |
| Theme Settings | `themesSelect`, `opacity`, `background` |
| Security Settings | `admin_token`, `showAdminAddr`, `errorMinute`, `errorCount` |
| Storage Settings | `file_storage`, `storage_path` and storage backend-specific configurations |
::: warning Note
- `admin_token` (admin password) cannot be set to empty
- `themesChoices` (theme list) cannot be modified through the admin panel
- After modifying storage settings, existing files will not be automatically migrated
:::
### Configuration Effect
Configuration changes take effect immediately without restarting the service. Configurations are saved in the database and persist after restart.
**Configuration storage location:**
- Database: `data/filecodebox.db`
- Table name: `keyvalue`
- Key name: `settings`
## API Endpoints
All admin panel functions are implemented through REST APIs. Here are the main endpoints:
### Authentication Endpoint
**Login**
```
POST /admin/login
Content-Type: application/json
{
"password": "your-admin-password"
}
```
Response:
```json
{
"code": 200,
"detail": {
"token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer"
}
}
```
### Dashboard Endpoint
**Get Statistics**
```
GET /admin/dashboard
Authorization: Bearer <token>
```
### File Management Endpoints
**Get File List**
```
GET /admin/file/list?page=1&size=10&keyword=
Authorization: Bearer <token>
```
**Delete File**
```
DELETE /admin/file/delete
Authorization: Bearer <token>
Content-Type: application/json
{
"id": 123
}
```
**Download File**
```
GET /admin/file/download?id=123
Authorization: Bearer <token>
```
**Modify File Information**
```
PATCH /admin/file/update
Authorization: Bearer <token>
Content-Type: application/json
{
"id": 123,
"code": "newcode",
"expired_at": "2024-12-31T23:59:59"
}
```
### Local File Endpoints
**Get Local File List**
```
GET /admin/local/lists
Authorization: Bearer <token>
```
**Delete Local File**
```
DELETE /admin/local/delete
Authorization: Bearer <token>
Content-Type: application/json
{
"filename": "example.txt"
}
```
**Share Local File**
```
POST /admin/local/share
Authorization: Bearer <token>
Content-Type: application/json
{
"filename": "example.txt",
"expire_style": "day",
"expire_value": 7
}
```
### Configuration Endpoints
**Get Configuration**
```
GET /admin/config/get
Authorization: Bearer <token>
```
**Update Configuration**
```
PATCH /admin/config/update
Authorization: Bearer <token>
Content-Type: application/json
{
"admin_token": "new-password",
"uploadSize": 52428800
}
```
## Common Issues
### Forgot Admin Password
If you forgot the admin password, you can reset it through the following methods:
1. Stop the FileCodeBox service
2. Open `data/filecodebox.db` using an SQLite tool
3. Query the record with `key='settings'` in the `keyvalue` table
4. Modify the `admin_token` value in the JSON
5. Restart the service
```sql
-- View current configuration
SELECT * FROM keyvalue WHERE key = 'settings';
-- Or delete configuration to restore default password
DELETE FROM keyvalue WHERE key = 'settings';
```
### File Deletion Failed
If an error occurs when deleting files, possible reasons:
1. **Storage backend connection failed**: Check if storage configuration is correct
2. **File no longer exists**: File may have been manually deleted
3. **Insufficient permissions**: Check write permissions for storage directory
### Configuration Changes Not Taking Effect
If configuration changes don't take effect:
1. Check if you clicked the save button
2. Refresh the page to see if configuration was saved
3. Check browser console for error messages
4. Confirm configuration value format is correct (e.g., don't enter strings for numeric types)
## Next Steps
- [Configuration Guide](/en/guide/configuration) - Learn detailed descriptions of all configuration options
- [Security Settings](/en/guide/security) - Learn how to enhance system security
- [Storage Configuration](/en/guide/storage) - Configure different storage backends
+325
View File
@@ -0,0 +1,325 @@
# Security Settings
FileCodeBox provides multiple layers of security mechanisms to protect your file sharing service. This document explains how to properly configure security options to ensure secure system operation.
## Admin Password
### Change Default Password
::: danger Important Security Warning
FileCodeBox's default admin password is `FileCodeBox2023`. **You must change this password immediately in production environments!** Using the default password allows anyone to access your admin panel.
:::
There are two ways to change the admin password:
**Method 1: Via Admin Panel (Recommended)**
1. Access `/admin` to enter the admin panel
2. Log in with the current password
3. Go to the "System Settings" page
4. Find the `admin_token` configuration item
5. Enter a new secure password and save
**Method 2: Via Database**
Configuration is stored in the `keyvalue` table of the `data/filecodebox.db` database. You can directly modify the `admin_token` value.
### Password Security Recommendations
- Use a strong password with at least 16 characters
- Include uppercase and lowercase letters, numbers, and special characters
- Avoid common words or personal information
- Change password regularly
```python
# Recommended password format example
"admin_token": "Xk9#mP2$vL5@nQ8&wR3"
```
### Hide Admin Entry
By default, the admin panel entry is hidden. You can control whether to show the admin entry on the homepage via the `showAdminAddr` configuration:
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `showAdminAddr` | int | `0` | Show admin entry (1=show, 0=hide) |
::: tip Recommendation
For public services, it's recommended to keep `showAdminAddr` at `0` and access the admin panel directly via the `/admin` path.
:::
## IP Rate Limiting
FileCodeBox has built-in IP-based rate limiting mechanisms to effectively prevent abuse and attacks.
### Upload Rate Limiting
Limit the number of uploads from a single IP within a specified time:
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `uploadMinute` | int | `1` | Upload limit time window (minutes) |
| `uploadCount` | int | `10` | Maximum uploads allowed within the time window |
**How it works:**
- System records upload requests from each IP
- When an IP's upload count reaches `uploadCount` within `uploadMinute` minutes
- Subsequent upload requests from that IP will be rejected with HTTP 423 error
- Counter resets after the time window expires
**Configuration examples:**
```python
# Relaxed configuration: Max 20 uploads in 5 minutes
{
"uploadMinute": 5,
"uploadCount": 20
}
# Strict configuration: Max 3 uploads in 1 minute
{
"uploadMinute": 1,
"uploadCount": 3
}
```
### Error Rate Limiting
Limit the number of error attempts from a single IP to prevent brute-force attacks on extraction codes:
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `errorMinute` | int | `1` | Error limit time window (minutes) |
| `errorCount` | int | `1` | Maximum errors allowed within the time window |
**How it works:**
- When a user enters an incorrect extraction code, the system records the error count for that IP
- When error count reaches `errorCount`, that IP will be temporarily locked
- Lock duration is `errorMinute` minutes
- During lockout, all extraction requests from that IP will be rejected
**Configuration example:**
```python
# Anti-brute-force configuration: Max 3 errors in 5 minutes
{
"errorMinute": 5,
"errorCount": 3
}
```
::: warning Note
The default configuration `errorMinute=1, errorCount=1` is very strict, meaning you need to wait 1 minute after entering one incorrect extraction code before retrying. Adjust this configuration based on actual needs.
:::
## Upload Restrictions
### File Size Limit
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `uploadSize` | int | `10485760` | Maximum single file upload size (bytes), default 10MB |
| `openUpload` | int | `1` | Enable upload functionality (1=enabled, 0=disabled) |
**Common size conversions:**
- 10MB = 10 * 1024 * 1024 = `10485760`
- 50MB = 50 * 1024 * 1024 = `52428800`
- 100MB = 100 * 1024 * 1024 = `104857600`
- 1GB = 1024 * 1024 * 1024 = `1073741824`
### File Expiration Settings
Through file expiration mechanisms, you can automatically clean up expired files, reducing storage usage and security risks:
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `expireStyle` | list | `["day","hour","minute","forever","count"]` | Available expiration methods |
| `max_save_seconds` | int | `0` | Maximum file retention time (seconds), 0 means no limit |
**Expiration methods explained:**
- `day` - Expire by days
- `hour` - Expire by hours
- `minute` - Expire by minutes
- `forever` - Never expire (requires alphanumeric extraction code)
- `count` - Expire by download count
**Security recommendations:**
For public services, it's recommended to:
1. Remove the `forever` option to avoid permanent file storage
2. Set `max_save_seconds` to limit maximum retention time
3. Prefer using `count` method for automatic deletion after download
```python
# Recommended configuration for public services
{
"expireStyle": ["hour", "minute", "count"],
"max_save_seconds": 86400 # Max retention 1 day
}
```
### Disable Upload Functionality
In some cases, you may need to temporarily disable upload functionality:
```python
{
"openUpload": 0 # Disable upload functionality
}
```
## Reverse Proxy Security Configuration
In production environments, Nginx or other reverse proxy servers are typically used. Here are security configuration recommendations:
### Nginx Configuration Example
```nginx
server {
listen 80;
server_name your-domain.com;
# Force HTTPS redirect
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name your-domain.com;
# SSL certificate configuration
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers on;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Limit request body size (match uploadSize configuration)
client_max_body_size 100M;
# Pass real IP
location / {
proxy_pass http://127.0.0.1:12345;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Static resource caching
location /assets {
proxy_pass http://127.0.0.1:12345;
proxy_cache_valid 200 7d;
add_header Cache-Control "public, max-age=604800";
}
}
```
### Key Security Configuration Notes
**1. Pass Real IP**
FileCodeBox's IP limiting functionality depends on obtaining the client's real IP. The system obtains IP in the following order:
1. `X-Real-IP` request header
2. `X-Forwarded-For` request header
3. Direct client connection IP
Ensure the reverse proxy correctly sets these headers:
```nginx
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
```
**2. Request Body Size Limit**
Nginx's `client_max_body_size` should match or be slightly larger than FileCodeBox's `uploadSize` configuration:
```nginx
client_max_body_size 100M; # Allow max 100MB uploads
```
**3. HTTPS Encryption**
It's strongly recommended to enable HTTPS in production environments:
- Protect uploaded file content
- Protect admin login credentials
- Prevent man-in-the-middle attacks
### Caddy Configuration Example
```nginx
your-domain.com {
reverse_proxy localhost:12345
header {
X-Frame-Options "SAMEORIGIN"
X-Content-Type-Options "nosniff"
X-XSS-Protection "1; mode=block"
Strict-Transport-Security "max-age=31536000; includeSubDomains"
}
}
```
## Security Checklist
Before deploying FileCodeBox, confirm the following security configurations:
- [ ] Changed default admin password `admin_token`
- [ ] Hidden admin entry `showAdminAddr: 0`
- [ ] Configured appropriate upload rate limiting
- [ ] Configured error rate limiting to prevent brute-force attacks
- [ ] Set reasonable file size limits
- [ ] Configured file expiration policy
- [ ] Enabled HTTPS encryption
- [ ] Reverse proxy correctly passes real IP
- [ ] Set security response headers
## Recommended Security Configurations
### Public Service Configuration
```python
{
"admin_token": "your-very-secure-password",
"showAdminAddr": 0,
"uploadSize": 10485760, # 10MB
"uploadMinute": 1,
"uploadCount": 5,
"errorMinute": 5,
"errorCount": 3,
"expireStyle": ["hour", "minute", "count"],
"max_save_seconds": 86400, # Max 1 day
"openUpload": 1
}
```
### Internal Service Configuration
```python
{
"admin_token": "internal-secure-password",
"showAdminAddr": 1,
"uploadSize": 104857600, # 100MB
"uploadMinute": 5,
"uploadCount": 50,
"errorMinute": 1,
"errorCount": 5,
"expireStyle": ["day", "hour", "forever"],
"max_save_seconds": 0, # No limit
"openUpload": 1
}
```
## Next Steps
- [Configuration Guide](/en/guide/configuration) - Learn about all configuration options
- [Storage Configuration](/en/guide/storage) - Configure secure storage backends
- [File Sharing](/en/guide/share) - Learn about file sharing features
+342
View File
@@ -0,0 +1,342 @@
# File Sharing
FileCodeBox provides simple and easy-to-use file and text sharing functionality. Users can securely share and retrieve files using extraction codes.
## Sharing Methods
FileCodeBox supports two sharing methods:
1. **Text Sharing** - Share text content directly, suitable for code snippets, configuration files, etc.
2. **File Sharing** - Upload files for sharing, supports various file formats
## Text Sharing
### How to Use
1. Select the "Text Share" tab on the homepage
2. Enter or paste the content to share in the text box
3. Select expiration method and time
4. Click the "Share" button
5. Get the extraction code
### Text Size Limit
::: warning Note
The maximum content size for text sharing is **222KB** (227,328 bytes). If content exceeds this limit, it's recommended to use file sharing instead.
:::
Text content size is calculated using UTF-8 encoding. Chinese characters typically occupy 3 bytes.
### API Endpoint
**POST** `/share/text/`
Request parameters:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `text` | string | Yes | Text content to share |
| `expire_value` | int | No | Expiration value, default 1 |
| `expire_style` | string | No | Expiration method, default `day` |
Response example:
```json
{
"code": 200,
"detail": {
"code": "123456"
}
}
```
## File Sharing
### How to Use
1. Select the "File Share" tab on the homepage
2. Click the upload area or drag files to the upload area
3. Select expiration method and time
4. Click the "Upload" button
5. Get the extraction code
### File Size Limit
The default maximum single file upload size is **10MB**. Administrators can modify this limit via the `uploadSize` configuration.
::: tip Tip
If you need to upload large files, contact the administrator to enable chunked upload functionality or adjust the `uploadSize` configuration.
:::
### Supported Upload Methods
- **Click Upload** - Click the upload area to select files
- **Drag Upload** - Drag files to the upload area
- **Paste Upload** - Paste images from clipboard (supported by some themes)
### API Endpoint
**POST** `/share/file/`
Request parameters (multipart/form-data):
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `file` | file | Yes | File to upload |
| `expire_value` | int | No | Expiration value, default 1 |
| `expire_style` | string | No | Expiration method, default `day` |
Response example:
```json
{
"code": 200,
"detail": {
"code": "654321",
"name": "example.pdf"
}
}
```
## Expiration Settings
FileCodeBox supports multiple flexible expiration methods:
| Expiration Method | Parameter Value | Description |
|-------------------|-----------------|-------------|
| By Days | `day` | File expires after specified days |
| By Hours | `hour` | File expires after specified hours |
| By Minutes | `minute` | File expires after specified minutes |
| Never Expire | `forever` | File is permanently valid |
| By Count | `count` | File expires after specified download count |
::: info Note
- Administrators can control available expiration methods via the `expireStyle` configuration
- Administrators can limit maximum file retention time via the `max_save_seconds` configuration
:::
### Expiration Method Examples
```bash
# File expires after 3 days
expire_value=3, expire_style=day
# File expires after 12 hours
expire_value=12, expire_style=hour
# File expires after 30 minutes
expire_value=30, expire_style=minute
# File never expires
expire_value=1, expire_style=forever
# File expires after 5 downloads
expire_value=5, expire_style=count
```
## Retrieving Files
### How to Use
1. Enter the extraction code in the "Retrieve File" area on the homepage
2. Click the "Retrieve" button
3. System displays file information (filename, size, etc.)
4. Click the "Download" button to download the file, or view text content directly
### Extraction Code Notes
- Extraction codes are typically **6-digit numbers**
- Files that never expire use **alphanumeric** extraction codes
- Extraction codes are case-sensitive (for alphanumeric codes)
### API Endpoints
**Query File Information**
**POST** `/share/select/`
Request parameters:
```json
{
"code": "123456"
}
```
Response example (file):
```json
{
"code": 200,
"detail": {
"code": "123456",
"name": "example.pdf",
"size": 1048576,
"text": "https://example.com/download/..."
}
}
```
Response example (text):
```json
{
"code": 200,
"detail": {
"code": "123456",
"name": "Text",
"size": 1024,
"text": "This is the shared text content..."
}
}
```
**Direct File Download**
**GET** `/share/select/?code=123456`
This endpoint returns file content directly, suitable for direct browser access.
## Chunked Upload (Large Files)
For large file uploads, FileCodeBox supports chunked upload functionality. This feature requires administrator enablement (`enableChunk=1`).
### Chunked Upload Flow
```mermaid
sequenceDiagram
participant C as Client
participant S as Server
C->>S: 1. Initialize upload (POST /chunk/upload/init/)
S-->>C: Return upload_id and chunk info
loop Each chunk
C->>S: 2. Upload chunk (POST /chunk/upload/chunk/{upload_id}/{chunk_index})
S-->>C: Return chunk hash
end
C->>S: 3. Complete upload (POST /chunk/upload/complete/{upload_id})
S-->>C: Return extraction code
```
### 1. Initialize Upload
**POST** `/chunk/upload/init/`
Request parameters:
```json
{
"file_name": "large_file.zip",
"file_size": 104857600,
"chunk_size": 5242880,
"file_hash": "sha256_hash_of_file"
}
```
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `file_name` | string | Yes | Filename |
| `file_size` | int | Yes | Total file size (bytes) |
| `chunk_size` | int | No | Chunk size, default 5MB |
| `file_hash` | string | Yes | SHA256 hash of the file |
Response example:
```json
{
"code": 200,
"detail": {
"existed": false,
"upload_id": "abc123def456",
"chunk_size": 5242880,
"total_chunks": 20,
"uploaded_chunks": []
}
}
```
### 2. Upload Chunk
**POST** `/chunk/upload/chunk/{upload_id}/{chunk_index}`
- `upload_id` - Upload session ID returned during initialization
- `chunk_index` - Chunk index, starting from 0
Request body: Chunk file data (multipart/form-data)
Response example:
```json
{
"code": 200,
"detail": {
"chunk_hash": "sha256_hash_of_chunk"
}
}
```
### 3. Complete Upload
**POST** `/chunk/upload/complete/{upload_id}`
Request parameters:
```json
{
"expire_value": 1,
"expire_style": "day"
}
```
Response example:
```json
{
"code": 200,
"detail": {
"code": "789012",
"name": "large_file.zip"
}
}
```
### Resume Upload
Chunked upload supports resume functionality. If upload is interrupted:
1. Call the initialization endpoint again with the same `file_hash`
2. Server returns `uploaded_chunks` list containing already uploaded chunk indices
3. Client only needs to upload chunks not in the list
## Error Handling
### Common Error Codes
| Error Code | Description | Solution |
|------------|-------------|----------|
| 403 | File size exceeds limit | Reduce file size or contact administrator to adjust limit |
| 403 | Content too large | Text exceeds 222KB, use file sharing instead |
| 403 | Upload rate limit | Wait a while before retrying |
| 404 | File not found | Check if extraction code is correct |
| 404 | File expired | File has expired or download count exhausted |
### Rate Limiting
To prevent abuse, the system has rate limits on upload and retrieval operations:
- **Upload limit**: Default max 10 uploads per minute
- **Error limit**: Default max 1 error attempt per minute
::: tip Tip
If you encounter rate limiting, wait for the limit time window to pass before retrying.
:::
## Next Steps
- [Configuration Guide](/en/guide/configuration) - Learn how to configure sharing-related settings
- [Storage Configuration](/en/guide/storage) - Learn about file storage methods
- [Security Settings](/en/guide/security) - Learn about security-related configurations
- [Admin Panel](/en/guide/management) - Learn how to manage shared files
+395
View File
@@ -0,0 +1,395 @@
# Storage Configuration
FileCodeBox supports multiple storage backends. You can choose the appropriate storage method based on your needs. This document details the configuration methods for various storage backends.
## Storage Types Overview
| Storage Type | Config Value | Description |
|--------------|--------------|-------------|
| Local Storage | `local` | Default storage method, files saved on local server |
| S3-Compatible Storage | `s3` | Supports AWS S3, Aliyun OSS, MinIO, etc. |
| OneDrive | `onedrive` | Microsoft OneDrive cloud storage (work/school accounts only) |
| WebDAV | `webdav` | Storage services supporting WebDAV protocol |
| OpenDAL | `opendal` | Integrate more storage services via OpenDAL |
## Local Storage
Local storage is the default storage method. Files are saved in the server's `data/` directory.
### Configuration Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `file_storage` | string | `local` | Storage type |
| `storage_path` | string | `""` | Custom storage path (optional) |
### Configuration Example
```bash
file_storage=local
storage_path=
```
### Notes
- Files are stored by default in `data/share/data/` directory
- Subdirectories are automatically created by date: `year/month/day/fileID/`
- In production, it's recommended to mount the `data/` directory to persistent storage
## S3-Compatible Storage
Supports all S3-compatible object storage services, including AWS S3, Aliyun OSS, MinIO, Tencent Cloud COS, etc.
### Configuration Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `file_storage` | string | - | Set to `s3` |
| `s3_access_key_id` | string | `""` | Access Key ID |
| `s3_secret_access_key` | string | `""` | Secret Access Key |
| `s3_bucket_name` | string | `""` | Bucket name |
| `s3_endpoint_url` | string | `""` | S3 endpoint URL |
| `s3_region_name` | string | `auto` | Region name |
| `s3_signature_version` | string | `s3v2` | Signature version (`s3v2` or `s3v4`) |
| `s3_hostname` | string | `""` | S3 hostname (alternative) |
| `s3_proxy` | int | `0` | Download through server proxy (1=yes, 0=no) |
| `aws_session_token` | string | `""` | AWS session token (optional) |
### AWS S3 Configuration Example
```bash
file_storage=s3
s3_access_key_id=AKIAIOSFODNN7EXAMPLE
s3_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
s3_bucket_name=my-filecodebox-bucket
s3_endpoint_url=https://s3.amazonaws.com
s3_region_name=us-east-1
s3_signature_version=s3v4
```
### Aliyun OSS Configuration Example
```bash
file_storage=s3
s3_access_key_id=YourAccessKeyId
s3_secret_access_key=YourSecretAccessKey
s3_bucket_name=bucket-name
s3_endpoint_url=https://bucket-name.oss-cn-hangzhou.aliyuncs.com
s3_region_name=oss-cn-hangzhou
s3_signature_version=s3v4
```
::: tip Aliyun OSS Endpoint Format
Endpoint URL format: `https://<bucket-name>.<region>.aliyuncs.com`
Common regions:
- Hangzhou: `oss-cn-hangzhou`
- Shanghai: `oss-cn-shanghai`
- Beijing: `oss-cn-beijing`
- Shenzhen: `oss-cn-shenzhen`
:::
### MinIO Configuration Example
```bash
file_storage=s3
s3_access_key_id=minioadmin
s3_secret_access_key=minioadmin
s3_bucket_name=filecodebox
s3_endpoint_url=http://localhost:9000
s3_region_name=us-east-1
s3_signature_version=s3v4
```
::: warning MinIO Notes
- `s3_endpoint_url` should be the MinIO API interface address
- `s3_region_name` should match the `Server Location` in MinIO configuration
- Ensure the bucket is created and has correct access permissions
:::
### Tencent Cloud COS Configuration Example
```bash
file_storage=s3
s3_access_key_id=YourSecretId
s3_secret_access_key=YourSecretKey
s3_bucket_name=bucket-name-1250000000
s3_endpoint_url=https://cos.ap-guangzhou.myqcloud.com
s3_region_name=ap-guangzhou
s3_signature_version=s3v4
```
### Proxy Download
When `s3_proxy=1`, file downloads are proxied through the server instead of directly from S3. This is useful when:
- S3 bucket doesn't allow public access
- Need to hide the actual storage address
- Network environment restricts direct S3 access
## OneDrive Storage
OneDrive storage supports saving files to Microsoft OneDrive cloud storage.
::: warning Important Limitation
OneDrive storage **only supports work or school accounts** and requires admin permissions to authorize the API. Personal accounts cannot use this feature.
:::
### Configuration Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `file_storage` | string | - | Set to `onedrive` |
| `onedrive_domain` | string | `""` | Azure AD domain |
| `onedrive_client_id` | string | `""` | Application (client) ID |
| `onedrive_username` | string | `""` | Account email |
| `onedrive_password` | string | `""` | Account password |
| `onedrive_root_path` | string | `filebox_storage` | Storage root directory in OneDrive |
| `onedrive_proxy` | int | `0` | Download through server proxy |
### Configuration Example
```bash
file_storage=onedrive
onedrive_domain=contoso.onmicrosoft.com
onedrive_client_id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
onedrive_username=user@contoso.onmicrosoft.com
onedrive_password=your_password
onedrive_root_path=filebox_storage
```
### Azure App Registration Steps
To use OneDrive storage, you need to register an application in the Azure portal:
#### 1. Get Domain
Log in to [Azure Portal](https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade), hover over your account in the top right corner, and the **Domain** shown in the popup is the `onedrive_domain` value.
#### 2. Register Application
1. Click **+ New registration** in the top left
2. Enter application name (e.g., FileCodeBox)
3. **Supported account types**: Select "Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts"
4. **Redirect URI**: Select `Web`, enter `http://localhost`
5. Click **Register**
#### 3. Get Client ID
After registration, find the **Application (client) ID** in the **Essentials** section on the app overview page. This is the `onedrive_client_id` value.
#### 4. Configure Authentication
1. Select **Authentication** in the left menu
2. Find **Allow public client flows**, select **Yes**
3. Click **Save**
#### 5. Configure API Permissions
1. Select **API permissions** in the left menu
2. Click **+ Add a permission**
3. Select **Microsoft Graph****Delegated permissions**
4. Check the following permissions:
- `openid`
- `Files.Read`
- `Files.Read.All`
- `Files.ReadWrite`
- `Files.ReadWrite.All`
- `User.Read`
5. Click **Add permissions**
6. Click **Grant admin consent for xxx**
7. After confirmation, permission status should show **Granted**
### Install Dependencies
Using OneDrive storage requires additional Python dependencies:
```bash
pip install msal Office365-REST-Python-Client
```
### Verify Configuration
You can use the following code to test if the configuration is correct:
```python
import msal
from office365.graph_client import GraphClient
domain = 'your_domain'
client_id = 'your_client_id'
username = 'your_username'
password = 'your_password'
def acquire_token_pwd():
authority_url = f'https://login.microsoftonline.com/{domain}'
app = msal.PublicClientApplication(
authority=authority_url,
client_id=client_id
)
result = app.acquire_token_by_username_password(
username=username,
password=password,
scopes=['https://graph.microsoft.com/.default']
)
return result
# Test connection
client = GraphClient(acquire_token_pwd)
me = client.me.get().execute_query()
print(f"Login successful: {me.user_principal_name}")
```
## WebDAV Storage
WebDAV storage supports saving files to any service that supports the WebDAV protocol, such as Nextcloud, ownCloud, Nutstore, etc.
### Configuration Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `file_storage` | string | - | Set to `webdav` |
| `webdav_url` | string | `""` | WebDAV server URL |
| `webdav_username` | string | `""` | WebDAV username |
| `webdav_password` | string | `""` | WebDAV password |
| `webdav_root_path` | string | `filebox_storage` | Storage root directory in WebDAV |
| `webdav_proxy` | int | `0` | Download through server proxy |
### Configuration Example
```bash
file_storage=webdav
webdav_url=https://dav.example.com/remote.php/dav/files/username/
webdav_username=your_username
webdav_password=your_password
webdav_root_path=filebox_storage
```
### Nextcloud Configuration Example
```bash
file_storage=webdav
webdav_url=https://your-nextcloud.com/remote.php/dav/files/username/
webdav_username=your_username
webdav_password=your_app_password
webdav_root_path=FileCodeBox
```
::: tip Nextcloud App Password
It's recommended to create an app password in Nextcloud instead of using your main password:
1. Log in to Nextcloud
2. Go to **Settings****Security**
3. Create a new app password in **Devices & sessions**
:::
### Nutstore Configuration Example
```bash
file_storage=webdav
webdav_url=https://dav.jianguoyun.com/dav/
webdav_username=your_email@example.com
webdav_password=your_app_password
webdav_root_path=FileCodeBox
```
::: tip Nutstore App Password
Nutstore requires an app password:
1. Log in to Nutstore web version
2. Go to **Account Info****Security Options**
3. Add an app password
:::
## OpenDAL Storage
OpenDAL is a unified data access layer that supports multiple storage services. Through OpenDAL, you can use Google Cloud Storage, Azure Blob Storage, and more.
### Configuration Parameters
| Parameter | Type | Description |
|-----------|------|-------------|
| `file_storage` | string | Set to `opendal` |
| `opendal_scheme` | string | Storage service type (e.g., `gcs`, `azblob`) |
| `opendal_<scheme>_<setting>` | string | Service-specific configuration parameters |
### Install Dependencies
```bash
pip install opendal
```
### Google Cloud Storage Configuration Example
```bash
file_storage=opendal
opendal_scheme=gcs
opendal_gcs_root=/filecodebox
opendal_gcs_bucket=your-bucket-name
opendal_gcs_credential=base64_encoded_credential
```
### Azure Blob Storage Configuration Example
```bash
file_storage=opendal
opendal_scheme=azblob
opendal_azblob_root=/filecodebox
opendal_azblob_container=your-container
opendal_azblob_account_name=your_account
opendal_azblob_account_key=your_key
```
### Supported Services
OpenDAL supports numerous storage services. For the complete list, see the [OpenDAL Official Documentation](https://opendal.apache.org/docs/rust/opendal/services/index.html).
Common services include:
- `gcs` - Google Cloud Storage
- `azblob` - Azure Blob Storage
- `obs` - Huawei Cloud OBS
- `oss` - Aliyun OSS (via OpenDAL)
- `cos` - Tencent Cloud COS (via OpenDAL)
- `hdfs` - Hadoop HDFS
- `ftp` - FTP server
- `sftp` - SFTP server
::: warning OpenDAL Notes
1. Services integrated via OpenDAL download through server proxy, consuming both storage service and server bandwidth
2. Compared to native S3/OneDrive support, OpenDAL may lack some debugging information
3. OpenDAL is written in Rust with good performance
:::
## Storage Selection Recommendations
| Scenario | Recommended Storage | Reason |
|----------|---------------------|--------|
| Personal/Small deployment | Local storage | Simple and easy, no extra configuration needed |
| Enterprise intranet | MinIO + S3 | Self-hosted object storage, data control |
| Public cloud deployment | Corresponding cloud provider S3 | Fast same-region access, low cost |
| Existing OneDrive | OneDrive | Utilize existing resources |
| Existing WebDAV | WebDAV | Good compatibility |
| Special storage needs | OpenDAL | Supports more storage services |
## Common Issues
### S3 Upload Failure
1. Check if Access Key and Secret Key are correct
2. Confirm bucket name and region configuration are correct
3. Check bucket access permission settings
4. Confirm signature version (`s3v2` or `s3v4`) matches service provider requirements
### OneDrive Authentication Failure
1. Confirm using a work/school account, not a personal account
2. Check if Azure app has been granted admin consent
3. Confirm API permissions are fully configured
4. Verify username and password are correct
### WebDAV Connection Failure
1. Check if WebDAV URL format is correct
2. Confirm username and password (or app password) are correct
3. Check if server supports WebDAV protocol
4. Confirm network connection is normal
View File
+380
View File
@@ -0,0 +1,380 @@
# File Upload
FileCodeBox provides multiple flexible file upload methods, supporting both regular upload and chunked upload to meet different scenario requirements.
## Upload Methods
FileCodeBox supports the following upload methods:
### Drag and Drop Upload
Drag files directly to the upload area to start uploading. This is the most convenient upload method.
1. Open the FileCodeBox homepage
2. Drag files from your file manager to the upload area
3. Release the mouse, file upload begins
4. Get the extraction code after upload completes
::: tip Tip
Drag and drop upload supports dragging multiple files simultaneously (depending on theme support).
:::
### Click Upload
Click the upload area to select files through the system file picker.
1. Click the "Select File" button in the upload area
2. Select the file to upload in the popup file picker
3. File upload begins after confirming selection
4. Get the extraction code after upload completes
### Paste Upload
Supports pasting images directly from clipboard for upload (supported by some themes).
1. Copy an image to clipboard (screenshot or copy image)
2. Use `Ctrl+V` (Windows/Linux) or `Cmd+V` (macOS) to paste in the upload area
3. Image upload starts automatically
4. Get the extraction code after upload completes
::: warning Note
Paste upload only supports image formats, not other file types. Specific support depends on the theme being used.
:::
## File Size Limits
### Default Limits
| Setting | Default | Description |
|---------|---------|-------------|
| `uploadSize` | 10MB | Maximum single file upload size |
### Modify Upload Limits
Administrators can modify upload size limits through the admin panel or configuration file:
```python
# Set maximum upload size to 100MB
uploadSize = 104857600 # 100 * 1024 * 1024
```
::: info Note
`uploadSize` is in bytes. Common conversions:
- 10MB = 10485760
- 50MB = 52428800
- 100MB = 104857600
- 500MB = 524288000
- 1GB = 1073741824
:::
### Exceeding Limit Handling
When an uploaded file exceeds the size limit, the system returns a 403 error:
```json
{
"detail": "Size exceeds limit, maximum is 10.00 MB"
}
```
## Regular Upload API
### File Upload Endpoint
**POST** `/share/file/`
Content-Type: `multipart/form-data`
**Request parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `file` | file | Yes | File to upload |
| `expire_value` | int | No | Expiration value, default 1 |
| `expire_style` | string | No | Expiration method, default `day` |
**Expiration method options:**
| Value | Description |
|-------|-------------|
| `day` | Expire by days |
| `hour` | Expire by hours |
| `minute` | Expire by minutes |
| `forever` | Never expire |
| `count` | Expire by download count |
**Response example:**
```json
{
"code": 200,
"detail": {
"code": "654321",
"name": "example.pdf"
}
}
```
**cURL example:**
```bash
curl -X POST "http://localhost:12345/share/file/" \
-F "file=@/path/to/file.pdf" \
-F "expire_value=7" \
-F "expire_style=day"
```
## Chunked Upload API
For large files, FileCodeBox supports chunked upload functionality. Chunked upload splits large files into multiple small chunks for separate uploading, supporting resume capability.
::: warning Prerequisite
Chunked upload functionality requires administrator enablement: `enableChunk=1`
:::
### Chunked Upload Flow
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Initialize │ ──▶ │Upload Chunks│ ──▶ │ Complete │
│ /init/ │ │ /chunk/ │ │ /complete/ │
└─────────────┘ └─────────────┘ └─────────────┘
┌───────────┐
│ Loop │
│each chunk │
└───────────┘
```
### 1. Initialize Upload
**POST** `/chunk/upload/init/`
**Request parameters:**
```json
{
"file_name": "large_file.zip",
"file_size": 104857600,
"chunk_size": 5242880,
"file_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
```
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `file_name` | string | Yes | - | Filename |
| `file_size` | int | Yes | - | Total file size (bytes) |
| `chunk_size` | int | No | 5MB | Chunk size (bytes) |
| `file_hash` | string | Yes | - | SHA256 hash of the file |
**Response example:**
```json
{
"code": 200,
"detail": {
"existed": false,
"upload_id": "abc123def456789",
"chunk_size": 5242880,
"total_chunks": 20,
"uploaded_chunks": []
}
}
```
| Field | Description |
|-------|-------------|
| `existed` | Whether file already exists (instant upload) |
| `upload_id` | Upload session ID |
| `chunk_size` | Chunk size |
| `total_chunks` | Total number of chunks |
| `uploaded_chunks` | List of already uploaded chunk indices |
### 2. Upload Chunk
**POST** `/chunk/upload/chunk/{upload_id}/{chunk_index}`
**Path parameters:**
| Parameter | Description |
|-----------|-------------|
| `upload_id` | Upload session ID returned during initialization |
| `chunk_index` | Chunk index, starting from 0 |
**Request body:**
Content-Type: `multipart/form-data`
| Parameter | Type | Description |
|-----------|------|-------------|
| `chunk` | file | Chunk data |
**Response example:**
```json
{
"code": 200,
"detail": {
"chunk_hash": "a1b2c3d4e5f6..."
}
}
```
**cURL example:**
```bash
# Upload first chunk (index 0)
curl -X POST "http://localhost:12345/chunk/upload/chunk/abc123def456789/0" \
-F "chunk=@/path/to/chunk_0"
```
### 3. Complete Upload
**POST** `/chunk/upload/complete/{upload_id}`
**Path parameters:**
| Parameter | Description |
|-----------|-------------|
| `upload_id` | Upload session ID |
**Request parameters:**
```json
{
"expire_value": 7,
"expire_style": "day"
}
```
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `expire_value` | int | Yes | Expiration value |
| `expire_style` | string | Yes | Expiration method |
**Response example:**
```json
{
"code": 200,
"detail": {
"code": "789012",
"name": "large_file.zip"
}
}
```
### Resume Upload
Chunked upload supports resume functionality. When upload is interrupted:
1. Call the initialization endpoint again with the same `file_hash`
2. Server returns `uploaded_chunks` list containing already uploaded chunk indices
3. Client only needs to upload chunks not in the list
4. Call the complete endpoint after all chunks are uploaded
**Example flow:**
```javascript
// 1. Initialize upload
const initResponse = await fetch('/chunk/upload/init/', {
method: 'POST',
body: JSON.stringify({
file_name: 'large_file.zip',
file_size: fileSize,
chunk_size: 5 * 1024 * 1024,
file_hash: fileHash
})
});
const { upload_id, uploaded_chunks, total_chunks } = await initResponse.json();
// 2. Upload incomplete chunks
for (let i = 0; i < total_chunks; i++) {
if (!uploaded_chunks.includes(i)) {
const chunk = file.slice(i * chunkSize, (i + 1) * chunkSize);
await fetch(`/chunk/upload/chunk/${upload_id}/${i}`, {
method: 'POST',
body: chunk
});
}
}
// 3. Complete upload
await fetch(`/chunk/upload/complete/${upload_id}`, {
method: 'POST',
body: JSON.stringify({
expire_value: 7,
expire_style: 'day'
})
});
```
## Error Handling
### Common Errors
| HTTP Status | Error Message | Cause | Solution |
|-------------|---------------|-------|----------|
| 403 | Size exceeds limit | File exceeds `uploadSize` limit | Reduce file size or contact administrator to adjust limit |
| 403 | Upload rate limit | Exceeded IP upload rate limit | Wait for limit time window before retrying |
| 400 | Invalid expiration type | `expire_style` value not in allowed list | Use a valid expiration method |
| 404 | Upload session not found | `upload_id` invalid or expired | Re-initialize upload |
| 400 | Invalid chunk index | `chunk_index` out of range | Check if chunk index is correct |
| 400 | Incomplete chunks | Chunk count insufficient when completing upload | Ensure all chunks are uploaded |
### Rate Limiting
The system has rate limits on upload operations to prevent abuse:
| Setting | Default | Description |
|---------|---------|-------------|
| `uploadMinute` | 1 | Limit time window (minutes) |
| `uploadCount` | 10 | Maximum uploads within time window |
When rate limit is exceeded, you need to wait for the time window to pass before continuing uploads.
### Error Response Format
```json
{
"detail": "Error message description"
}
```
## Upload Configuration
### Related Settings
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `openUpload` | int | 1 | Enable upload (1=enabled, 0=disabled) |
| `uploadSize` | int | 10485760 | Maximum upload size (bytes) |
| `enableChunk` | int | 0 | Enable chunked upload (1=enabled, 0=disabled) |
| `uploadMinute` | int | 1 | Upload rate limit time window (minutes) |
| `uploadCount` | int | 10 | Maximum uploads within time window |
| `expireStyle` | list | ["day","hour","minute","forever","count"] | Allowed expiration methods |
### Configuration Example
```python
# Allow 100MB file uploads, enable chunked upload
uploadSize = 104857600
enableChunk = 1
# Relax upload rate limit: max 50 uploads per 5 minutes
uploadMinute = 5
uploadCount = 50
# Only allow expiration by days and count
expireStyle = ["day", "count"]
```
## Next Steps
- [File Sharing](/en/guide/share) - Learn the complete sharing process
- [Configuration Guide](/en/guide/configuration) - Learn about all configuration options
- [Storage Configuration](/en/guide/storage) - Learn about file storage methods
- [Security Settings](/en/guide/security) - Learn about security-related configurations
+206
View File
@@ -0,0 +1,206 @@
# 配置说明
FileCodeBox 提供了丰富的配置选项,可以通过管理面板或直接修改配置来自定义系统行为。本文档详细介绍所有可用的配置项。
## 配置方式
FileCodeBox 支持两种配置方式:
1. **管理面板配置**(推荐):访问 `/admin` 进入管理面板,在设置页面修改配置
2. **数据库配置**:配置存储在 `data/filecodebox.db` 数据库中
::: tip 提示
首次启动时,系统会使用 `core/settings.py` 中的默认配置。修改后的配置会保存到数据库中。
:::
## 基础设置
### 站点信息
| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `name` | string | `文件快递柜 - FileCodeBox` | 站点名称,显示在页面标题和导航栏 |
| `description` | string | `开箱即用的文件快传系统` | 站点描述,用于 SEO |
| `keywords` | string | `FileCodeBox, 文件快递柜...` | 站点关键词,用于 SEO |
| `port` | int | `12345` | 服务监听端口 |
### 通知设置
| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `notify_title` | string | `系统通知` | 通知标题 |
| `notify_content` | string | 欢迎信息 | 通知内容,支持 HTML |
| `page_explain` | string | 法律声明 | 页面底部说明文字 |
| `robotsText` | string | `User-agent: *\nDisallow: /` | robots.txt 内容 |
## 上传设置
### 文件上传限制
| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `openUpload` | int | `1` | 是否开启上传功能(1=开启,0=关闭) |
| `uploadSize` | int | `10485760` | 单文件最大上传大小(字节),默认 10MB |
| `enableChunk` | int | `0` | 是否启用分片上传(1=启用,0=禁用) |
::: warning 注意
`uploadSize` 的单位是字节。10MB = 10 * 1024 * 1024 = 10485760 字节
:::
### 上传频率限制
| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `uploadMinute` | int | `1` | 上传限制的时间窗口(分钟) |
| `uploadCount` | int | `10` | 在时间窗口内允许的最大上传次数 |
例如:默认配置表示每 1 分钟内最多允许上传 10 次。
### 文件过期设置
| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `expireStyle` | list | `["day","hour","minute","forever","count"]` | 可选的过期方式 |
| `max_save_seconds` | int | `0` | 文件最大保存时间(秒),0 表示不限制 |
过期方式说明:
- `day` - 按天过期
- `hour` - 按小时过期
- `minute` - 按分钟过期
- `forever` - 永不过期
- `count` - 按下载次数过期
## 主题设置
### 主题选择
| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `themesSelect` | string | `themes/2024` | 当前使用的主题 |
| `themesChoices` | list | 见下方 | 可用主题列表 |
默认可用主题:
```json
[
{
"name": "2023",
"key": "themes/2023",
"author": "Lan",
"version": "1.0"
},
{
"name": "2024",
"key": "themes/2024",
"author": "Lan",
"version": "1.0"
}
]
```
### 界面样式
| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `opacity` | float | `0.9` | 界面透明度(0-1 |
| `background` | string | `""` | 自定义背景图片 URL,为空则使用默认背景 |
## 管理员设置
| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `admin_token` | string | `FileCodeBox2023` | 管理员登录密码 |
| `showAdminAddr` | int | `0` | 是否在首页显示管理入口(1=显示,0=隐藏) |
::: danger 安全警告
请务必在生产环境中修改默认的 `admin_token`!使用默认密码会导致严重的安全风险。
:::
## 安全设置
### 错误次数限制
| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `errorMinute` | int | `1` | 错误限制的时间窗口(分钟) |
| `errorCount` | int | `1` | 在时间窗口内允许的最大错误次数 |
此设置用于防止暴力破解提取码。
## 存储设置
### 存储类型
| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `file_storage` | string | `local` | 存储后端类型 |
| `storage_path` | string | `""` | 自定义存储路径 |
支持的存储类型:
- `local` - 本地存储
- `s3` - S3 兼容存储(AWS S3、阿里云 OSS、MinIO 等)
- `onedrive` - OneDrive 存储
- `webdav` - WebDAV 存储
- `opendal` - OpenDAL 存储
详细的存储配置请参考 [存储配置](/guide/storage)。
## 配置示例
### 示例 1:小型个人使用
适合个人或小团队使用,限制较宽松:
```python
{
"name": "我的文件分享",
"uploadSize": 52428800, # 50MB
"uploadMinute": 5, # 5分钟
"uploadCount": 20, # 最多20次
"expireStyle": ["day", "hour", "forever"],
"admin_token": "your-secure-password",
"showAdminAddr": 1
}
```
### 示例 2:公开服务
适合公开服务,需要更严格的限制:
```python
{
"name": "公共文件快递柜",
"uploadSize": 10485760, # 10MB
"uploadMinute": 1, # 1分钟
"uploadCount": 5, # 最多5次
"errorMinute": 5, # 5分钟
"errorCount": 3, # 最多3次错误
"expireStyle": ["hour", "minute", "count"],
"max_save_seconds": 86400, # 最长保存1天
"admin_token": "very-secure-password-123",
"showAdminAddr": 0
}
```
### 示例 3:企业内部使用
适合企业内部使用,支持大文件和分片上传:
```python
{
"name": "企业文件中转站",
"uploadSize": 1073741824, # 1GB
"enableChunk": 1, # 启用分片上传
"uploadMinute": 10, # 10分钟
"uploadCount": 100, # 最多100次
"expireStyle": ["day", "forever"],
"file_storage": "s3", # 使用S3存储
"admin_token": "enterprise-secure-token",
"showAdminAddr": 1
}
```
## 下一步
- [存储配置](/guide/storage) - 了解如何配置不同的存储后端
- [安全设置](/guide/security) - 了解如何增强系统安全性
- [文件分享](/guide/share) - 了解文件分享功能
+412
View File
@@ -0,0 +1,412 @@
# 管理面板
FileCodeBox 提供了功能完善的管理面板,让管理员可以方便地管理文件、查看系统状态和修改配置。本文档介绍管理面板的各项功能和使用方法。
## 访问管理面板
### 登录方式
管理面板位于 `/admin` 路径。访问方式:
1. 在浏览器中访问 `http://your-domain.com/admin`
2. 输入管理员密码(`admin_token` 配置项的值)
3. 点击登录按钮
::: tip 提示
默认管理员密码是 `FileCodeBox2023`。请务必在生产环境中修改此密码,详见 [安全设置](/guide/security)。
:::
### 显示管理入口
默认情况下,首页不显示管理面板入口。您可以通过配置控制是否显示:
| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `showAdminAddr` | int | `0` | 是否在首页显示管理入口(1=显示,0=隐藏) |
::: warning 安全建议
在公开服务中,建议保持 `showAdminAddr``0`,通过直接访问 `/admin` 路径进入管理面板,减少被恶意扫描的风险。
:::
### 认证机制
管理面板使用 JWTJSON Web Token)进行身份认证:
1. 登录成功后,服务器返回一个包含管理员身份的 Token
2. 后续请求通过 `Authorization: Bearer <token>` 头部携带 Token
3. Token 用于验证管理员身份,确保只有授权用户可以访问管理功能
## 仪表盘
登录后首先看到的是仪表盘页面,展示系统的整体运行状态。
### 统计指标
仪表盘显示以下关键指标:
| 指标 | 说明 |
|------|------|
| **文件总数** (`totalFiles`) | 系统中存储的文件总数量 |
| **存储使用量** (`storageUsed`) | 所有文件占用的总存储空间(字节) |
| **系统运行时间** (`sysUptime`) | 系统首次启动的时间 |
| **昨日上传数** (`yesterdayCount`) | 昨天一整天上传的文件数量 |
| **昨日上传量** (`yesterdaySize`) | 昨天上传文件的总大小(字节) |
| **今日上传数** (`todayCount`) | 今天到目前为止上传的文件数量 |
| **今日上传量** (`todaySize`) | 今天上传文件的总大小(字节) |
### 指标说明
- **文件总数**:包括所有未过期的文件和文本分享
- **存储使用量**:显示实际文件占用的存储空间,不包括数据库等系统文件
- **昨日/今日统计**:基于文件创建时间计算,用于了解系统使用趋势
::: tip 提示
存储使用量显示的是字节数。例如 `10485760` 表示约 10MB。
:::
## 文件管理
### 文件列表
文件管理页面展示系统中所有已分享的文件,支持分页浏览和搜索。
**列表信息包括:**
- 文件 ID
- 提取码(code
- 文件名前缀(prefix
- 文件后缀(suffix
- 文件大小
- 创建时间
- 过期时间
- 剩余下载次数
### 搜索文件
使用搜索功能可以快速找到特定文件:
1. 在搜索框中输入关键词
2. 系统会根据文件名前缀(prefix)进行模糊匹配
3. 搜索结果实时更新
**搜索示例:**
- 输入 `report` 可以找到所有文件名包含 "report" 的文件
- 输入 `.pdf` 可以找到所有 PDF 文件(如果文件名包含此字符串)
### 分页浏览
文件列表支持分页显示:
| 参数 | 默认值 | 说明 |
|------|--------|------|
| `page` | `1` | 当前页码 |
| `size` | `10` | 每页显示数量 |
### 删除文件
管理员可以删除任意文件:
1. 在文件列表中找到要删除的文件
2. 点击删除按钮
3. 确认删除操作
::: danger 警告
删除操作不可恢复!文件将从存储后端永久删除,同时删除数据库中的记录。
:::
**删除流程:**
1. 系统首先从存储后端(本地/S3/OneDrive 等)删除实际文件
2. 然后从数据库中删除文件记录
3. 删除后,对应的提取码将失效
### 下载文件
管理员可以直接下载任意文件:
1. 在文件列表中找到目标文件
2. 点击下载按钮
3. 文件将通过浏览器下载
对于文本分享,系统会直接返回文本内容而不是下载文件。
### 修改文件信息
管理员可以修改已分享文件的部分信息:
| 可修改字段 | 说明 |
|------------|------|
| `code` | 提取码(必须唯一,不能与其他文件重复) |
| `prefix` | 文件名前缀 |
| `suffix` | 文件后缀名 |
| `expired_at` | 过期时间 |
| `expired_count` | 剩余下载次数 |
**修改提取码:**
```
原提取码:abc123
新提取码:myfile2024
```
::: warning 注意
修改提取码时,系统会检查新提取码是否已被使用。如果已存在相同的提取码,修改将失败。
:::
## 本地文件管理
除了管理已分享的文件,管理面板还提供了本地文件管理功能,用于管理 `data/local` 目录中的文件。
### 查看本地文件
本地文件列表显示 `data/local` 目录中的所有文件:
| 信息 | 说明 |
|------|------|
| 文件名 | 文件的完整名称 |
| 创建时间 | 文件的创建时间 |
| 文件大小 | 文件大小(字节) |
### 分享本地文件
可以将本地文件快速分享:
1. 在本地文件列表中选择要分享的文件
2. 设置过期方式和过期值
3. 点击分享按钮
4. 系统生成提取码
**分享参数:**
| 参数 | 说明 |
|------|------|
| `filename` | 要分享的文件名 |
| `expire_style` | 过期方式(day/hour/minute/forever/count |
| `expire_value` | 过期值(天数/小时数/分钟数/下载次数) |
### 删除本地文件
可以删除 `data/local` 目录中的文件:
1. 在本地文件列表中找到要删除的文件
2. 点击删除按钮
3. 确认删除
::: tip 使用场景
本地文件管理功能适用于:
- 批量上传文件到服务器后进行分享
- 管理通过其他方式上传到服务器的文件
- 清理不需要的本地文件
:::
## 系统设置
### 查看配置
在系统设置页面可以查看当前所有配置项的值。配置项按类别分组显示:
- 基础设置(站点名称、描述等)
- 上传设置(文件大小限制、频率限制等)
- 存储设置(存储类型、路径等)
- 主题设置(主题选择、透明度等)
- 安全设置(管理员密码、错误限制等)
### 修改配置
管理员可以通过管理面板修改大部分配置:
1. 进入系统设置页面
2. 找到要修改的配置项
3. 输入新的值
4. 点击保存按钮
**可修改的配置项:**
| 类别 | 配置项示例 |
|------|------------|
| 基础设置 | `name`, `description`, `keywords`, `notify_title`, `notify_content` |
| 上传设置 | `uploadSize`, `uploadMinute`, `uploadCount`, `openUpload`, `enableChunk` |
| 过期设置 | `expireStyle`, `max_save_seconds` |
| 主题设置 | `themesSelect`, `opacity`, `background` |
| 安全设置 | `admin_token`, `showAdminAddr`, `errorMinute`, `errorCount` |
| 存储设置 | `file_storage`, `storage_path` 及各存储后端的配置 |
::: warning 注意
- `admin_token`(管理员密码)不能设置为空
- `themesChoices`(主题列表)不可通过管理面板修改
- 修改存储设置后,已有文件不会自动迁移
:::
### 配置生效
配置修改后立即生效,无需重启服务。配置保存在数据库中,重启后仍然有效。
**配置存储位置:**
- 数据库:`data/filecodebox.db`
- 表名:`keyvalue`
- 键名:`settings`
## API 接口
管理面板的所有功能都通过 REST API 实现,以下是主要接口:
### 认证接口
**登录**
```
POST /admin/login
Content-Type: application/json
{
"password": "your-admin-password"
}
```
响应:
```json
{
"code": 200,
"detail": {
"token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer"
}
}
```
### 仪表盘接口
**获取统计数据**
```
GET /admin/dashboard
Authorization: Bearer <token>
```
### 文件管理接口
**获取文件列表**
```
GET /admin/file/list?page=1&size=10&keyword=
Authorization: Bearer <token>
```
**删除文件**
```
DELETE /admin/file/delete
Authorization: Bearer <token>
Content-Type: application/json
{
"id": 123
}
```
**下载文件**
```
GET /admin/file/download?id=123
Authorization: Bearer <token>
```
**修改文件信息**
```
PATCH /admin/file/update
Authorization: Bearer <token>
Content-Type: application/json
{
"id": 123,
"code": "newcode",
"expired_at": "2024-12-31T23:59:59"
}
```
### 本地文件接口
**获取本地文件列表**
```
GET /admin/local/lists
Authorization: Bearer <token>
```
**删除本地文件**
```
DELETE /admin/local/delete
Authorization: Bearer <token>
Content-Type: application/json
{
"filename": "example.txt"
}
```
**分享本地文件**
```
POST /admin/local/share
Authorization: Bearer <token>
Content-Type: application/json
{
"filename": "example.txt",
"expire_style": "day",
"expire_value": 7
}
```
### 配置接口
**获取配置**
```
GET /admin/config/get
Authorization: Bearer <token>
```
**更新配置**
```
PATCH /admin/config/update
Authorization: Bearer <token>
Content-Type: application/json
{
"admin_token": "new-password",
"uploadSize": 52428800
}
```
## 常见问题
### 忘记管理员密码
如果忘记了管理员密码,可以通过以下方式重置:
1. 停止 FileCodeBox 服务
2. 使用 SQLite 工具打开 `data/filecodebox.db`
3. 查询 `keyvalue` 表中 `key='settings'` 的记录
4. 修改 JSON 中的 `admin_token`
5. 重启服务
```sql
-- 查看当前配置
SELECT * FROM keyvalue WHERE key = 'settings';
-- 或者删除配置,恢复默认密码
DELETE FROM keyvalue WHERE key = 'settings';
```
### 文件删除失败
如果删除文件时出现错误,可能的原因:
1. **存储后端连接失败**:检查存储配置是否正确
2. **文件已不存在**:文件可能已被手动删除
3. **权限不足**:检查存储目录的写入权限
### 配置修改不生效
如果修改配置后没有生效:
1. 检查是否点击了保存按钮
2. 刷新页面查看配置是否已保存
3. 检查浏览器控制台是否有错误信息
4. 确认配置值的格式是否正确(如数字类型不要输入字符串)
## 下一步
- [配置说明](/guide/configuration) - 了解所有配置选项的详细说明
- [安全设置](/guide/security) - 了解如何增强系统安全性
- [存储配置](/guide/storage) - 配置不同的存储后端
+324
View File
@@ -0,0 +1,324 @@
# 安全设置
FileCodeBox 提供了多层安全机制来保护您的文件分享服务。本文档介绍如何正确配置安全选项,确保系统安全运行。
## 管理员密码
### 修改默认密码
::: danger 重要安全警告
FileCodeBox 的默认管理员密码是 `FileCodeBox2023`。**在生产环境中必须立即修改此密码!**使用默认密码会导致任何人都可以访问您的管理面板。
:::
修改管理员密码有两种方式:
**方式一:通过管理面板修改(推荐)**
1. 访问 `/admin` 进入管理面板
2. 使用当前密码登录
3. 进入「系统设置」页面
4. 找到 `admin_token` 配置项
5. 输入新的安全密码并保存
**方式二:通过数据库修改**
配置存储在 `data/filecodebox.db` 数据库的 `keyvalue` 表中,可以直接修改 `admin_token` 的值。
### 密码安全建议
- 使用至少 16 个字符的强密码
- 包含大小写字母、数字和特殊字符
- 避免使用常见词汇或个人信息
- 定期更换密码
```python
# 推荐的密码格式示例
"admin_token": "Xk9#mP2$vL5@nQ8&wR3"
```
### 隐藏管理入口
默认情况下,管理面板入口是隐藏的。您可以通过 `showAdminAddr` 配置控制是否在首页显示管理入口:
| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `showAdminAddr` | int | `0` | 是否显示管理入口(1=显示,0=隐藏) |
::: tip 建议
在公开服务中,建议保持 `showAdminAddr``0`,通过直接访问 `/admin` 路径进入管理面板。
:::
## IP 速率限制
FileCodeBox 内置了基于 IP 的速率限制机制,可以有效防止滥用和攻击。
### 上传频率限制
限制单个 IP 在指定时间内的上传次数:
| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `uploadMinute` | int | `1` | 上传限制的时间窗口(分钟) |
| `uploadCount` | int | `10` | 在时间窗口内允许的最大上传次数 |
**工作原理:**
- 系统记录每个 IP 的上传请求
- 当某 IP 在 `uploadMinute` 分钟内的上传次数达到 `uploadCount`
- 该 IP 的后续上传请求将被拒绝,返回 HTTP 423 错误
- 等待时间窗口过期后,计数器重置
**配置示例:**
```python
# 宽松配置:5分钟内最多上传20次
{
"uploadMinute": 5,
"uploadCount": 20
}
# 严格配置:1分钟内最多上传3次
{
"uploadMinute": 1,
"uploadCount": 3
}
```
### 错误次数限制
限制单个 IP 的错误尝试次数,防止暴力破解提取码:
| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `errorMinute` | int | `1` | 错误限制的时间窗口(分钟) |
| `errorCount` | int | `1` | 在时间窗口内允许的最大错误次数 |
**工作原理:**
- 当用户输入错误的提取码时,系统记录该 IP 的错误次数
- 当错误次数达到 `errorCount` 时,该 IP 将被暂时锁定
- 锁定时间为 `errorMinute` 分钟
- 锁定期间,该 IP 的所有提取请求都将被拒绝
**配置示例:**
```python
# 防暴力破解配置:5分钟内最多允许3次错误
{
"errorMinute": 5,
"errorCount": 3
}
```
::: warning 注意
默认配置 `errorMinute=1, errorCount=1` 非常严格,意味着输入一次错误的提取码后需要等待1分钟才能重试。根据实际需求调整此配置。
:::
## 上传限制
### 文件大小限制
| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `uploadSize` | int | `10485760` | 单文件最大上传大小(字节),默认 10MB |
| `openUpload` | int | `1` | 是否开启上传功能(1=开启,0=关闭) |
**常用大小换算:**
- 10MB = 10 * 1024 * 1024 = `10485760`
- 50MB = 50 * 1024 * 1024 = `52428800`
- 100MB = 100 * 1024 * 1024 = `104857600`
- 1GB = 1024 * 1024 * 1024 = `1073741824`
### 文件过期设置
通过文件过期机制,可以自动清理过期文件,减少存储占用和安全风险:
| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `expireStyle` | list | `["day","hour","minute","forever","count"]` | 可选的过期方式 |
| `max_save_seconds` | int | `0` | 文件最大保存时间(秒),0 表示不限制 |
**过期方式说明:**
- `day` - 按天数过期
- `hour` - 按小时过期
- `minute` - 按分钟过期
- `forever` - 永不过期(需要字符串提取码)
- `count` - 按下载次数过期
**安全建议:**
对于公开服务,建议:
1. 移除 `forever` 选项,避免文件永久存储
2. 设置 `max_save_seconds` 限制最长保存时间
3. 优先使用 `count` 方式,下载后自动删除
```python
# 公开服务推荐配置
{
"expireStyle": ["hour", "minute", "count"],
"max_save_seconds": 86400 # 最长保存1天
}
```
### 关闭上传功能
在某些情况下,您可能需要临时关闭上传功能:
```python
{
"openUpload": 0 # 关闭上传功能
}
```
## 反向代理安全配置
在生产环境中,通常会使用 Nginx 或其他反向代理服务器。以下是安全配置建议:
### Nginx 配置示例
```nginx
server {
listen 80;
server_name your-domain.com;
# 强制 HTTPS 重定向
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name your-domain.com;
# SSL 证书配置
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers on;
# 安全头部
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# 限制请求体大小(与 uploadSize 配置一致)
client_max_body_size 100M;
# 传递真实 IP
location / {
proxy_pass http://127.0.0.1:12345;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 静态资源缓存
location /assets {
proxy_pass http://127.0.0.1:12345;
proxy_cache_valid 200 7d;
add_header Cache-Control "public, max-age=604800";
}
}
```
### 关键安全配置说明
**1. 传递真实 IP**
FileCodeBox 的 IP 限制功能依赖于获取客户端真实 IP。系统会按以下顺序获取 IP:
1. `X-Real-IP` 请求头
2. `X-Forwarded-For` 请求头
3. 直接连接的客户端 IP
确保反向代理正确设置这些头部:
```nginx
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
```
**2. 请求体大小限制**
Nginx 的 `client_max_body_size` 应该与 FileCodeBox 的 `uploadSize` 配置一致或略大:
```nginx
client_max_body_size 100M; # 允许上传最大 100MB
```
**3. HTTPS 加密**
强烈建议在生产环境中启用 HTTPS:
- 保护用户上传的文件内容
- 保护管理员登录凭据
- 防止中间人攻击
### Caddy 配置示例
```nginx
your-domain.com {
reverse_proxy localhost:12345
header {
X-Frame-Options "SAMEORIGIN"
X-Content-Type-Options "nosniff"
X-XSS-Protection "1; mode=block"
Strict-Transport-Security "max-age=31536000; includeSubDomains"
}
}
```
## 安全检查清单
部署 FileCodeBox 前,请确认以下安全配置:
- [ ] 已修改默认管理员密码 `admin_token`
- [ ] 已隐藏管理入口 `showAdminAddr: 0`
- [ ] 已配置合适的上传频率限制
- [ ] 已配置错误次数限制防止暴力破解
- [ ] 已设置合理的文件大小限制
- [ ] 已配置文件过期策略
- [ ] 已启用 HTTPS 加密
- [ ] 反向代理已正确传递真实 IP
- [ ] 已设置安全响应头部
## 推荐安全配置
### 公开服务配置
```python
{
"admin_token": "your-very-secure-password",
"showAdminAddr": 0,
"uploadSize": 10485760, # 10MB
"uploadMinute": 1,
"uploadCount": 5,
"errorMinute": 5,
"errorCount": 3,
"expireStyle": ["hour", "minute", "count"],
"max_save_seconds": 86400, # 最长1天
"openUpload": 1
}
```
### 内部服务配置
```python
{
"admin_token": "internal-secure-password",
"showAdminAddr": 1,
"uploadSize": 104857600, # 100MB
"uploadMinute": 5,
"uploadCount": 50,
"errorMinute": 1,
"errorCount": 5,
"expireStyle": ["day", "hour", "forever"],
"max_save_seconds": 0, # 不限制
"openUpload": 1
}
```
## 下一步
- [配置说明](/guide/configuration) - 了解所有配置选项
- [存储配置](/guide/storage) - 配置安全的存储后端
- [文件分享](/guide/share) - 了解文件分享功能
+341
View File
@@ -0,0 +1,341 @@
# 文件分享
FileCodeBox 提供了简单易用的文件和文本分享功能。用户可以通过提取码安全地分享和获取文件。
## 分享方式
FileCodeBox 支持两种分享方式:
1. **文本分享** - 直接分享文本内容,适合代码片段、配置文件等
2. **文件分享** - 上传文件进行分享,支持各种文件格式
## 文本分享
### 使用方法
1. 在首页选择「文本分享」标签
2. 在文本框中输入或粘贴要分享的内容
3. 选择过期方式和时间
4. 点击「分享」按钮
5. 获取提取码
### 文本大小限制
::: warning 注意
文本分享的最大内容大小为 **222KB**(227,328 字节)。如果内容超过此限制,建议使用文件分享方式。
:::
文本内容大小按 UTF-8 编码计算,中文字符通常占用 3 个字节。
### API 接口
**POST** `/share/text/`
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `text` | string | 是 | 要分享的文本内容 |
| `expire_value` | int | 否 | 过期数值,默认 1 |
| `expire_style` | string | 否 | 过期方式,默认 `day` |
响应示例:
```json
{
"code": 200,
"detail": {
"code": "123456"
}
}
```
## 文件分享
### 使用方法
1. 在首页选择「文件分享」标签
2. 点击上传区域或拖拽文件到上传区域
3. 选择过期方式和时间
4. 点击「上传」按钮
5. 获取提取码
### 文件大小限制
默认单文件最大上传大小为 **10MB**。管理员可以通过 `uploadSize` 配置项修改此限制。
::: tip 提示
如果需要上传大文件,请联系管理员启用分片上传功能,或调整 `uploadSize` 配置。
:::
### 支持的上传方式
- **点击上传** - 点击上传区域选择文件
- **拖拽上传** - 将文件拖拽到上传区域
- **粘贴上传** - 从剪贴板粘贴图片(部分主题支持)
### API 接口
**POST** `/share/file/`
请求参数(multipart/form-data):
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `file` | file | 是 | 要上传的文件 |
| `expire_value` | int | 否 | 过期数值,默认 1 |
| `expire_style` | string | 否 | 过期方式,默认 `day` |
响应示例:
```json
{
"code": 200,
"detail": {
"code": "654321",
"name": "example.pdf"
}
}
```
## 过期设置
FileCodeBox 支持多种灵活的过期方式:
| 过期方式 | 参数值 | 说明 |
|----------|--------|------|
| 按天过期 | `day` | 文件在指定天数后过期 |
| 按小时过期 | `hour` | 文件在指定小时后过期 |
| 按分钟过期 | `minute` | 文件在指定分钟后过期 |
| 永不过期 | `forever` | 文件永久有效 |
| 按次数过期 | `count` | 文件在被下载指定次数后过期 |
::: info 说明
- 管理员可以通过 `expireStyle` 配置项控制用户可选的过期方式
- 管理员可以通过 `max_save_seconds` 配置项限制文件的最长保存时间
:::
### 过期方式示例
```bash
# 文件 3 天后过期
expire_value=3, expire_style=day
# 文件 12 小时后过期
expire_value=12, expire_style=hour
# 文件 30 分钟后过期
expire_value=30, expire_style=minute
# 文件永不过期
expire_value=1, expire_style=forever
# 文件被下载 5 次后过期
expire_value=5, expire_style=count
```
## 提取文件
### 使用方法
1. 在首页的「提取文件」区域输入提取码
2. 点击「提取」按钮
3. 系统会显示文件信息(文件名、大小等)
4. 点击「下载」按钮下载文件,或直接查看文本内容
### 提取码说明
- 提取码通常为 **6 位数字**
- 永不过期的文件使用 **字母数字混合** 的提取码
- 提取码区分大小写(针对字母数字混合的情况)
### API 接口
**查询文件信息**
**POST** `/share/select/`
请求参数:
```json
{
"code": "123456"
}
```
响应示例(文件):
```json
{
"code": 200,
"detail": {
"code": "123456",
"name": "example.pdf",
"size": 1048576,
"text": "https://example.com/download/..."
}
}
```
响应示例(文本):
```json
{
"code": 200,
"detail": {
"code": "123456",
"name": "Text",
"size": 1024,
"text": "这是分享的文本内容..."
}
}
```
**直接下载文件**
**GET** `/share/select/?code=123456`
此接口会直接返回文件内容,适合在浏览器中直接访问。
## 分片上传(大文件)
对于大文件上传,FileCodeBox 支持分片上传功能。此功能需要管理员启用(`enableChunk=1`)。
### 分片上传流程
```mermaid
sequenceDiagram
participant C as 客户端
participant S as 服务器
C->>S: 1. 初始化上传 (POST /chunk/upload/init/)
S-->>C: 返回 upload_id 和分片信息
loop 每个分片
C->>S: 2. 上传分片 (POST /chunk/upload/chunk/{upload_id}/{chunk_index})
S-->>C: 返回分片哈希
end
C->>S: 3. 完成上传 (POST /chunk/upload/complete/{upload_id})
S-->>C: 返回提取码
```
### 1. 初始化上传
**POST** `/chunk/upload/init/`
请求参数:
```json
{
"file_name": "large_file.zip",
"file_size": 104857600,
"chunk_size": 5242880,
"file_hash": "sha256_hash_of_file"
}
```
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `file_name` | string | 是 | 文件名 |
| `file_size` | int | 是 | 文件总大小(字节) |
| `chunk_size` | int | 否 | 分片大小,默认 5MB |
| `file_hash` | string | 是 | 文件的 SHA256 哈希值 |
响应示例:
```json
{
"code": 200,
"detail": {
"existed": false,
"upload_id": "abc123def456",
"chunk_size": 5242880,
"total_chunks": 20,
"uploaded_chunks": []
}
}
```
### 2. 上传分片
**POST** `/chunk/upload/chunk/{upload_id}/{chunk_index}`
- `upload_id` - 初始化时返回的上传会话 ID
- `chunk_index` - 分片索引,从 0 开始
请求体:分片文件数据(multipart/form-data
响应示例:
```json
{
"code": 200,
"detail": {
"chunk_hash": "sha256_hash_of_chunk"
}
}
```
### 3. 完成上传
**POST** `/chunk/upload/complete/{upload_id}`
请求参数:
```json
{
"expire_value": 1,
"expire_style": "day"
}
```
响应示例:
```json
{
"code": 200,
"detail": {
"code": "789012",
"name": "large_file.zip"
}
}
```
### 断点续传
分片上传支持断点续传。如果上传中断,可以:
1. 重新调用初始化接口,使用相同的 `file_hash`
2. 服务器会返回已上传的分片列表 `uploaded_chunks`
3. 客户端只需上传未完成的分片
## 错误处理
### 常见错误码
| 错误码 | 说明 | 解决方案 |
|--------|------|----------|
| 403 | 文件大小超过限制 | 减小文件大小或联系管理员调整限制 |
| 403 | 内容过多 | 文本超过 222KB,请使用文件分享 |
| 403 | 上传频率限制 | 等待一段时间后重试 |
| 404 | 文件不存在 | 检查提取码是否正确 |
| 404 | 文件已过期 | 文件已过期或下载次数已用完 |
### 频率限制
为防止滥用,系统对上传和提取操作有频率限制:
- **上传限制**:默认每分钟最多 10 次上传
- **错误限制**:默认每分钟最多 1 次错误尝试
::: tip 提示
如果遇到频率限制,请等待限制时间窗口过后再重试。
:::
## 下一步
- [配置说明](/guide/configuration) - 了解如何配置分享相关设置
- [存储配置](/guide/storage) - 了解文件存储方式
- [安全设置](/guide/security) - 了解安全相关配置
- [管理面板](/guide/management) - 了解如何管理分享的文件
+383 -14
View File
@@ -1,26 +1,395 @@
# 阿里云设 # 存储配
S3 AccessKeyId: `AccessKeyId`
S3 SecretAccessKey: `SecretAccessKey` FileCodeBox 支持多种存储后端,您可以根据需求选择合适的存储方式。本文档将详细介绍各种存储后端的配置方法。
S3 BucketName: `bucket-name` ## 存储类型概览
S3 EndpointUrl: `https://<bucket-name>.<region>.aliyuncs.com` | 存储类型 | 配置值 | 说明 |
|---------|--------|------|
| 本地存储 | `local` | 默认存储方式,文件保存在服务器本地 |
| S3 兼容存储 | `s3` | 支持 AWS S3、阿里云 OSS、MinIO 等 |
| OneDrive | `onedrive` | 微软 OneDrive 云存储(仅支持工作/学校账户) |
| WebDAV | `webdav` | 支持 WebDAV 协议的存储服务 |
| OpenDAL | `opendal` | 通过 OpenDAL 集成更多存储服务 |
S3 Signature Version: `s3v4` ## 本地存储
S3 Region Name`region` 本地存储是默认的存储方式,文件将保存在服务器的 `data/` 目录下。
# Minio设置 ### 配置参数
S3 AccessKeyId: `AccessKeyId` | 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `file_storage` | string | `local` | 存储类型 |
| `storage_path` | string | `""` | 自定义存储路径(可选) |
S3 SecretAccessKey: `SecretAccessKey` ### 配置示例
S3 BucketName: `bucket-name` ```bash
file_storage=local
storage_path=
```
S3 EndpointUrl: api接口地址 ### 说明
S3 Signature Version: `s3v4` - 文件默认存储在 `data/share/data/` 目录下
- 按日期自动创建子目录:`年/月/日/文件ID/`
- 建议在生产环境中将 `data/` 目录挂载到持久化存储
S3 Region Name:根据`configurations`里面设置的 `Server Location` ## S3 兼容存储
支持所有 S3 兼容的对象存储服务,包括 AWS S3、阿里云 OSS、MinIO、腾讯云 COS 等。
### 配置参数
| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `file_storage` | string | - | 设置为 `s3` |
| `s3_access_key_id` | string | `""` | Access Key ID |
| `s3_secret_access_key` | string | `""` | Secret Access Key |
| `s3_bucket_name` | string | `""` | 存储桶名称 |
| `s3_endpoint_url` | string | `""` | S3 端点 URL |
| `s3_region_name` | string | `auto` | 区域名称 |
| `s3_signature_version` | string | `s3v2` | 签名版本(`s3v2``s3v4` |
| `s3_hostname` | string | `""` | S3 主机名(备用) |
| `s3_proxy` | int | `0` | 是否通过服务器代理下载(1=是,0=否) |
| `aws_session_token` | string | `""` | AWS 会话令牌(可选) |
### AWS S3 配置示例
```bash
file_storage=s3
s3_access_key_id=AKIAIOSFODNN7EXAMPLE
s3_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
s3_bucket_name=my-filecodebox-bucket
s3_endpoint_url=https://s3.amazonaws.com
s3_region_name=us-east-1
s3_signature_version=s3v4
```
### 阿里云 OSS 配置示例
```bash
file_storage=s3
s3_access_key_id=您的AccessKeyId
s3_secret_access_key=您的SecretAccessKey
s3_bucket_name=bucket-name
s3_endpoint_url=https://bucket-name.oss-cn-hangzhou.aliyuncs.com
s3_region_name=oss-cn-hangzhou
s3_signature_version=s3v4
```
::: tip 阿里云 OSS 端点格式
端点 URL 格式为:`https://<bucket-name>.<region>.aliyuncs.com`
常用区域:
- 杭州:`oss-cn-hangzhou`
- 上海:`oss-cn-shanghai`
- 北京:`oss-cn-beijing`
- 深圳:`oss-cn-shenzhen`
:::
### MinIO 配置示例
```bash
file_storage=s3
s3_access_key_id=minioadmin
s3_secret_access_key=minioadmin
s3_bucket_name=filecodebox
s3_endpoint_url=http://localhost:9000
s3_region_name=us-east-1
s3_signature_version=s3v4
```
::: warning MinIO 注意事项
- `s3_endpoint_url` 填写 MinIO 的 API 接口地址
- `s3_region_name` 根据 MinIO 配置中的 `Server Location` 设置
- 确保存储桶已创建且有正确的访问权限
:::
### 腾讯云 COS 配置示例
```bash
file_storage=s3
s3_access_key_id=您的SecretId
s3_secret_access_key=您的SecretKey
s3_bucket_name=bucket-name-1250000000
s3_endpoint_url=https://cos.ap-guangzhou.myqcloud.com
s3_region_name=ap-guangzhou
s3_signature_version=s3v4
```
### 代理下载
`s3_proxy=1` 时,文件下载将通过服务器中转,而不是直接从 S3 下载。这在以下情况下有用:
- S3 存储桶不允许公开访问
- 需要隐藏实际的存储地址
- 网络环境限制直接访问 S3
## OneDrive 存储
OneDrive 存储支持将文件保存到微软 OneDrive 云存储。
::: warning 重要限制
OneDrive 存储**仅支持工作或学校账户**,并且需要有管理员权限以授权 API。个人账户无法使用此功能。
:::
### 配置参数
| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `file_storage` | string | - | 设置为 `onedrive` |
| `onedrive_domain` | string | `""` | Azure AD 域名 |
| `onedrive_client_id` | string | `""` | 应用程序(客户端)ID |
| `onedrive_username` | string | `""` | 账户邮箱 |
| `onedrive_password` | string | `""` | 账户密码 |
| `onedrive_root_path` | string | `filebox_storage` | OneDrive 中的存储根目录 |
| `onedrive_proxy` | int | `0` | 是否通过服务器代理下载 |
### 配置示例
```bash
file_storage=onedrive
onedrive_domain=contoso.onmicrosoft.com
onedrive_client_id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
onedrive_username=user@contoso.onmicrosoft.com
onedrive_password=your_password
onedrive_root_path=filebox_storage
```
### Azure 应用注册步骤
要使用 OneDrive 存储,您需要在 Azure 门户中注册应用程序:
#### 1. 获取域名
登录 [Azure 门户](https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade),将鼠标置于右上角账号处,浮窗显示的**域**即为 `onedrive_domain` 的值。
#### 2. 注册应用
1. 点击左上角的 **+ 新注册**
2. 输入应用名称(如:FileCodeBox
3. **受支持的帐户类型**:选择"任何组织目录(任何 Azure AD 目录 - 多租户)中的帐户和个人 Microsoft 帐户"
4. **重定向 URI**:选择 `Web`,输入 `http://localhost`
5. 点击**注册**
#### 3. 获取客户端 ID
注册完成后,在应用概述页面的**概要**中找到**应用程序(客户端)ID**,即为 `onedrive_client_id` 的值。
#### 4. 配置身份验证
1. 在左侧菜单选择**身份验证**
2. 找到**允许公共客户端流**,选择**是**
3. 点击**保存**
#### 5. 配置 API 权限
1. 在左侧菜单选择 **API 权限**
2. 点击 **+ 添加权限**
3. 选择 **Microsoft Graph****委托的权限**
4. 勾选以下权限:
- `openid`
- `Files.Read`
- `Files.Read.All`
- `Files.ReadWrite`
- `Files.ReadWrite.All`
- `User.Read`
5. 点击**添加权限**
6. 点击**代表 xxx 授予管理员同意**
7. 确认后,权限状态应显示为**已授予**
### 安装依赖
使用 OneDrive 存储需要安装额外的 Python 依赖:
```bash
pip install msal Office365-REST-Python-Client
```
### 验证配置
您可以使用以下代码测试配置是否正确:
```python
import msal
from office365.graph_client import GraphClient
domain = 'your_domain'
client_id = 'your_client_id'
username = 'your_username'
password = 'your_password'
def acquire_token_pwd():
authority_url = f'https://login.microsoftonline.com/{domain}'
app = msal.PublicClientApplication(
authority=authority_url,
client_id=client_id
)
result = app.acquire_token_by_username_password(
username=username,
password=password,
scopes=['https://graph.microsoft.com/.default']
)
return result
# 测试连接
client = GraphClient(acquire_token_pwd)
me = client.me.get().execute_query()
print(f"登录成功:{me.user_principal_name}")
```
## WebDAV 存储
WebDAV 存储支持将文件保存到任何支持 WebDAV 协议的服务,如 Nextcloud、ownCloud、坚果云等。
### 配置参数
| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `file_storage` | string | - | 设置为 `webdav` |
| `webdav_url` | string | `""` | WebDAV 服务器 URL |
| `webdav_username` | string | `""` | WebDAV 用户名 |
| `webdav_password` | string | `""` | WebDAV 密码 |
| `webdav_root_path` | string | `filebox_storage` | WebDAV 中的存储根目录 |
| `webdav_proxy` | int | `0` | 是否通过服务器代理下载 |
### 配置示例
```bash
file_storage=webdav
webdav_url=https://dav.example.com/remote.php/dav/files/username/
webdav_username=your_username
webdav_password=your_password
webdav_root_path=filebox_storage
```
### Nextcloud 配置示例
```bash
file_storage=webdav
webdav_url=https://your-nextcloud.com/remote.php/dav/files/username/
webdav_username=your_username
webdav_password=your_app_password
webdav_root_path=FileCodeBox
```
::: tip Nextcloud 应用密码
建议在 Nextcloud 中创建应用密码,而不是使用主密码:
1. 登录 Nextcloud
2. 进入**设置** → **安全**
3. 在**设备与会话**中创建新的应用密码
:::
### 坚果云配置示例
```bash
file_storage=webdav
webdav_url=https://dav.jianguoyun.com/dav/
webdav_username=your_email@example.com
webdav_password=your_app_password
webdav_root_path=FileCodeBox
```
::: tip 坚果云应用密码
坚果云需要使用应用密码:
1. 登录坚果云网页版
2. 进入**账户信息** → **安全选项**
3. 添加应用密码
:::
## OpenDAL 存储
OpenDAL 是一个统一的数据访问层,支持多种存储服务。通过 OpenDAL,您可以使用 Google Cloud Storage、Azure Blob Storage 等更多存储服务。
### 配置参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `file_storage` | string | 设置为 `opendal` |
| `opendal_scheme` | string | 存储服务类型(如 `gcs``azblob` |
| `opendal_<scheme>_<setting>` | string | 服务特定的配置参数 |
### 安装依赖
```bash
pip install opendal
```
### Google Cloud Storage 配置示例
```bash
file_storage=opendal
opendal_scheme=gcs
opendal_gcs_root=/filecodebox
opendal_gcs_bucket=your-bucket-name
opendal_gcs_credential=base64_encoded_credential
```
### Azure Blob Storage 配置示例
```bash
file_storage=opendal
opendal_scheme=azblob
opendal_azblob_root=/filecodebox
opendal_azblob_container=your-container
opendal_azblob_account_name=your_account
opendal_azblob_account_key=your_key
```
### 支持的服务
OpenDAL 支持众多存储服务,完整列表请参考 [OpenDAL 官方文档](https://opendal.apache.org/docs/rust/opendal/services/index.html)。
常用服务包括:
- `gcs` - Google Cloud Storage
- `azblob` - Azure Blob Storage
- `obs` - 华为云 OBS
- `oss` - 阿里云 OSS(通过 OpenDAL
- `cos` - 腾讯云 COS(通过 OpenDAL
- `hdfs` - Hadoop HDFS
- `ftp` - FTP 服务器
- `sftp` - SFTP 服务器
::: warning OpenDAL 注意事项
1. 通过 OpenDAL 集成的服务均通过服务器中转下载,会同时消耗存储服务和服务器的流量
2. 相比原生 S3/OneDrive 支持,OpenDAL 方式可能缺少一些调试信息
3. OpenDAL 采用 Rust 编写,性能较好
:::
## 存储选择建议
| 场景 | 推荐存储 | 原因 |
|------|----------|------|
| 个人/小型部署 | 本地存储 | 简单易用,无需额外配置 |
| 企业内网 | MinIO + S3 | 自建对象存储,数据可控 |
| 公有云部署 | 对应云厂商 S3 | 同区域访问快,成本低 |
| 已有 OneDrive | OneDrive | 利用现有资源 |
| 已有 WebDAV | WebDAV | 兼容性好 |
| 特殊存储需求 | OpenDAL | 支持更多存储服务 |
## 常见问题
### S3 上传失败
1. 检查 Access Key 和 Secret Key 是否正确
2. 确认存储桶名称和区域配置正确
3. 检查存储桶的访问权限设置
4. 确认签名版本(`s3v2``s3v4`)与服务商要求一致
### OneDrive 认证失败
1. 确认使用的是工作/学校账户,而非个人账户
2. 检查 Azure 应用是否已授予管理员同意
3. 确认 API 权限配置完整
4. 验证用户名和密码是否正确
### WebDAV 连接失败
1. 检查 WebDAV URL 格式是否正确
2. 确认用户名和密码(或应用密码)正确
3. 检查服务器是否支持 WebDAV 协议
4. 确认网络连接正常
+379
View File
@@ -0,0 +1,379 @@
# 文件上传
FileCodeBox 提供了多种灵活的文件上传方式,支持普通上传和分片上传,满足不同场景的需求。
## 上传方式
FileCodeBox 支持以下几种上传方式:
### 拖拽上传
将文件直接拖拽到上传区域即可开始上传。这是最便捷的上传方式。
1. 打开 FileCodeBox 首页
2. 将文件从文件管理器拖拽到上传区域
3. 松开鼠标,文件开始上传
4. 上传完成后获取提取码
::: tip 提示
拖拽上传支持同时拖拽多个文件(取决于主题支持)。
:::
### 点击上传
点击上传区域,通过系统文件选择器选择文件。
1. 点击上传区域的「选择文件」按钮
2. 在弹出的文件选择器中选择要上传的文件
3. 确认选择后文件开始上传
4. 上传完成后获取提取码
### 粘贴上传
支持从剪贴板直接粘贴图片进行上传(部分主题支持)。
1. 复制图片到剪贴板(截图或复制图片)
2. 在上传区域使用 `Ctrl+V`Windows/Linux)或 `Cmd+V`macOS)粘贴
3. 图片自动开始上传
4. 上传完成后获取提取码
::: warning 注意
粘贴上传仅支持图片格式,不支持其他文件类型。具体支持情况取决于所使用的主题。
:::
## 文件大小限制
### 默认限制
| 配置项 | 默认值 | 说明 |
|--------|--------|------|
| `uploadSize` | 10MB | 单文件最大上传大小 |
### 修改上传限制
管理员可以通过管理面板或配置文件修改上传大小限制:
```python
# 设置最大上传大小为 100MB
uploadSize = 104857600 # 100 * 1024 * 1024
```
::: info 说明
`uploadSize` 的单位是字节。常用换算:
- 10MB = 10485760
- 50MB = 52428800
- 100MB = 104857600
- 500MB = 524288000
- 1GB = 1073741824
:::
### 超出限制的处理
当上传文件超过大小限制时,系统会返回 403 错误:
```json
{
"detail": "大小超过限制,最大为10.00 MB"
}
```
## 普通上传 API
### 文件上传接口
**POST** `/share/file/`
Content-Type: `multipart/form-data`
**请求参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `file` | file | 是 | 要上传的文件 |
| `expire_value` | int | 否 | 过期数值,默认 1 |
| `expire_style` | string | 否 | 过期方式,默认 `day` |
**过期方式选项:**
| 值 | 说明 |
|----|------|
| `day` | 按天过期 |
| `hour` | 按小时过期 |
| `minute` | 按分钟过期 |
| `forever` | 永不过期 |
| `count` | 按下载次数过期 |
**响应示例:**
```json
{
"code": 200,
"detail": {
"code": "654321",
"name": "example.pdf"
}
}
```
**cURL 示例:**
```bash
curl -X POST "http://localhost:12345/share/file/" \
-F "file=@/path/to/file.pdf" \
-F "expire_value=7" \
-F "expire_style=day"
```
## 分片上传 API
对于大文件,FileCodeBox 支持分片上传功能。分片上传将大文件分割成多个小块分别上传,支持断点续传。
::: warning 前提条件
分片上传功能需要管理员启用:`enableChunk=1`
:::
### 分片上传流程
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 初始化上传 │ ──▶ │ 上传分片 │ ──▶ │ 完成上传 │
│ /init/ │ │ /chunk/ │ │ /complete/ │
└─────────────┘ └─────────────┘ └─────────────┘
┌───────────┐
│ 循环上传 │
│ 每个分片 │
└───────────┘
```
### 1. 初始化上传
**POST** `/chunk/upload/init/`
**请求参数:**
```json
{
"file_name": "large_file.zip",
"file_size": 104857600,
"chunk_size": 5242880,
"file_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
```
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| `file_name` | string | 是 | - | 文件名 |
| `file_size` | int | 是 | - | 文件总大小(字节) |
| `chunk_size` | int | 否 | 5MB | 分片大小(字节) |
| `file_hash` | string | 是 | - | 文件的 SHA256 哈希值 |
**响应示例:**
```json
{
"code": 200,
"detail": {
"existed": false,
"upload_id": "abc123def456789",
"chunk_size": 5242880,
"total_chunks": 20,
"uploaded_chunks": []
}
}
```
| 字段 | 说明 |
|------|------|
| `existed` | 文件是否已存在(秒传) |
| `upload_id` | 上传会话 ID |
| `chunk_size` | 分片大小 |
| `total_chunks` | 总分片数 |
| `uploaded_chunks` | 已上传的分片索引列表 |
### 2. 上传分片
**POST** `/chunk/upload/chunk/{upload_id}/{chunk_index}`
**路径参数:**
| 参数 | 说明 |
|------|------|
| `upload_id` | 初始化时返回的上传会话 ID |
| `chunk_index` | 分片索引,从 0 开始 |
**请求体:**
Content-Type: `multipart/form-data`
| 参数 | 类型 | 说明 |
|------|------|------|
| `chunk` | file | 分片数据 |
**响应示例:**
```json
{
"code": 200,
"detail": {
"chunk_hash": "a1b2c3d4e5f6..."
}
}
```
**cURL 示例:**
```bash
# 上传第一个分片(索引为 0
curl -X POST "http://localhost:12345/chunk/upload/chunk/abc123def456789/0" \
-F "chunk=@/path/to/chunk_0"
```
### 3. 完成上传
**POST** `/chunk/upload/complete/{upload_id}`
**路径参数:**
| 参数 | 说明 |
|------|------|
| `upload_id` | 上传会话 ID |
**请求参数:**
```json
{
"expire_value": 7,
"expire_style": "day"
}
```
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `expire_value` | int | 是 | 过期数值 |
| `expire_style` | string | 是 | 过期方式 |
**响应示例:**
```json
{
"code": 200,
"detail": {
"code": "789012",
"name": "large_file.zip"
}
}
```
### 断点续传
分片上传支持断点续传。当上传中断后:
1. 使用相同的 `file_hash` 重新调用初始化接口
2. 服务器返回 `uploaded_chunks` 列表,包含已上传的分片索引
3. 客户端只需上传不在列表中的分片
4. 所有分片上传完成后调用完成接口
**示例流程:**
```javascript
// 1. 初始化上传
const initResponse = await fetch('/chunk/upload/init/', {
method: 'POST',
body: JSON.stringify({
file_name: 'large_file.zip',
file_size: fileSize,
chunk_size: 5 * 1024 * 1024,
file_hash: fileHash
})
});
const { upload_id, uploaded_chunks, total_chunks } = await initResponse.json();
// 2. 上传未完成的分片
for (let i = 0; i < total_chunks; i++) {
if (!uploaded_chunks.includes(i)) {
const chunk = file.slice(i * chunkSize, (i + 1) * chunkSize);
await fetch(`/chunk/upload/chunk/${upload_id}/${i}`, {
method: 'POST',
body: chunk
});
}
}
// 3. 完成上传
await fetch(`/chunk/upload/complete/${upload_id}`, {
method: 'POST',
body: JSON.stringify({
expire_value: 7,
expire_style: 'day'
})
});
```
## 错误处理
### 常见错误
| HTTP 状态码 | 错误信息 | 原因 | 解决方案 |
|-------------|----------|------|----------|
| 403 | 大小超过限制 | 文件超过 `uploadSize` 限制 | 减小文件大小或联系管理员调整限制 |
| 403 | 上传频率限制 | 超过 IP 上传频率限制 | 等待限制时间窗口后重试 |
| 400 | 过期时间类型错误 | `expire_style` 值不在允许列表中 | 使用有效的过期方式 |
| 404 | 上传会话不存在 | `upload_id` 无效或已过期 | 重新初始化上传 |
| 400 | 无效的分片索引 | `chunk_index` 超出范围 | 检查分片索引是否正确 |
| 400 | 分片不完整 | 完成上传时分片数量不足 | 确保所有分片都已上传 |
### 频率限制
系统对上传操作有频率限制,防止滥用:
| 配置项 | 默认值 | 说明 |
|--------|--------|------|
| `uploadMinute` | 1 | 限制时间窗口(分钟) |
| `uploadCount` | 10 | 时间窗口内最大上传次数 |
当超过频率限制时,需要等待时间窗口过后才能继续上传。
### 错误响应格式
```json
{
"detail": "错误信息描述"
}
```
## 上传配置
### 相关配置项
| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `openUpload` | int | 1 | 是否开放上传(1=开放,0=关闭) |
| `uploadSize` | int | 10485760 | 最大上传大小(字节) |
| `enableChunk` | int | 0 | 是否启用分片上传(1=启用,0=禁用) |
| `uploadMinute` | int | 1 | 上传频率限制时间窗口(分钟) |
| `uploadCount` | int | 10 | 时间窗口内最大上传次数 |
| `expireStyle` | list | ["day","hour","minute","forever","count"] | 允许的过期方式 |
### 配置示例
```python
# 允许上传 100MB 文件,启用分片上传
uploadSize = 104857600
enableChunk = 1
# 放宽上传频率限制:每 5 分钟最多 50 次
uploadMinute = 5
uploadCount = 50
# 只允许按天和按次数过期
expireStyle = ["day", "count"]
```
## 下一步
- [文件分享](/guide/share) - 了解完整的分享流程
- [配置说明](/guide/configuration) - 了解所有配置选项
- [存储配置](/guide/storage) - 了解文件存储方式
- [安全设置](/guide/security) - 了解安全相关配置