├── .dockerignore ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── custom.md │ └── feature_request.md ├── images │ ├── img.png │ ├── img_1.png │ ├── img_10.png │ ├── img_11.png │ ├── img_12.png │ ├── img_13.png │ ├── img_14.png │ ├── img_2.png │ ├── img_3.png │ ├── img_4.png │ ├── img_5.png │ ├── img_6.png │ ├── img_7.png │ ├── img_8.png │ └── img_9.png └── workflows │ ├── docker-image.yml │ └── docs.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── SECURITY.md ├── apps ├── __init__.py ├── admin │ ├── __init__.py │ ├── dependencies.py │ ├── schemas.py │ ├── services.py │ └── views.py └── base │ ├── __init__.py │ ├── dependencies.py │ ├── migrations │ ├── migrations_001.py │ └── migrations_002.py │ ├── models.py │ ├── schemas.py │ ├── utils.py │ └── views.py ├── core ├── __init__.py ├── database.py ├── logger.py ├── response.py ├── settings.py ├── storage.py ├── tasks.py └── utils.py ├── docker-compose.yml ├── docs ├── .vitepress │ ├── cache │ │ └── deps │ │ │ ├── @theme_index.js │ │ │ ├── @theme_index.js.map │ │ │ ├── _metadata.json │ │ │ ├── chunk-CQOUZRMK.js │ │ │ ├── chunk-CQOUZRMK.js.map │ │ │ ├── chunk-KT7LHMJ2.js │ │ │ ├── chunk-KT7LHMJ2.js.map │ │ │ ├── package.json │ │ │ ├── vitepress___@vue_devtools-api.js │ │ │ ├── vitepress___@vue_devtools-api.js.map │ │ │ ├── vitepress___@vueuse_core.js │ │ │ ├── vitepress___@vueuse_core.js.map │ │ │ ├── vitepress___@vueuse_integrations_useFocusTrap.js │ │ │ ├── vitepress___@vueuse_integrations_useFocusTrap.js.map │ │ │ ├── vitepress___mark__js_src_vanilla__js.js │ │ │ ├── vitepress___mark__js_src_vanilla__js.js.map │ │ │ ├── vitepress___minisearch.js │ │ │ ├── vitepress___minisearch.js.map │ │ │ ├── vue.js │ │ │ └── vue.js.map │ └── config.mts ├── api │ └── index.md ├── changelog.md ├── contributing.md ├── en │ ├── api │ │ └── index.md │ ├── changelog.md │ ├── contributing.md │ ├── guide │ │ ├── configuration.md │ │ ├── getting-started.md │ │ ├── introduction.md │ │ ├── management.md │ │ ├── security.md │ │ ├── share.md │ │ ├── storage.md │ │ ├── upload copy.md │ │ └── upload.md │ └── index.md ├── guide │ ├── configuration.md │ ├── getting-started.md │ ├── introduction.md │ ├── management.md │ ├── security.md │ ├── share.md │ ├── storage.md │ └── upload.md ├── index.md ├── package.json ├── pnpm-lock.yaml └── public │ └── logo_small.png ├── main.py ├── readme.md ├── readme_en.md ├── readme_onedrive.md ├── readme_opendal.md ├── requirements.txt └── themes ├── 2023 ├── assets │ ├── AboutView-DYjyUJF_.js │ ├── AdminView-BC_-34Xk.css │ ├── AdminView-BfUHrcUB.js │ ├── CardTools-BeU-pM3r.css │ ├── CardTools.vue_vue_type_script_setup_true_lang-CdJKhv07.js │ ├── FileView-DVCieedT.js │ ├── FileView-TIfHKQHQ.css │ ├── HomeView-Bj3zqJSZ.css │ ├── HomeView-DxH6ZVhr.js │ ├── KaTeX_AMS-Regular-BQhdFMY1.woff2 │ ├── KaTeX_AMS-Regular-DMm9YOAa.woff │ ├── KaTeX_AMS-Regular-DRggAlZN.ttf │ ├── KaTeX_Caligraphic-Bold-ATXxdsX0.ttf │ ├── KaTeX_Caligraphic-Bold-BEiXGLvX.woff │ ├── KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 │ ├── KaTeX_Caligraphic-Regular-CTRA-rTL.woff │ ├── KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 │ ├── KaTeX_Caligraphic-Regular-wX97UBjC.ttf │ ├── KaTeX_Fraktur-Bold-BdnERNNW.ttf │ ├── KaTeX_Fraktur-Bold-BsDP51OF.woff │ ├── KaTeX_Fraktur-Bold-CL6g_b3V.woff2 │ ├── KaTeX_Fraktur-Regular-CB_wures.ttf │ ├── KaTeX_Fraktur-Regular-CTYiF6lA.woff2 │ ├── KaTeX_Fraktur-Regular-Dxdc4cR9.woff │ ├── KaTeX_Main-Bold-Cx986IdX.woff2 │ ├── KaTeX_Main-Bold-Jm3AIy58.woff │ ├── KaTeX_Main-Bold-waoOVXN0.ttf │ ├── KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 │ ├── KaTeX_Main-BoldItalic-DzxPMmG6.ttf │ ├── KaTeX_Main-BoldItalic-SpSLRI95.woff │ ├── KaTeX_Main-Italic-3WenGoN9.ttf │ ├── KaTeX_Main-Italic-BMLOBm91.woff │ ├── KaTeX_Main-Italic-NWA7e6Wa.woff2 │ ├── KaTeX_Main-Regular-B22Nviop.woff2 │ ├── KaTeX_Main-Regular-Dr94JaBh.woff │ ├── KaTeX_Main-Regular-ypZvNtVU.ttf │ ├── KaTeX_Math-BoldItalic-B3XSjfu4.ttf │ ├── KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 │ ├── KaTeX_Math-BoldItalic-iY-2wyZ7.woff │ ├── KaTeX_Math-Italic-DA0__PXp.woff │ ├── KaTeX_Math-Italic-flOr_0UB.ttf │ ├── KaTeX_Math-Italic-t53AETM-.woff2 │ ├── KaTeX_SansSerif-Bold-CFMepnvq.ttf │ ├── KaTeX_SansSerif-Bold-D1sUS0GD.woff2 │ ├── KaTeX_SansSerif-Bold-DbIhKOiC.woff │ ├── KaTeX_SansSerif-Italic-C3H0VqGB.woff2 │ ├── KaTeX_SansSerif-Italic-DN2j7dab.woff │ ├── KaTeX_SansSerif-Italic-YYjJ1zSn.ttf │ ├── KaTeX_SansSerif-Regular-BNo7hRIc.ttf │ ├── KaTeX_SansSerif-Regular-CS6fqUqJ.woff │ ├── KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 │ ├── KaTeX_Script-Regular-C5JkGWo-.ttf │ ├── KaTeX_Script-Regular-D3wIWfF6.woff2 │ ├── KaTeX_Script-Regular-D5yQViql.woff │ ├── KaTeX_Size1-Regular-C195tn64.woff │ ├── KaTeX_Size1-Regular-Dbsnue_I.ttf │ ├── KaTeX_Size1-Regular-mCD8mA8B.woff2 │ ├── KaTeX_Size2-Regular-B7gKUWhC.ttf │ ├── KaTeX_Size2-Regular-Dy4dx90m.woff2 │ ├── KaTeX_Size2-Regular-oD1tc_U0.woff │ ├── KaTeX_Size3-Regular-CTq5MqoE.woff │ ├── KaTeX_Size3-Regular-DgpXs0kz.ttf │ ├── KaTeX_Size4-Regular-BF-4gkZK.woff │ ├── KaTeX_Size4-Regular-DWFBv043.ttf │ ├── KaTeX_Size4-Regular-Dl5lxZxV.woff2 │ ├── KaTeX_Typewriter-Regular-C0xS9mPB.woff │ ├── KaTeX_Typewriter-Regular-CO6r4hn1.woff2 │ ├── KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf │ ├── LocalView-Bi6sx1Wt.css │ ├── LocalView-Co9a5QvS.js │ ├── SendView-BKoCrLal.css │ ├── SendView-DvkC6_VH.js │ ├── SettingView-Ba6-Wyi6.css │ ├── SettingView-C0CG5ugf.js │ ├── _baseClone-DD6IrDYX.js │ ├── config-CZWWa62X.js │ ├── config-DYaq6EHK.css │ ├── el-form-item-CKZiX9BY.css │ ├── el-form-item-llYGp2SQ.js │ ├── el-input-CNNwOXGP.js │ ├── el-input-CR4hgTr-.css │ ├── el-popper-C96qJ_vz.js │ ├── el-popper-DG5wR-qi.css │ ├── el-select-BpOyN384.css │ ├── el-select-LRJf3l5x.js │ ├── el-tag-5pOVEKUY.js │ ├── el-tag-DljBBxJR.css │ ├── el-tooltip-l0sNRNKZ.js │ ├── index-4qqIFT66.css │ ├── index-CBpXPsVN.js │ ├── index-CxMsK_Ni.js │ ├── logo_small.png │ └── vnode-Cc0RQjVK.js └── index.html └── 2024 ├── assets ├── AdminLayout-BA8HwEVA.css ├── AdminLayout-DDCPshEE.js ├── DashboardView-Cp5dD9KH.js ├── DingTalk-CT5a5scH.ttf ├── FileManageView-DFpVuc4O.js ├── FileManageView-DrjnVkAt.css ├── LoginView-CUDwFSJJ.css ├── LoginView-CZDEkhdS.js ├── RetrievewFileView-1g5bCwjM.js ├── RetrievewFileView-Csu6yejH.css ├── SendFileView-1dGxBWlR.js ├── SendFileView-XxX3DVxA.css ├── SystemSettingsView-C_edee3t.js ├── box-DBrNLmg8.js ├── clipboard-BsVvyWeO.js ├── file-oKDOd1HK.js ├── hard-drive-DZ7L4y0D.js ├── index-Bx3cTsS0.css ├── index-CgqZQa96.js ├── logo_small.png └── trash-cjXYkUOS.js └── index.html /.dockerignore: -------------------------------------------------------------------------------- 1 | *.md 2 | .idea 3 | .git 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-language=python 2 | *.css linguist-language=python 3 | *.html linguist-language=python 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/images/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/.github/images/img.png -------------------------------------------------------------------------------- /.github/images/img_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/.github/images/img_1.png -------------------------------------------------------------------------------- /.github/images/img_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/.github/images/img_10.png -------------------------------------------------------------------------------- /.github/images/img_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/.github/images/img_11.png -------------------------------------------------------------------------------- /.github/images/img_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/.github/images/img_12.png -------------------------------------------------------------------------------- /.github/images/img_13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/.github/images/img_13.png -------------------------------------------------------------------------------- /.github/images/img_14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/.github/images/img_14.png -------------------------------------------------------------------------------- /.github/images/img_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/.github/images/img_2.png -------------------------------------------------------------------------------- /.github/images/img_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/.github/images/img_3.png -------------------------------------------------------------------------------- /.github/images/img_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/.github/images/img_4.png -------------------------------------------------------------------------------- /.github/images/img_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/.github/images/img_5.png -------------------------------------------------------------------------------- /.github/images/img_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/.github/images/img_6.png -------------------------------------------------------------------------------- /.github/images/img_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/.github/images/img_7.png -------------------------------------------------------------------------------- /.github/images/img_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/.github/images/img_8.png -------------------------------------------------------------------------------- /.github/images/img_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/.github/images/img_9.png -------------------------------------------------------------------------------- /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Build and push Docker image 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - master 8 | 9 | jobs: 10 | buildx: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v3 15 | with: 16 | submodules: true 17 | 18 | - name: Set up QEMU 19 | uses: docker/setup-qemu-action@v2 20 | 21 | - name: Set up Docker Buildx 22 | uses: docker/setup-buildx-action@v2 23 | 24 | - name: Cache Docker layers 25 | uses: actions/cache@v3 26 | with: 27 | path: /tmp/.buildx-cache 28 | key: ${{ runner.os }}-buildx-${{ github.sha }} 29 | restore-keys: | 30 | ${{ runner.os }}-buildx- 31 | 32 | - name: Login to DockerHub 33 | uses: docker/login-action@v2 34 | with: 35 | username: ${{ secrets.DOCKER_USERNAME }} 36 | password: ${{ secrets.DOCKER_PASSWORD }} 37 | 38 | - name: Build and push 39 | uses: docker/build-push-action@v4 40 | with: 41 | context: . 42 | platforms: linux/amd64,linux/arm64 43 | push: true 44 | tags: ${{ secrets.DOCKER_USERNAME }}/filecodebox:beta 45 | cache-from: type=local,src=/tmp/.buildx-cache 46 | cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max 47 | - name: Move cache 48 | run: | 49 | rm -rf /tmp/.buildx-cache 50 | mv /tmp/.buildx-cache-new /tmp/.buildx-cache 51 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | # 构建 VitePress 站点并将其部署到 GitHub Pages 的示例工作流程 2 | # 3 | name: Deploy VitePress site to Pages 4 | 5 | on: 6 | # 在针对 `main` 分支的推送上运行。如果你 7 | # 使用 `master` 分支作为默认分支,请将其更改为 `master` 8 | push: 9 | branches: [ master ] 10 | 11 | # 允许你从 Actions 选项卡手动运行此工作流程 12 | workflow_dispatch: 13 | 14 | # 设置 GITHUB_TOKEN 的权限,以允许部署到 GitHub Pages 15 | permissions: 16 | contents: read 17 | pages: write 18 | id-token: write 19 | 20 | # 只允许同时进行一次部署,跳过正在运行和最新队列之间的运行队列 21 | # 但是,不要取消正在进行的运行,因为我们希望允许这些生产部署完成 22 | concurrency: 23 | group: pages 24 | cancel-in-progress: false 25 | 26 | jobs: 27 | # 构建工作 28 | build: 29 | runs-on: ubuntu-latest 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v4 33 | with: 34 | fetch-depth: 0 # 如果未启用 lastUpdated,则不需要 35 | - uses: pnpm/action-setup@v3 36 | with: 37 | version: 9 38 | # - uses: oven-sh/setup-bun@v1 # 如果使用 Bun,请取消注释 39 | - name: Setup Node 40 | uses: actions/setup-node@v4 41 | with: 42 | node-version: 20 43 | cache: 'pnpm' 44 | cache-dependency-path: 'docs/pnpm-lock.yaml' 45 | - name: Setup Pages 46 | uses: actions/configure-pages@v4 47 | - name: Install dependencies 48 | working-directory: docs 49 | run: pnpm install 50 | - name: Build with VitePress 51 | working-directory: docs 52 | run: pnpm run docs:build 53 | - name: Upload artifact 54 | uses: actions/upload-pages-artifact@v3 55 | with: 56 | path: docs/.vitepress/dist 57 | 58 | # 部署工作 59 | deploy: 60 | environment: 61 | name: github-pages 62 | url: ${{ steps.deployment.outputs.page_url }} 63 | needs: build 64 | runs-on: ubuntu-latest 65 | name: Deploy 66 | steps: 67 | - name: Deploy to GitHub Pages 68 | id: deployment 69 | uses: actions/deploy-pages@v4 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | media/ 2 | logs/ 3 | .idea 4 | /data 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | # Created by .ignore support plugin (hsz.mobi) 9 | ### Python template 10 | # Byte-compiled / optimized / DLL files 11 | database.db 12 | # C extensions 13 | *.so 14 | *.env 15 | # Distribution / packaging 16 | .Python 17 | build/ 18 | develop-eggs/ 19 | downloads/ 20 | eggs/ 21 | .eggs/ 22 | lib/ 23 | lib64/ 24 | parts/ 25 | sdist/ 26 | var/ 27 | wheels/ 28 | share/python-wheels/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | MANIFEST 33 | .vite/ 34 | # PyInstaller 35 | # Usually these files are written by a python script from a template 36 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 37 | *.manifest 38 | *.spec 39 | 40 | # Installer logs 41 | pip-log.txt 42 | pip-delete-this-directory.txt 43 | 44 | # Unit test.pdf / coverage reports 45 | htmlcov/ 46 | .tox/ 47 | .nox/ 48 | .coverage 49 | .coverage.* 50 | .cache 51 | nosetests.xml 52 | coverage.xml 53 | *.cover 54 | *.py,cover 55 | .hypothesis/ 56 | .pytest_cache/ 57 | cover/ 58 | 59 | # Translations 60 | *.mo 61 | *.pot 62 | 63 | # Django stuff: 64 | *.log 65 | local_settings.py 66 | db.sqlite3 67 | db.sqlite3-journal 68 | 69 | # Flask stuff: 70 | instance/ 71 | .webassets-cache 72 | 73 | # Scrapy stuff: 74 | .scrapy 75 | 76 | # Sphinx documentation 77 | docs/_build/ 78 | 79 | # PyBuilder 80 | .pybuilder/ 81 | target/ 82 | *.db 83 | ./filecodebox.db-shm 84 | ./filecodebox.db-wal 85 | # Jupyter Notebook 86 | .ipynb_checkpoints 87 | 88 | # IPython 89 | profile_default/ 90 | ipython_config.py 91 | 92 | # pyenv 93 | # For a library or package, you might want to ignore these files since the code is 94 | # intended to run in multiple environments; otherwise, check them in: 95 | # .python-version 96 | 97 | # pipenv 98 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 99 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 100 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 101 | # install all needed dependencies. 102 | #Pipfile.lock 103 | 104 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 105 | __pypackages__/ 106 | 107 | # Celery stuff 108 | celerybeat-schedule 109 | celerybeat.pid 110 | 111 | # SageMath parsed files 112 | *.sage.py 113 | 114 | # Environments 115 | data/.env 116 | .venv 117 | env/ 118 | venv/ 119 | ENV/ 120 | env.bak/ 121 | venv.bak/ 122 | 123 | # Spyder project settings 124 | .spyderproject 125 | .spyproject 126 | 127 | # Rope project settings 128 | .ropeproject 129 | 130 | # mkdocs documentation 131 | /site 132 | 133 | # mypy 134 | .mypy_cache/ 135 | .dmypy.json 136 | dmypy.json 137 | 138 | # Pyre type checker 139 | .pyre/ 140 | 141 | # pytype static type analyzer 142 | .pytype/ 143 | 144 | # Cython debug symbols 145 | cython_debug/ 146 | 147 | # Project 148 | .vscode 149 | .DS_Store 150 | for_test.py 151 | .html 152 | /evaluate/temp.py 153 | /evaluation/back.json 154 | data/.env 155 | .backup/ 156 | /cloc-1.64.exe 157 | 158 | # Ignore node_modules 159 | node_modules/ -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9.5-slim-buster 2 | LABEL author="Lan" 3 | LABEL email="xzu@live.com" 4 | 5 | # 将当前目录下的文件复制到容器的 /app 目录 6 | COPY . /app 7 | 8 | # 设置时区为亚洲/上海 9 | RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone 10 | 11 | # 设置工作目录 12 | WORKDIR /app 13 | 14 | # 删除不必要的目录,减少镜像体积 15 | RUN rm -rf docs fcb-fronted 16 | 17 | # 安装依赖 18 | RUN pip install -r requirements.txt 19 | 20 | # 暴露端口 21 | EXPOSE 12345 22 | 23 | # 启动应用 24 | CMD ["python", "main.py"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Use this section to tell people about which versions of your project are 6 | currently being supported with security updates. 7 | 8 | | Version | Supported | 9 | | ------- | ------------------ | 10 | | 5.1.x | :white_check_mark: | 11 | | 5.0.x | :x: | 12 | | 4.0.x | :white_check_mark: | 13 | | < 4.0 | :x: | 14 | 15 | ## Reporting a Vulnerability 16 | 17 | Use this section to tell people how to report a vulnerability. 18 | 19 | Tell them where to go, how often they can expect to get an update on a 20 | reported vulnerability, what to expect if the vulnerability is accepted or 21 | declined, etc. 22 | -------------------------------------------------------------------------------- /apps/__init__.py: -------------------------------------------------------------------------------- 1 | # @Time : 2023/8/13 20:43 2 | # @Author : Lan 3 | # @File : __init__.py.py 4 | # @Software: PyCharm 5 | -------------------------------------------------------------------------------- /apps/admin/__init__.py: -------------------------------------------------------------------------------- 1 | # @Time : 2023/8/14 14:38 2 | # @Author : Lan 3 | # @File : __init__.py.py 4 | # @Software: PyCharm 5 | -------------------------------------------------------------------------------- /apps/admin/dependencies.py: -------------------------------------------------------------------------------- 1 | # @Time : 2023/8/15 17:43 2 | # @Author : Lan 3 | # @File : depends.py 4 | # @Software: PyCharm 5 | from fastapi import Header, HTTPException, Depends 6 | from fastapi.requests import Request 7 | import base64 8 | import hmac 9 | import json 10 | import time 11 | from core.settings import settings 12 | from apps.admin.services import FileService, ConfigService, LocalFileService 13 | 14 | 15 | def create_token(data: dict, expires_in: int = 3600 * 24) -> str: 16 | """ 17 | 创建JWT token 18 | :param data: 数据负载 19 | :param expires_in: 过期时间(秒) 20 | """ 21 | header = base64.b64encode( 22 | json.dumps({"alg": "HS256", "typ": "JWT"}).encode() 23 | ).decode() 24 | payload = base64.b64encode( 25 | json.dumps({**data, "exp": int(time.time()) + expires_in}).encode() 26 | ).decode() 27 | 28 | signature = hmac.new( 29 | settings.admin_token.encode(), f"{header}.{payload}".encode(), "sha256" 30 | ).digest() 31 | signature = base64.b64encode(signature).decode() 32 | 33 | return f"{header}.{payload}.{signature}" 34 | 35 | 36 | def verify_token(token: str) -> dict: 37 | """ 38 | 验证JWT token 39 | :param token: JWT token 40 | :return: 解码后的数据 41 | """ 42 | try: 43 | header_b64, payload_b64, signature_b64 = token.split(".") 44 | 45 | # 验证签名 46 | expected_signature = hmac.new( 47 | settings.admin_token.encode(), 48 | f"{header_b64}.{payload_b64}".encode(), 49 | "sha256", 50 | ).digest() 51 | expected_signature_b64 = base64.b64encode(expected_signature).decode() 52 | 53 | if signature_b64 != expected_signature_b64: 54 | raise ValueError("无效的签名") 55 | 56 | # 解码payload 57 | payload = json.loads(base64.b64decode(payload_b64)) 58 | 59 | # 检查是否过期 60 | if payload.get("exp", 0) < time.time(): 61 | raise ValueError("token已过期") 62 | 63 | return payload 64 | except Exception as e: 65 | raise ValueError(f"token验证失败: {str(e)}") 66 | 67 | 68 | async def admin_required( 69 | authorization: str = Header(default=None), request: Request = None 70 | ): 71 | """ 72 | 验证管理员权限 73 | """ 74 | try: 75 | if not authorization or not authorization.startswith("Bearer "): 76 | is_admin = False 77 | else: 78 | try: 79 | token = authorization.split(" ")[1] 80 | payload = verify_token(token) 81 | is_admin = payload.get("is_admin", False) 82 | except ValueError as e: 83 | is_admin = False 84 | 85 | if request.url.path.startswith("/share/"): 86 | if not settings.openUpload and not is_admin: 87 | raise HTTPException( 88 | status_code=403, detail="本站未开启游客上传,如需上传请先登录后台" 89 | ) 90 | else: 91 | if not is_admin: 92 | raise HTTPException(status_code=401, detail="未授权或授权校验失败") 93 | return is_admin 94 | except ValueError as e: 95 | raise HTTPException(status_code=401, detail=str(e)) 96 | 97 | 98 | async def share_required_login( 99 | authorization: str = Header(default=None), request: Request = None 100 | ): 101 | """ 102 | 验证分享上传权限 103 | 104 | 当settings.openUpload为False时,要求用户必须登录并具有管理员权限 105 | 当settings.openUpload为True时,允许游客上传 106 | 107 | :param authorization: 认证头信息 108 | :param request: 请求对象 109 | :return: 验证结果 110 | """ 111 | if not settings.openUpload: 112 | try: 113 | if not authorization or not authorization.startswith("Bearer "): 114 | raise HTTPException( 115 | status_code=403, detail="本站未开启游客上传,如需上传请先登录后台" 116 | ) 117 | 118 | token = authorization.split(" ")[1] 119 | try: 120 | payload = verify_token(token) 121 | if not payload.get("is_admin", False): 122 | raise HTTPException(status_code=401, detail="未授权或授权校验失败") 123 | except ValueError as e: 124 | raise HTTPException(status_code=401, detail=str(e)) 125 | except Exception as e: 126 | raise HTTPException(status_code=401, detail="认证失败:" + str(e)) 127 | 128 | return True 129 | 130 | 131 | async def get_file_service(): 132 | return FileService() 133 | 134 | 135 | async def get_config_service(): 136 | return ConfigService() 137 | 138 | 139 | async def get_local_file_service(): 140 | return LocalFileService() 141 | -------------------------------------------------------------------------------- /apps/admin/schemas.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from typing import Optional 3 | from pydantic import BaseModel 4 | 5 | 6 | class IDData(BaseModel): 7 | id: int 8 | 9 | 10 | class ShareItem(BaseModel): 11 | expire_value: int 12 | expire_style: str = "day" 13 | filename: str 14 | 15 | 16 | class DeleteItem(BaseModel): 17 | filename: str 18 | 19 | 20 | class LoginData(BaseModel): 21 | password: str 22 | 23 | 24 | class UpdateFileData(BaseModel): 25 | id: int 26 | code: Optional[str] = None 27 | prefix: Optional[str] = None 28 | suffix: Optional[str] = None 29 | expired_at: Optional[datetime.datetime] = None 30 | expired_count: Optional[int] = None 31 | -------------------------------------------------------------------------------- /apps/admin/services.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | 4 | from core.response import APIResponse 5 | from core.storage import FileStorageInterface, storages 6 | from core.settings import settings 7 | from apps.base.models import FileCodes, KeyValue 8 | from apps.base.utils import get_expire_info, get_file_path_name 9 | from fastapi import HTTPException 10 | from core.settings import data_root 11 | 12 | 13 | class FileService: 14 | def __init__(self): 15 | self.file_storage: FileStorageInterface = storages[settings.file_storage]() 16 | 17 | async def delete_file(self, file_id: int): 18 | file_code = await FileCodes.get(id=file_id) 19 | await self.file_storage.delete_file(file_code) 20 | await file_code.delete() 21 | 22 | async def list_files(self, page: int, size: int, keyword: str = ""): 23 | offset = (page - 1) * size 24 | files = ( 25 | await FileCodes.filter(prefix__icontains=keyword).limit(size).offset(offset) 26 | ) 27 | total = await FileCodes.filter(prefix__icontains=keyword).count() 28 | return files, total 29 | 30 | async def download_file(self, file_id: int): 31 | file_code = await FileCodes.filter(id=file_id).first() 32 | if not file_code: 33 | raise HTTPException(status_code=404, detail="文件不存在") 34 | if file_code.text: 35 | return APIResponse(detail=file_code.text) 36 | else: 37 | return await self.file_storage.get_file_response(file_code) 38 | 39 | async def share_local_file(self, item): 40 | local_file = LocalFileClass(item.filename) 41 | if not await local_file.exists(): 42 | raise HTTPException(status_code=404, detail="文件不存在") 43 | 44 | text = await local_file.read() 45 | expired_at, expired_count, used_count, code = await get_expire_info( 46 | item.expire_value, item.expire_style 47 | ) 48 | path, suffix, prefix, uuid_file_name, save_path = await get_file_path_name(item) 49 | 50 | await self.file_storage.save_file(text, save_path) 51 | 52 | await FileCodes.create( 53 | code=code, 54 | prefix=prefix, 55 | suffix=suffix, 56 | uuid_file_name=uuid_file_name, 57 | file_path=path, 58 | size=local_file.size, 59 | expired_at=expired_at, 60 | expired_count=expired_count, 61 | used_count=used_count, 62 | ) 63 | 64 | return { 65 | "code": code, 66 | "name": local_file.file, 67 | } 68 | 69 | 70 | class ConfigService: 71 | def get_config(self): 72 | return settings.items() 73 | 74 | async def update_config(self, data: dict): 75 | admin_token = data.get("admin_token") 76 | if admin_token is None or admin_token == "": 77 | raise HTTPException(status_code=400, detail="管理员密码不能为空") 78 | 79 | for key, value in data.items(): 80 | if key not in settings.default_config: 81 | continue 82 | if key in [ 83 | "errorCount", 84 | "errorMinute", 85 | "max_save_seconds", 86 | "onedrive_proxy", 87 | "openUpload", 88 | "port", 89 | "s3_proxy", 90 | "uploadCount", 91 | "uploadMinute", 92 | "uploadSize", 93 | ]: 94 | data[key] = int(value) 95 | elif key in ["opacity"]: 96 | data[key] = float(value) 97 | else: 98 | data[key] = value 99 | 100 | await KeyValue.filter(key="settings").update(value=data) 101 | for k, v in data.items(): 102 | settings.__setattr__(k, v) 103 | 104 | 105 | class LocalFileService: 106 | async def list_files(self): 107 | files = [] 108 | if not os.path.exists(data_root / "local"): 109 | os.makedirs(data_root / "local") 110 | for file in os.listdir(data_root / "local"): 111 | files.append(LocalFileClass(file)) 112 | return files 113 | 114 | async def delete_file(self, filename: str): 115 | file = LocalFileClass(filename) 116 | if await file.exists(): 117 | await file.delete() 118 | return "删除成功" 119 | raise HTTPException(status_code=404, detail="文件不存在") 120 | 121 | 122 | class LocalFileClass: 123 | def __init__(self, file): 124 | self.file = file 125 | self.path = data_root / "local" / file 126 | self.ctime = time.strftime( 127 | "%Y-%m-%d %H:%M:%S", time.localtime(os.path.getctime(self.path)) 128 | ) 129 | self.size = os.path.getsize(self.path) 130 | 131 | async def read(self): 132 | return open(self.path, "rb") 133 | 134 | async def write(self, data): 135 | with open(self.path, "w") as f: 136 | f.write(data) 137 | 138 | async def delete(self): 139 | os.remove(self.path) 140 | 141 | async def exists(self): 142 | return os.path.exists(self.path) 143 | -------------------------------------------------------------------------------- /apps/admin/views.py: -------------------------------------------------------------------------------- 1 | # @Time : 2023/8/14 14:38 2 | # @Author : Lan 3 | # @File : views.py 4 | # @Software: PyCharm 5 | import datetime 6 | 7 | from fastapi import APIRouter, Depends, HTTPException 8 | from apps.admin.services import FileService, ConfigService, LocalFileService 9 | from apps.admin.dependencies import ( 10 | admin_required, 11 | get_file_service, 12 | get_config_service, 13 | get_local_file_service, 14 | ) 15 | from apps.admin.schemas import IDData, ShareItem, DeleteItem, LoginData, UpdateFileData 16 | from core.response import APIResponse 17 | from apps.base.models import FileCodes, KeyValue 18 | from apps.admin.dependencies import create_token 19 | from core.settings import settings 20 | 21 | admin_api = APIRouter(prefix="/admin", tags=["管理"]) 22 | 23 | 24 | @admin_api.post("/login") 25 | async def login(data: LoginData): 26 | # 验证管理员密码 27 | if data.password != settings.admin_token: 28 | raise HTTPException(status_code=401, detail="密码错误") 29 | 30 | # 生成包含管理员身份的token 31 | token = create_token({"is_admin": True}) 32 | return APIResponse(detail={"token": token, "token_type": "Bearer"}) 33 | 34 | 35 | @admin_api.get("/dashboard") 36 | async def dashboard(admin: bool = Depends(admin_required)): 37 | all_codes = await FileCodes.all() 38 | all_size = str(sum([code.size for code in all_codes])) 39 | sys_start = await KeyValue.filter(key="sys_start").first() 40 | # 获取当前日期时间 41 | now = datetime.datetime.now() 42 | today_start = now.replace(hour=0, minute=0, second=0, microsecond=0) 43 | yesterday_start = today_start - datetime.timedelta(days=1) 44 | yesterday_end = today_start - datetime.timedelta(microseconds=1) 45 | # 统计昨天一整天的记录数(从昨天0点到23:59:59) 46 | yesterday_codes = FileCodes.filter( 47 | created_at__gte=yesterday_start, created_at__lte=yesterday_end 48 | ) 49 | # 统计今天到现在的记录数(从今天0点到现在) 50 | today_codes = FileCodes.filter(created_at__gte=today_start) 51 | return APIResponse( 52 | detail={ 53 | "totalFiles": len(all_codes), 54 | "storageUsed": all_size, 55 | "sysUptime": sys_start.value, 56 | "yesterdayCount": await yesterday_codes.count(), 57 | "yesterdaySize": str(sum([code.size for code in await yesterday_codes])), 58 | "todayCount": await today_codes.count(), 59 | "todaySize": str(sum([code.size for code in await today_codes])), 60 | } 61 | ) 62 | 63 | 64 | @admin_api.delete("/file/delete") 65 | async def file_delete( 66 | data: IDData, 67 | file_service: FileService = Depends(get_file_service), 68 | admin: bool = Depends(admin_required), 69 | ): 70 | await file_service.delete_file(data.id) 71 | return APIResponse() 72 | 73 | 74 | @admin_api.get("/file/list") 75 | async def file_list( 76 | page: int = 1, 77 | size: int = 10, 78 | keyword: str = "", 79 | file_service: FileService = Depends(get_file_service), 80 | admin: bool = Depends(admin_required), 81 | ): 82 | files, total = await file_service.list_files(page, size, keyword) 83 | return APIResponse( 84 | detail={ 85 | "page": page, 86 | "size": size, 87 | "data": files, 88 | "total": total, 89 | } 90 | ) 91 | 92 | 93 | @admin_api.get("/config/get") 94 | async def get_config( 95 | config_service: ConfigService = Depends(get_config_service), 96 | admin: bool = Depends(admin_required), 97 | ): 98 | return APIResponse(detail=config_service.get_config()) 99 | 100 | 101 | @admin_api.patch("/config/update") 102 | async def update_config( 103 | data: dict, 104 | config_service: ConfigService = Depends(get_config_service), 105 | admin: bool = Depends(admin_required), 106 | ): 107 | data.pop("themesChoices") 108 | await config_service.update_config(data) 109 | return APIResponse() 110 | 111 | 112 | @admin_api.get("/file/download") 113 | async def file_download( 114 | id: int, 115 | file_service: FileService = Depends(get_file_service), 116 | admin: bool = Depends(admin_required), 117 | ): 118 | file_content = await file_service.download_file(id) 119 | return file_content 120 | 121 | 122 | @admin_api.get("/local/lists") 123 | async def get_local_lists( 124 | local_file_service: LocalFileService = Depends(get_local_file_service), 125 | admin: bool = Depends(admin_required), 126 | ): 127 | files = await local_file_service.list_files() 128 | return APIResponse(detail=files) 129 | 130 | 131 | @admin_api.delete("/local/delete") 132 | async def delete_local_file( 133 | item: DeleteItem, 134 | local_file_service: LocalFileService = Depends(get_local_file_service), 135 | admin: bool = Depends(admin_required), 136 | ): 137 | result = await local_file_service.delete_file(item.filename) 138 | return APIResponse(detail=result) 139 | 140 | 141 | @admin_api.post("/local/share") 142 | async def share_local_file( 143 | item: ShareItem, 144 | file_service: FileService = Depends(get_file_service), 145 | admin: bool = Depends(admin_required), 146 | ): 147 | share_info = await file_service.share_local_file(item) 148 | return APIResponse(detail=share_info) 149 | 150 | 151 | @admin_api.patch("/file/update") 152 | async def update_file( 153 | data: UpdateFileData, 154 | admin: bool = Depends(admin_required), 155 | ): 156 | file_code = await FileCodes.filter(id=data.id).first() 157 | if not file_code: 158 | raise HTTPException(status_code=404, detail="文件不存在") 159 | update_data = {} 160 | 161 | if data.code is not None and data.code != file_code.code: 162 | # 判断code是否存在 163 | if await FileCodes.filter(code=data.code).first(): 164 | raise HTTPException(status_code=400, detail="code已存在") 165 | update_data["code"] = data.code 166 | if data.prefix is not None and data.prefix != file_code.prefix: 167 | update_data["prefix"] = data.prefix 168 | if data.suffix is not None and data.suffix != file_code.suffix: 169 | update_data["suffix"] = data.suffix 170 | if data.expired_at is not None and data.expired_at != file_code.expired_at: 171 | update_data["expired_at"] = data.expired_at 172 | if data.expired_count is not None and data.expired_count != file_code.expired_count: 173 | update_data["expired_count"] = data.expired_count 174 | 175 | await file_code.update_from_dict(update_data).save() 176 | return APIResponse(detail="更新成功") 177 | -------------------------------------------------------------------------------- /apps/base/__init__.py: -------------------------------------------------------------------------------- 1 | # @Time : 2023/8/13 20:43 2 | # @Author : Lan 3 | # @File : __init__.py.py 4 | # @Software: PyCharm 5 | -------------------------------------------------------------------------------- /apps/base/dependencies.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Union 2 | from datetime import datetime, timedelta 3 | from fastapi import HTTPException, Request 4 | 5 | 6 | class IPRateLimit: 7 | def __init__(self, count: int, minutes: int): 8 | self.ips: Dict[str, Dict[str, Union[int, datetime]]] = {} 9 | self.count = count 10 | self.minutes = minutes 11 | 12 | def check_ip(self, ip: str) -> bool: 13 | if ip in self.ips: 14 | ip_info = self.ips[ip] 15 | if ip_info["count"] >= self.count: 16 | if ip_info["time"] + timedelta(minutes=self.minutes) > datetime.now(): 17 | return False 18 | self.ips.pop(ip) 19 | return True 20 | 21 | def add_ip(self, ip: str) -> int: 22 | ip_info = self.ips.get(ip, {"count": 0, "time": datetime.now()}) 23 | ip_info["count"] += 1 24 | ip_info["time"] = datetime.now() 25 | self.ips[ip] = ip_info 26 | return ip_info["count"] 27 | 28 | async def remove_expired_ip(self) -> None: 29 | now = datetime.now() 30 | expiration = timedelta(minutes=self.minutes) 31 | self.ips = { 32 | ip: info 33 | for ip, info in self.ips.items() 34 | if info["time"] + expiration >= now 35 | } 36 | 37 | def __call__(self, request: Request) -> str: 38 | ip = ( 39 | request.headers.get("X-Real-IP") 40 | or request.headers.get("X-Forwarded-For") 41 | or request.client.host 42 | ) 43 | if not self.check_ip(ip): 44 | raise HTTPException(status_code=423, detail="请求次数过多,请稍后再试") 45 | return ip 46 | -------------------------------------------------------------------------------- /apps/base/migrations/migrations_001.py: -------------------------------------------------------------------------------- 1 | from tortoise import connections 2 | 3 | 4 | async def create_file_codes_table(): 5 | conn = connections.get("default") 6 | await conn.execute_script( 7 | """ 8 | CREATE TABLE IF NOT EXISTS filecodes 9 | ( 10 | id INTEGER not null 11 | primary key autoincrement, 12 | code VARCHAR(255) not null 13 | unique, 14 | prefix VARCHAR(255) default '' not null, 15 | suffix VARCHAR(255) default '' not null, 16 | uuid_file_name VARCHAR(255), 17 | file_path VARCHAR(255), 18 | size INT default 0 not null, 19 | text TEXT, 20 | expired_at TIMESTAMP, 21 | expired_count INT default 0 not null, 22 | used_count INT default 0 not null, 23 | created_at TIMESTAMP default CURRENT_TIMESTAMP not null 24 | ); 25 | CREATE INDEX IF NOT EXISTS idx_filecodes_code_1c7ee7 26 | on filecodes (code); 27 | """ 28 | ) 29 | 30 | 31 | async def create_key_value_table(): 32 | conn = connections.get("default") 33 | await conn.execute_script( 34 | """ 35 | CREATE TABLE IF NOT EXISTS keyvalue 36 | ( 37 | id INTEGER not null 38 | primary key autoincrement, 39 | key VARCHAR(255) not null 40 | unique, 41 | value JSON, 42 | created_at TIMESTAMP default CURRENT_TIMESTAMP not null 43 | ); 44 | CREATE INDEX IF NOT EXISTS idx_keyvalue_key_eab890 45 | on keyvalue (key); 46 | """ 47 | ) 48 | 49 | 50 | async def migrate(): 51 | await create_file_codes_table() 52 | await create_key_value_table() 53 | -------------------------------------------------------------------------------- /apps/base/migrations/migrations_002.py: -------------------------------------------------------------------------------- 1 | from tortoise import connections 2 | 3 | 4 | async def create_upload_chunk_and_update_file_codes_table(): 5 | conn = connections.get("default") 6 | await conn.execute_script( 7 | """ 8 | ALTER TABLE "filecodes" ADD "file_hash" VARCHAR(128); 9 | ALTER TABLE "filecodes" ADD "is_chunked" BOOL NOT NULL DEFAULT False; 10 | ALTER TABLE "filecodes" ADD "upload_id" VARCHAR(128); 11 | CREATE TABLE "uploadchunk" ( 12 | id INTEGER not null primary key autoincrement, 13 | "upload_id" VARCHAR(36) NOT NULL, 14 | "chunk_index" INT NOT NULL, 15 | "chunk_hash" VARCHAR(128) NOT NULL, 16 | "total_chunks" INT NOT NULL, 17 | "file_size" BIGINT NOT NULL, 18 | "chunk_size" INT NOT NULL, 19 | "created_at" TIMESTAMPTZ NOT NULL, 20 | "file_name" VARCHAR(255) NOT NULL, 21 | "completed" BOOL NOT NULL 22 | ); 23 | """ 24 | ) 25 | 26 | 27 | async def migrate(): 28 | await create_upload_chunk_and_update_file_codes_table() 29 | -------------------------------------------------------------------------------- /apps/base/models.py: -------------------------------------------------------------------------------- 1 | # @Time : 2023/8/13 20:43 2 | # @Author : Lan 3 | # @File : models.py 4 | # @Software: PyCharm 5 | from typing import Optional 6 | 7 | from tortoise.models import Model 8 | from tortoise.contrib.pydantic import pydantic_model_creator 9 | 10 | from tortoise import fields, models 11 | from datetime import datetime 12 | from core.utils import get_now 13 | 14 | 15 | class FileCodes(models.Model): 16 | id = fields.IntField(pk=True) 17 | code = fields.CharField(max_length=255, unique=True, index=True) 18 | prefix = fields.CharField(max_length=255, default="") 19 | suffix = fields.CharField(max_length=255, default="") 20 | uuid_file_name = fields.CharField(max_length=255, null=True) 21 | file_path = fields.CharField(max_length=255, null=True) 22 | size = fields.IntField(default=0) 23 | text = fields.TextField(null=True) 24 | expired_at = fields.DatetimeField(null=True) 25 | expired_count = fields.IntField(default=0) 26 | used_count = fields.IntField(default=0) 27 | created_at = fields.DatetimeField(auto_now_add=True) 28 | file_hash = fields.CharField(max_length=64, null=True) 29 | is_chunked = fields.BooleanField(default=False) 30 | upload_id = fields.CharField(max_length=36, null=True) 31 | 32 | async def is_expired(self): 33 | if self.expired_at is None: 34 | return False 35 | if self.expired_at and self.expired_count < 0: 36 | return self.expired_at < await get_now() 37 | return self.expired_count <= 0 38 | 39 | async def get_file_path(self): 40 | return f"{self.file_path}/{self.uuid_file_name}" 41 | 42 | 43 | class UploadChunk(models.Model): 44 | id = fields.IntField(pk=True) 45 | upload_id = fields.CharField(max_length=36, index=True) 46 | chunk_index = fields.IntField() 47 | chunk_hash = fields.CharField(max_length=64) 48 | total_chunks = fields.IntField() 49 | file_size = fields.BigIntField() 50 | chunk_size = fields.IntField() 51 | file_name = fields.CharField(max_length=255) 52 | created_at = fields.DatetimeField(auto_now_add=True) 53 | completed = fields.BooleanField(default=False) 54 | 55 | 56 | class KeyValue(Model): 57 | id: Optional[int] = fields.IntField(pk=True) 58 | key: Optional[str] = fields.CharField( 59 | max_length=255, description="键", index=True, unique=True 60 | ) 61 | value: Optional[str] = fields.JSONField(description="值", null=True) 62 | created_at: Optional[datetime] = fields.DatetimeField( 63 | auto_now_add=True, description="创建时间" 64 | ) 65 | 66 | 67 | file_codes_pydantic = pydantic_model_creator(FileCodes, name="FileCodes") 68 | upload_chunk_pydantic = pydantic_model_creator(UploadChunk, name="UploadChunk") 69 | key_value_pydantic = pydantic_model_creator(KeyValue, name="KeyValue") 70 | -------------------------------------------------------------------------------- /apps/base/schemas.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel 2 | 3 | 4 | class SelectFileModel(BaseModel): 5 | code: str 6 | 7 | 8 | class InitChunkUploadModel(BaseModel): 9 | file_name: str 10 | chunk_size: int = 5 * 1024 * 1024 11 | file_size: int 12 | file_hash: str 13 | 14 | 15 | class CompleteUploadModel(BaseModel): 16 | expire_value: int 17 | expire_style: str 18 | -------------------------------------------------------------------------------- /apps/base/utils.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import hashlib 3 | import os 4 | import uuid 5 | 6 | from fastapi import UploadFile, HTTPException 7 | from typing import Optional, Tuple 8 | 9 | from apps.base.dependencies import IPRateLimit 10 | from apps.base.models import FileCodes 11 | from core.settings import settings 12 | from core.utils import get_random_num, get_random_string, max_save_times_desc, sanitize_filename 13 | 14 | 15 | async def get_file_path_name(file: UploadFile) -> Tuple[str, str, str, str, str]: 16 | """获取文件路径和文件名""" 17 | today = datetime.datetime.now() 18 | storage_path = settings.storage_path.strip("/") # 移除开头和结尾的斜杠 19 | file_uuid = uuid.uuid4().hex 20 | filename = await sanitize_filename(file.filename) 21 | # 使用 UUID 作为子目录名 22 | base_path = f"share/data/{today.strftime('%Y/%m/%d')}/{file_uuid}" 23 | 24 | # 如果设置了存储路径,将其添加到基础路径中 25 | path = f"{storage_path}/{base_path}" if storage_path else base_path 26 | 27 | prefix, suffix = os.path.splitext(filename) 28 | # 保持原始文件名 29 | save_path = f"{path}/{filename}" 30 | return path, suffix, prefix, filename, save_path 31 | 32 | 33 | async def get_chunk_file_path_name(file_name: str, upload_id: str) -> Tuple[str, str, str, str, str]: 34 | """获取切片文件的路径和文件名""" 35 | today = datetime.datetime.now() 36 | storage_path = settings.storage_path.strip("/") # 移除开头和结尾的斜杠 37 | base_path = f"share/data/{today.strftime('%Y/%m/%d')}/{upload_id}" 38 | path = f"{storage_path}/{base_path}" if storage_path else base_path 39 | prefix, suffix = os.path.splitext(file_name) 40 | save_path = f"{path}/{prefix}{suffix}" 41 | return path, suffix, prefix, file_name, save_path 42 | 43 | 44 | async def get_expire_info( 45 | expire_value: int, expire_style: str 46 | ) -> Tuple[Optional[datetime.datetime], int, int, str]: 47 | """获取过期信息""" 48 | expired_count, used_count = -1, 0 49 | now = datetime.datetime.now() 50 | code = None 51 | 52 | max_timedelta = ( 53 | datetime.timedelta(seconds=settings.max_save_seconds) 54 | if settings.max_save_seconds > 0 55 | else datetime.timedelta(days=7) 56 | ) 57 | detail = ( 58 | await max_save_times_desc(settings.max_save_seconds) 59 | if settings.max_save_seconds > 0 60 | else "7天" 61 | ) 62 | detail = f"限制最长时间为 {detail[0]},可换用其他方式" 63 | 64 | expire_styles = { 65 | "day": lambda: now + datetime.timedelta(days=expire_value), 66 | "hour": lambda: now + datetime.timedelta(hours=expire_value), 67 | "minute": lambda: now + datetime.timedelta(minutes=expire_value), 68 | "count": lambda: (now + datetime.timedelta(days=1), expire_value), 69 | "forever": lambda: (None, None), # 修改这里 70 | } 71 | 72 | if expire_style in expire_styles: 73 | result = expire_styles[expire_style]() 74 | if isinstance(result, tuple): 75 | expired_at, extra = result 76 | if expire_style == "count": 77 | expired_count = extra 78 | elif expire_style == "forever": 79 | code = await get_random_code(style="string") # 移动到这里 80 | else: 81 | expired_at = result 82 | if expired_at and expired_at - now > max_timedelta: 83 | raise HTTPException(status_code=403, detail=detail) 84 | else: 85 | expired_at = now + datetime.timedelta(days=1) 86 | 87 | if not code: 88 | code = await get_random_code() 89 | 90 | return expired_at, expired_count, used_count, code 91 | 92 | 93 | async def get_random_code(style="num") -> str: 94 | """获取随机字符串""" 95 | while True: 96 | code = await get_random_num() if style == "num" else await get_random_string() 97 | if not await FileCodes.filter(code=code).exists(): 98 | return code 99 | 100 | 101 | async def calculate_file_hash(file: UploadFile, chunk_size=1024 * 1024) -> str: 102 | sha = hashlib.sha256() 103 | await file.seek(0) 104 | while True: 105 | chunk = await file.read(chunk_size) 106 | if not chunk: 107 | break 108 | sha.update(chunk) 109 | await file.seek(0) 110 | return sha.hexdigest() 111 | 112 | 113 | ip_limit = { 114 | "error": IPRateLimit(count=settings.uploadCount, minutes=settings.errorMinute), 115 | "upload": IPRateLimit(count=settings.errorCount, minutes=settings.errorMinute), 116 | } 117 | -------------------------------------------------------------------------------- /core/__init__.py: -------------------------------------------------------------------------------- 1 | # @Time : 2023/8/11 20:06 2 | # @Author : Lan 3 | # @File : __init__.py.py 4 | # @Software: PyCharm 5 | -------------------------------------------------------------------------------- /core/database.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import importlib 3 | import os 4 | 5 | from tortoise import Tortoise 6 | 7 | from core.logger import logger 8 | from core.settings import data_root 9 | 10 | 11 | async def init_db(): 12 | try: 13 | # 使用正确的Tortoise初始化配置格式 14 | db_config = { 15 | "db_url": f"sqlite://{data_root}/filecodebox.db", 16 | "modules": {"models": ["apps.base.models"]}, 17 | "use_tz": False, 18 | "timezone": "Asia/Shanghai" 19 | } 20 | 21 | await Tortoise.init(**db_config) 22 | 23 | # 创建migrations表 24 | await Tortoise.get_connection("default").execute_script(""" 25 | CREATE TABLE IF NOT EXISTS migrates ( 26 | id INTEGER PRIMARY KEY AUTOINCREMENT, 27 | migration_file VARCHAR(255) NOT NULL UNIQUE, 28 | executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 29 | ) 30 | """) 31 | 32 | # 执行迁移 33 | await execute_migrations() 34 | 35 | except Exception as e: 36 | logger.error(f"数据库初始化失败: {str(e)}") 37 | raise 38 | 39 | 40 | async def execute_migrations(): 41 | """执行数据库迁移""" 42 | try: 43 | # 收集迁移文件 44 | migration_files = [] 45 | for root, dirs, files in os.walk("apps"): 46 | if "migrations" in dirs: 47 | migration_path = os.path.join(root, "migrations") 48 | migration_files.extend(glob.glob(os.path.join(migration_path, "migrations_*.py"))) 49 | 50 | # 按文件名排序 51 | migration_files.sort() 52 | 53 | for migration_file in migration_files: 54 | file_name = os.path.basename(migration_file) 55 | 56 | # 检查是否已执行 57 | executed = await Tortoise.get_connection("default").execute_query( 58 | "SELECT id FROM migrates WHERE migration_file = ?", [file_name] 59 | ) 60 | 61 | if not executed[1]: 62 | logger.info(f"执行迁移: {file_name}") 63 | # 导入并执行migration 64 | module_path = migration_file.replace("/", ".").replace("\\", ".").replace(".py", "") 65 | try: 66 | migration_module = importlib.import_module(module_path) 67 | if hasattr(migration_module, "migrate"): 68 | await migration_module.migrate() 69 | # 记录执行 70 | await Tortoise.get_connection("default").execute_query( 71 | "INSERT INTO migrates (migration_file) VALUES (?)", 72 | [file_name] 73 | ) 74 | logger.info(f"迁移完成: {file_name}") 75 | except Exception as e: 76 | logger.error(f"迁移 {file_name} 执行失败: {str(e)}") 77 | raise 78 | 79 | except Exception as e: 80 | logger.error(f"迁移过程发生错误: {str(e)}") 81 | raise 82 | -------------------------------------------------------------------------------- /core/logger.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import sys 3 | 4 | 5 | def setup_logger(): 6 | # 创建logger对象 7 | _logger = logging.getLogger('FileCodeBox') 8 | _logger.setLevel(logging.INFO) 9 | 10 | # 创建控制台处理器 11 | console_handler = logging.StreamHandler(sys.stdout) 12 | console_handler.setLevel(logging.INFO) 13 | 14 | # 设置日志格式 15 | formatter = logging.Formatter( 16 | '%(asctime)s - %(name)s - %(levelname)s - %(message)s', 17 | datefmt='%Y-%m-%d %H:%M:%S' 18 | ) 19 | console_handler.setFormatter(formatter) 20 | 21 | # 添加处理器到logger 22 | _logger.addHandler(console_handler) 23 | 24 | return _logger 25 | 26 | 27 | # 创建全局logger实例 28 | logger = setup_logger() 29 | -------------------------------------------------------------------------------- /core/response.py: -------------------------------------------------------------------------------- 1 | # @Time : 2023/8/14 11:48 2 | # @Author : Lan 3 | # @File : response.py 4 | # @Software: PyCharm 5 | from typing import Generic, TypeVar 6 | 7 | from pydantic.v1.generics import GenericModel 8 | 9 | T = TypeVar("T") 10 | 11 | 12 | class APIResponse(GenericModel, Generic[T]): 13 | code: int = 200 14 | message: str = "ok" 15 | detail: T 16 | -------------------------------------------------------------------------------- /core/settings.py: -------------------------------------------------------------------------------- 1 | # @Time : 2023/8/15 09:51 2 | # @Author : Lan 3 | # @File : settings.py 4 | # @Software: PyCharm 5 | from pathlib import Path 6 | 7 | BASE_DIR = Path(__file__).resolve().parent.parent 8 | data_root = BASE_DIR / "data" 9 | 10 | if not data_root.exists(): 11 | data_root.mkdir(parents=True, exist_ok=True) 12 | 13 | DEFAULT_CONFIG = { 14 | "file_storage": "local", 15 | "storage_path": "", 16 | "name": "文件快递柜 - FileCodeBox", 17 | "description": "开箱即用的文件快传系统", 18 | "notify_title": "系统通知", 19 | "notify_content": '欢迎使用 FileCodeBox,本程序开源于 Github ,欢迎Star和Fork。', 20 | "page_explain": "请勿上传或分享违法内容。根据《中华人民共和国网络安全法》、《中华人民共和国刑法》、《中华人民共和国治安管理处罚法》等相关规定。 传播或存储违法、违规内容,会受到相关处罚,严重者将承担刑事责任。本站坚决配合相关部门,确保网络内容的安全,和谐,打造绿色网络环境。", 21 | "keywords": "FileCodeBox, 文件快递柜, 口令传送箱, 匿名口令分享文本, 文件", 22 | "s3_access_key_id": "", 23 | "s3_secret_access_key": "", 24 | "s3_bucket_name": "", 25 | "s3_endpoint_url": "", 26 | "s3_region_name": "auto", 27 | "s3_signature_version": "s3v2", 28 | "s3_hostname": "", 29 | "s3_proxy": 0, 30 | "max_save_seconds": 0, 31 | "aws_session_token": "", 32 | "onedrive_domain": "", 33 | "onedrive_client_id": "", 34 | "onedrive_username": "", 35 | "onedrive_password": "", 36 | "onedrive_root_path": "filebox_storage", 37 | "onedrive_proxy": 0, 38 | "webdav_hostname": "", 39 | "webdav_root_path": "filebox_storage", 40 | "webdav_proxy": 0, 41 | "admin_token": "FileCodeBox2023", 42 | "openUpload": 1, 43 | "uploadSize": 1024 * 1024 * 10, 44 | "expireStyle": ["day", "hour", "minute", "forever", "count"], 45 | "uploadMinute": 1, 46 | "enableChunk": 0, 47 | "webdav_url": "", 48 | "webdav_password": "", 49 | "webdav_username": "", 50 | "opacity": 0.9, 51 | "background": "", 52 | "uploadCount": 10, 53 | "themesChoices": [ 54 | { 55 | "name": "2023", 56 | "key": "themes/2023", 57 | "author": "Lan", 58 | "version": "1.0", 59 | }, 60 | { 61 | "name": "2024", 62 | "key": "themes/2024", 63 | "author": "Lan", 64 | "version": "1.0", 65 | }, 66 | ], 67 | "themesSelect": "themes/2024", 68 | "errorMinute": 1, 69 | "errorCount": 1, 70 | "port": 12345, 71 | "showAdminAddr": 0, 72 | "robotsText": "User-agent: *\nDisallow: /", 73 | } 74 | 75 | 76 | class Settings: 77 | def __init__(self, defaults=None): 78 | self.default_config = defaults or {} 79 | self.user_config = {} 80 | 81 | def __getattr__(self, attr): 82 | if attr in self.user_config: 83 | return self.user_config[attr] 84 | if attr in self.default_config: 85 | return self.default_config[attr] 86 | raise AttributeError( 87 | f"'{self.__class__.__name__}' object has no attribute '{attr}'" 88 | ) 89 | 90 | def __setattr__(self, key, value): 91 | if key in ["default_config", "user_config"]: 92 | super().__setattr__(key, value) 93 | else: 94 | self.user_config[key] = value 95 | 96 | def items(self): 97 | return {**self.default_config, **self.user_config}.items() 98 | 99 | 100 | settings = Settings(DEFAULT_CONFIG) 101 | -------------------------------------------------------------------------------- /core/tasks.py: -------------------------------------------------------------------------------- 1 | # @Time : 2023/8/15 22:00 2 | # @Author : Lan 3 | # @File : tasks.py 4 | # @Software: PyCharm 5 | import asyncio 6 | import logging 7 | import os 8 | 9 | from tortoise.expressions import Q 10 | 11 | from apps.base.models import FileCodes 12 | from apps.base.utils import ip_limit 13 | from core.settings import settings, data_root 14 | from core.storage import FileStorageInterface, storages 15 | from core.utils import get_now 16 | 17 | 18 | async def delete_expire_files(): 19 | file_storage: FileStorageInterface = storages[settings.file_storage]() 20 | while True: 21 | try: 22 | # 遍历 share目录下的所有文件夹,删除空的文件夹,并判断父目录是否为空,如果为空也删除 23 | if settings.file_storage == "local": 24 | for root, dirs, files in os.walk(f"{data_root}/share/data"): 25 | if not dirs and not files: 26 | os.rmdir(root) 27 | await ip_limit["error"].remove_expired_ip() 28 | await ip_limit["upload"].remove_expired_ip() 29 | expire_data = await FileCodes.filter( 30 | Q(expired_at__lt=await get_now()) | Q(expired_count=0) 31 | ).all() 32 | for exp in expire_data: 33 | await file_storage.delete_file(exp) 34 | await exp.delete() 35 | except Exception as e: 36 | logging.error(e) 37 | finally: 38 | await asyncio.sleep(600) 39 | -------------------------------------------------------------------------------- /core/utils.py: -------------------------------------------------------------------------------- 1 | # @Time : 2023/8/13 19:54 2 | # @Author : Lan 3 | # @File : utils.py 4 | # @Software: PyCharm 5 | import datetime 6 | import hashlib 7 | import os 8 | import random 9 | import re 10 | import string 11 | import time 12 | from core.settings import settings 13 | 14 | 15 | async def get_random_num(): 16 | """ 17 | 获取随机数 18 | :return: 19 | """ 20 | return random.randint(10000, 99999) 21 | 22 | 23 | r_s = string.ascii_uppercase + string.digits 24 | 25 | 26 | async def get_random_string(): 27 | """ 28 | 获取随机字符串 29 | :return: 30 | """ 31 | return "".join(random.choice(r_s) for _ in range(5)) 32 | 33 | 34 | async def get_now(): 35 | """ 36 | 获取当前时间 37 | :return: 38 | """ 39 | return datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=8))) 40 | 41 | 42 | async def get_select_token(code: str): 43 | """ 44 | 获取下载token 45 | :param code: 46 | :return: 47 | """ 48 | token = settings.admin_token 49 | return hashlib.sha256( 50 | f"{code}{int(time.time() / 1000)}000{token}".encode() 51 | ).hexdigest() 52 | 53 | 54 | async def get_file_url(code: str): 55 | """ 56 | 对于需要通过服务器中转下载的服务,获取文件下载地址 57 | :param code: 58 | :return: 59 | """ 60 | return f"/share/download?key={await get_select_token(code)}&code={code}" 61 | 62 | 63 | async def max_save_times_desc(max_save_seconds: int): 64 | """ 65 | 获取最大保存时间的描述 66 | :param max_save_seconds: 67 | :return: 68 | """ 69 | 70 | def gen_desc_zh(value: int, desc: str): 71 | if value > 0: 72 | return f"{value}{desc}" 73 | else: 74 | return "" 75 | 76 | def gen_desc_en(value: int, desc: str): 77 | if value > 0: 78 | ret = f"{value} {desc}" 79 | if value > 1: 80 | ret += "s" 81 | ret += " " 82 | return ret 83 | else: 84 | return "" 85 | 86 | max_timedelta = datetime.timedelta(seconds=max_save_seconds) 87 | desc_zh, desc_en = "最长保存时间:", "Max save time: " 88 | desc_zh += gen_desc_zh(max_timedelta.days, "天") 89 | desc_en += gen_desc_en(max_timedelta.days, "day") 90 | desc_zh += gen_desc_zh(max_timedelta.seconds // 3600, "小时") 91 | desc_en += gen_desc_en(max_timedelta.seconds // 3600, "hour") 92 | desc_zh += gen_desc_zh(max_timedelta.seconds % 3600 // 60, "分钟") 93 | desc_en += gen_desc_en(max_timedelta.seconds % 3600 // 60, "minute") 94 | desc_zh += gen_desc_zh(max_timedelta.seconds % 60, "秒") 95 | desc_en += gen_desc_en(max_timedelta.seconds % 60, "second") 96 | return desc_zh, desc_en 97 | 98 | 99 | async def sanitize_filename(filename: str) -> str: 100 | """ 101 | 安全处理文件名: 102 | 1. 剥离路径只保留文件名 103 | 2. 替换非法字符 104 | 3. 处理空文件名情况 105 | """ 106 | filename = os.path.basename(filename) 107 | illegal_chars = r'[\\/*?:"<>|\x00-\x1F]' # 包含控制字符 108 | # 替换非法字符为下划线 109 | cleaned = re.sub(illegal_chars, '_', filename) 110 | # 处理空格(可选替换为_) 111 | cleaned = cleaned.replace(' ', '_') 112 | # 处理连续下划线 113 | cleaned = re.sub(r'_+', '_', cleaned) 114 | # 处理首尾特殊字符 115 | cleaned = cleaned.strip('._') 116 | # 处理空文件名情况 117 | if not cleaned: 118 | cleaned = 'unnamed_file' 119 | # 长度限制(按需调整) 120 | return cleaned[:255] 121 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | file-code-box: 4 | image: lanol/filecodebox:latest 5 | volumes: 6 | - fcb-data:/app/data:rw 7 | restart: unless-stopped 8 | ports: 9 | - "12345:12345" 10 | volumes: 11 | fcb-data: 12 | external: false -------------------------------------------------------------------------------- /docs/.vitepress/cache/deps/_metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "hash": "d07f6dee", 3 | "configHash": "dee2d3bc", 4 | "lockfileHash": "bd28b2c2", 5 | "browserHash": "b96651ac", 6 | "optimized": { 7 | "vue": { 8 | "src": "../../../node_modules/.pnpm/vue@3.5.13/node_modules/vue/dist/vue.runtime.esm-bundler.js", 9 | "file": "vue.js", 10 | "fileHash": "bc5c7f34", 11 | "needsInterop": false 12 | }, 13 | "vitepress > @vue/devtools-api": { 14 | "src": "../../../node_modules/.pnpm/@vue+devtools-api@7.7.1/node_modules/@vue/devtools-api/dist/index.js", 15 | "file": "vitepress___@vue_devtools-api.js", 16 | "fileHash": "00859881", 17 | "needsInterop": false 18 | }, 19 | "vitepress > @vueuse/core": { 20 | "src": "../../../node_modules/.pnpm/@vueuse+core@12.5.0/node_modules/@vueuse/core/index.mjs", 21 | "file": "vitepress___@vueuse_core.js", 22 | "fileHash": "1c6da938", 23 | "needsInterop": false 24 | }, 25 | "vitepress > @vueuse/integrations/useFocusTrap": { 26 | "src": "../../../node_modules/.pnpm/@vueuse+integrations@12.5.0_focus-trap@7.6.4/node_modules/@vueuse/integrations/useFocusTrap.mjs", 27 | "file": "vitepress___@vueuse_integrations_useFocusTrap.js", 28 | "fileHash": "11c08fcf", 29 | "needsInterop": false 30 | }, 31 | "vitepress > mark.js/src/vanilla.js": { 32 | "src": "../../../node_modules/.pnpm/mark.js@8.11.1/node_modules/mark.js/src/vanilla.js", 33 | "file": "vitepress___mark__js_src_vanilla__js.js", 34 | "fileHash": "b93dbfe7", 35 | "needsInterop": false 36 | }, 37 | "vitepress > minisearch": { 38 | "src": "../../../node_modules/.pnpm/minisearch@7.1.1/node_modules/minisearch/dist/es/index.js", 39 | "file": "vitepress___minisearch.js", 40 | "fileHash": "bfb6c3ac", 41 | "needsInterop": false 42 | }, 43 | "@theme/index": { 44 | "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", 45 | "file": "@theme_index.js", 46 | "fileHash": "07512287", 47 | "needsInterop": false 48 | } 49 | }, 50 | "chunks": { 51 | "chunk-KT7LHMJ2": { 52 | "file": "chunk-KT7LHMJ2.js" 53 | }, 54 | "chunk-CQOUZRMK": { 55 | "file": "chunk-CQOUZRMK.js" 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /docs/.vitepress/cache/deps/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module" 3 | } 4 | -------------------------------------------------------------------------------- /docs/.vitepress/cache/deps/vitepress___@vueuse_core.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": [], 4 | "sourcesContent": [], 5 | "mappings": "", 6 | "names": [] 7 | } 8 | -------------------------------------------------------------------------------- /docs/.vitepress/cache/deps/vue.js: -------------------------------------------------------------------------------- 1 | import { 2 | BaseTransition, 3 | BaseTransitionPropsValidators, 4 | Comment, 5 | DeprecationTypes, 6 | EffectScope, 7 | ErrorCodes, 8 | ErrorTypeStrings, 9 | Fragment, 10 | KeepAlive, 11 | ReactiveEffect, 12 | Static, 13 | Suspense, 14 | Teleport, 15 | Text, 16 | TrackOpTypes, 17 | Transition, 18 | TransitionGroup, 19 | TriggerOpTypes, 20 | VueElement, 21 | assertNumber, 22 | callWithAsyncErrorHandling, 23 | callWithErrorHandling, 24 | camelize, 25 | capitalize, 26 | cloneVNode, 27 | compatUtils, 28 | compile, 29 | computed, 30 | createApp, 31 | createBaseVNode, 32 | createBlock, 33 | createCommentVNode, 34 | createElementBlock, 35 | createHydrationRenderer, 36 | createPropsRestProxy, 37 | createRenderer, 38 | createSSRApp, 39 | createSlots, 40 | createStaticVNode, 41 | createTextVNode, 42 | createVNode, 43 | customRef, 44 | defineAsyncComponent, 45 | defineComponent, 46 | defineCustomElement, 47 | defineEmits, 48 | defineExpose, 49 | defineModel, 50 | defineOptions, 51 | defineProps, 52 | defineSSRCustomElement, 53 | defineSlots, 54 | devtools, 55 | effect, 56 | effectScope, 57 | getCurrentInstance, 58 | getCurrentScope, 59 | getCurrentWatcher, 60 | getTransitionRawChildren, 61 | guardReactiveProps, 62 | h, 63 | handleError, 64 | hasInjectionContext, 65 | hydrate, 66 | hydrateOnIdle, 67 | hydrateOnInteraction, 68 | hydrateOnMediaQuery, 69 | hydrateOnVisible, 70 | initCustomFormatter, 71 | initDirectivesForSSR, 72 | inject, 73 | isMemoSame, 74 | isProxy, 75 | isReactive, 76 | isReadonly, 77 | isRef, 78 | isRuntimeOnly, 79 | isShallow, 80 | isVNode, 81 | markRaw, 82 | mergeDefaults, 83 | mergeModels, 84 | mergeProps, 85 | nextTick, 86 | normalizeClass, 87 | normalizeProps, 88 | normalizeStyle, 89 | onActivated, 90 | onBeforeMount, 91 | onBeforeUnmount, 92 | onBeforeUpdate, 93 | onDeactivated, 94 | onErrorCaptured, 95 | onMounted, 96 | onRenderTracked, 97 | onRenderTriggered, 98 | onScopeDispose, 99 | onServerPrefetch, 100 | onUnmounted, 101 | onUpdated, 102 | onWatcherCleanup, 103 | openBlock, 104 | popScopeId, 105 | provide, 106 | proxyRefs, 107 | pushScopeId, 108 | queuePostFlushCb, 109 | reactive, 110 | readonly, 111 | ref, 112 | registerRuntimeCompiler, 113 | render, 114 | renderList, 115 | renderSlot, 116 | resolveComponent, 117 | resolveDirective, 118 | resolveDynamicComponent, 119 | resolveFilter, 120 | resolveTransitionHooks, 121 | setBlockTracking, 122 | setDevtoolsHook, 123 | setTransitionHooks, 124 | shallowReactive, 125 | shallowReadonly, 126 | shallowRef, 127 | ssrContextKey, 128 | ssrUtils, 129 | stop, 130 | toDisplayString, 131 | toHandlerKey, 132 | toHandlers, 133 | toRaw, 134 | toRef, 135 | toRefs, 136 | toValue, 137 | transformVNodeArgs, 138 | triggerRef, 139 | unref, 140 | useAttrs, 141 | useCssModule, 142 | useCssVars, 143 | useHost, 144 | useId, 145 | useModel, 146 | useSSRContext, 147 | useShadowRoot, 148 | useSlots, 149 | useTemplateRef, 150 | useTransitionState, 151 | vModelCheckbox, 152 | vModelDynamic, 153 | vModelRadio, 154 | vModelSelect, 155 | vModelText, 156 | vShow, 157 | version, 158 | warn, 159 | watch, 160 | watchEffect, 161 | watchPostEffect, 162 | watchSyncEffect, 163 | withAsyncContext, 164 | withCtx, 165 | withDefaults, 166 | withDirectives, 167 | withKeys, 168 | withMemo, 169 | withModifiers, 170 | withScopeId 171 | } from "./chunk-CQOUZRMK.js"; 172 | export { 173 | BaseTransition, 174 | BaseTransitionPropsValidators, 175 | Comment, 176 | DeprecationTypes, 177 | EffectScope, 178 | ErrorCodes, 179 | ErrorTypeStrings, 180 | Fragment, 181 | KeepAlive, 182 | ReactiveEffect, 183 | Static, 184 | Suspense, 185 | Teleport, 186 | Text, 187 | TrackOpTypes, 188 | Transition, 189 | TransitionGroup, 190 | TriggerOpTypes, 191 | VueElement, 192 | assertNumber, 193 | callWithAsyncErrorHandling, 194 | callWithErrorHandling, 195 | camelize, 196 | capitalize, 197 | cloneVNode, 198 | compatUtils, 199 | compile, 200 | computed, 201 | createApp, 202 | createBlock, 203 | createCommentVNode, 204 | createElementBlock, 205 | createBaseVNode as createElementVNode, 206 | createHydrationRenderer, 207 | createPropsRestProxy, 208 | createRenderer, 209 | createSSRApp, 210 | createSlots, 211 | createStaticVNode, 212 | createTextVNode, 213 | createVNode, 214 | customRef, 215 | defineAsyncComponent, 216 | defineComponent, 217 | defineCustomElement, 218 | defineEmits, 219 | defineExpose, 220 | defineModel, 221 | defineOptions, 222 | defineProps, 223 | defineSSRCustomElement, 224 | defineSlots, 225 | devtools, 226 | effect, 227 | effectScope, 228 | getCurrentInstance, 229 | getCurrentScope, 230 | getCurrentWatcher, 231 | getTransitionRawChildren, 232 | guardReactiveProps, 233 | h, 234 | handleError, 235 | hasInjectionContext, 236 | hydrate, 237 | hydrateOnIdle, 238 | hydrateOnInteraction, 239 | hydrateOnMediaQuery, 240 | hydrateOnVisible, 241 | initCustomFormatter, 242 | initDirectivesForSSR, 243 | inject, 244 | isMemoSame, 245 | isProxy, 246 | isReactive, 247 | isReadonly, 248 | isRef, 249 | isRuntimeOnly, 250 | isShallow, 251 | isVNode, 252 | markRaw, 253 | mergeDefaults, 254 | mergeModels, 255 | mergeProps, 256 | nextTick, 257 | normalizeClass, 258 | normalizeProps, 259 | normalizeStyle, 260 | onActivated, 261 | onBeforeMount, 262 | onBeforeUnmount, 263 | onBeforeUpdate, 264 | onDeactivated, 265 | onErrorCaptured, 266 | onMounted, 267 | onRenderTracked, 268 | onRenderTriggered, 269 | onScopeDispose, 270 | onServerPrefetch, 271 | onUnmounted, 272 | onUpdated, 273 | onWatcherCleanup, 274 | openBlock, 275 | popScopeId, 276 | provide, 277 | proxyRefs, 278 | pushScopeId, 279 | queuePostFlushCb, 280 | reactive, 281 | readonly, 282 | ref, 283 | registerRuntimeCompiler, 284 | render, 285 | renderList, 286 | renderSlot, 287 | resolveComponent, 288 | resolveDirective, 289 | resolveDynamicComponent, 290 | resolveFilter, 291 | resolveTransitionHooks, 292 | setBlockTracking, 293 | setDevtoolsHook, 294 | setTransitionHooks, 295 | shallowReactive, 296 | shallowReadonly, 297 | shallowRef, 298 | ssrContextKey, 299 | ssrUtils, 300 | stop, 301 | toDisplayString, 302 | toHandlerKey, 303 | toHandlers, 304 | toRaw, 305 | toRef, 306 | toRefs, 307 | toValue, 308 | transformVNodeArgs, 309 | triggerRef, 310 | unref, 311 | useAttrs, 312 | useCssModule, 313 | useCssVars, 314 | useHost, 315 | useId, 316 | useModel, 317 | useSSRContext, 318 | useShadowRoot, 319 | useSlots, 320 | useTemplateRef, 321 | useTransitionState, 322 | vModelCheckbox, 323 | vModelDynamic, 324 | vModelRadio, 325 | vModelSelect, 326 | vModelText, 327 | vShow, 328 | version, 329 | warn, 330 | watch, 331 | watchEffect, 332 | watchPostEffect, 333 | watchSyncEffect, 334 | withAsyncContext, 335 | withCtx, 336 | withDefaults, 337 | withDirectives, 338 | withKeys, 339 | withMemo, 340 | withModifiers, 341 | withScopeId 342 | }; 343 | //# sourceMappingURL=vue.js.map 344 | -------------------------------------------------------------------------------- /docs/.vitepress/cache/deps/vue.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": [], 4 | "sourcesContent": [], 5 | "mappings": "", 6 | "names": [] 7 | } 8 | -------------------------------------------------------------------------------- /docs/.vitepress/config.mts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitepress' 2 | 3 | // https://vitepress.dev/reference/site-config 4 | export default defineConfig({ 5 | title: 'FileCodeBox', 6 | description: '简单高效的文件分享工具', 7 | lang: 'zh-CN', 8 | lastUpdated: true, 9 | locales: { 10 | root: { 11 | label: '简体中文', 12 | lang: 'zh-CN', 13 | title: 'FileCodeBox', 14 | description: '匿名口令分享文本,文件', 15 | themeConfig: { 16 | logo: '/logo_small.png', 17 | nav: [ 18 | { text: '首页', link: '/' }, 19 | { text: '指南', link: '/guide/getting-started' }, 20 | { text: 'API', link: '/api/' }, 21 | { text: 'Demo', link: 'https://share.lanol.cn' }, 22 | { 23 | text: '了解更多', 24 | items: [ 25 | { text: '更新日志', link: '/changelog' }, 26 | { text: '贡献指南', link: '/contributing' }, 27 | ], 28 | }, 29 | ], 30 | 31 | sidebar: { 32 | '/guide/': [ 33 | { 34 | text: '介绍', 35 | items: [ 36 | { 37 | text: '什么是 FileCodeBox', 38 | link: '/guide/introduction', 39 | }, 40 | { text: '快速开始', link: '/guide/getting-started' }, 41 | ], 42 | }, 43 | { 44 | text: '基础功能', 45 | items: [ 46 | { text: '文件上传', link: '/guide/upload' }, 47 | { text: '文件分享', link: '/guide/share' }, 48 | { text: '文件管理', link: '/guide/management' }, 49 | ], 50 | }, 51 | { 52 | text: '高级特性', 53 | items: [ 54 | { text: '存储配置', link: '/guide/storage' }, 55 | { text: '安全设置', link: '/guide/security' }, 56 | { text: '系统配置', link: '/guide/configuration' }, 57 | ], 58 | }, 59 | ], 60 | '/api/': [ 61 | { 62 | text: 'API 参考', 63 | items: [ 64 | { text: '分享接口', link: '/api/#分享接口' }, 65 | { text: '管理接口', link: '/api/#管理接口' }, 66 | { text: '错误响应', link: '/api/#错误响应' }, 67 | { text: '状态码说明', link: '/api/#状态码说明' }, 68 | ], 69 | }, 70 | ], 71 | }, 72 | socialLinks: [ 73 | { icon: 'github', link: 'https://github.com/vastsa/FileCodeBox' }, 74 | ], 75 | footer: { 76 | message: '基于 LGPL-3.0 license 发布', 77 | copyright: 'Copyright © 2022-present FileCodeBox', 78 | }, 79 | search: { 80 | provider: 'local', 81 | }, 82 | outline: { 83 | level: [2, 3], 84 | label: '目录', 85 | }, 86 | }, 87 | }, 88 | en: { 89 | label: 'English', 90 | lang: 'en-US', 91 | title: 'FileCodeBox', 92 | description: 'Simple and efficient file sharing tool', 93 | themeConfig: { 94 | logo: '/logo_small.png', 95 | nav: [ 96 | { text: 'Home', link: '/en/' }, 97 | { text: 'Guide', link: '/en/guide/getting-started' }, 98 | { text: 'API', link: '/en/api/' }, 99 | { text: 'Demo', link: 'https://share.lanol.cn' }, 100 | { 101 | text: 'More', 102 | items: [ 103 | { text: 'Changelog', link: '/en/changelog' }, 104 | { text: 'Contributing', link: '/en/contributing' }, 105 | ], 106 | }, 107 | ], 108 | 109 | sidebar: { 110 | '/en/guide/': [ 111 | { 112 | text: 'Introduction', 113 | items: [ 114 | { 115 | text: 'What is FileCodeBox', 116 | link: '/en/guide/introduction', 117 | }, 118 | { 119 | text: 'Getting Started', 120 | link: '/en/guide/getting-started', 121 | }, 122 | ], 123 | }, 124 | { 125 | text: 'Basic Features', 126 | items: [ 127 | { text: 'File Upload', link: '/en/guide/upload' }, 128 | { text: 'File Sharing', link: '/en/guide/share' }, 129 | { text: 'File Management', link: '/en/guide/management' }, 130 | ], 131 | }, 132 | { 133 | text: 'Advanced Features', 134 | items: [ 135 | { text: 'Storage Configuration', link: '/en/guide/storage' }, 136 | { text: 'Security Settings', link: '/en/guide/security' }, 137 | { 138 | text: 'System Configuration', 139 | link: '/en/guide/configuration', 140 | }, 141 | ], 142 | }, 143 | ], 144 | '/en/api/': [ 145 | { 146 | text: 'API Reference', 147 | items: [ 148 | { text: 'Share API', link: '/en/api/#share-api' }, 149 | { text: 'Admin API', link: '/en/api/#admin-api' }, 150 | { text: 'Error Response', link: '/en/api/#error-response' }, 151 | { text: 'Status Codes', link: '/en/api/#status-codes' }, 152 | ], 153 | }, 154 | ], 155 | }, 156 | socialLinks: [ 157 | { icon: 'github', link: 'https://github.com/vastsa/FileCodeBox' }, 158 | ], 159 | footer: { 160 | message: 'Released under the LGPL-3.0 license', 161 | copyright: 'Copyright © 2022-present FileCodeBox', 162 | }, 163 | search: { 164 | provider: 'local', 165 | }, 166 | outline: { 167 | level: [2, 3], 168 | label: '目录', 169 | }, 170 | }, 171 | }, 172 | }, 173 | 174 | themeConfig: { 175 | // 语言切换器 176 | langMenuLabel: '切换语言', 177 | 178 | // 社交链接 179 | socialLinks: [ 180 | { icon: 'github', link: 'https://github.com/vastsa/FileCodeBox' }, 181 | ], 182 | 183 | // 页脚 184 | footer: { 185 | message: '基于 LGPL-3.0 license 发布', 186 | copyright: 'Copyright © 2022-present FileCodeBox', 187 | }, 188 | 189 | // 搜索 190 | search: { 191 | provider: 'local', 192 | options: { 193 | locales: { 194 | zh: { 195 | translations: { 196 | button: { 197 | buttonText: '搜索文档', 198 | buttonAriaLabel: '搜索文档', 199 | }, 200 | modal: { 201 | noResultsText: '无法找到相关结果', 202 | resetButtonTitle: '清除查询条件', 203 | footer: { 204 | selectText: '选择', 205 | navigateText: '切换', 206 | }, 207 | }, 208 | }, 209 | }, 210 | en: { 211 | translations: { 212 | button: { 213 | buttonText: 'Search', 214 | buttonAriaLabel: 'Search docs', 215 | }, 216 | modal: { 217 | noResultsText: 'No results found', 218 | resetButtonTitle: 'Clear query', 219 | footer: { 220 | selectText: 'to select', 221 | navigateText: 'to navigate', 222 | }, 223 | }, 224 | }, 225 | }, 226 | }, 227 | }, 228 | }, 229 | 230 | outline: { 231 | level: [2, 3], 232 | label: '目录', 233 | }, 234 | }, 235 | }) 236 | -------------------------------------------------------------------------------- /docs/api/index.md: -------------------------------------------------------------------------------- 1 | # FileCodeBox API 文档 2 | 3 | ## API 版本: 2.1.0 4 | 5 | ## 目录 6 | - [认证](#认证) 7 | - [分享接口](#分享接口) 8 | - [管理接口](#管理接口) 9 | 10 | ## 认证 11 | 12 | 部分接口需要在请求头中携带 `Authorization` 进行认证: 13 | 14 | ``` 15 | Authorization: Bearer 16 | ``` 17 | 18 | ## 分享接口 19 | 20 | ### 分享文本 21 | 22 | **POST** `/share/text/` 23 | 24 | 分享文本内容,获取分享码。 25 | 26 | **请求参数:** 27 | 28 | | 参数名 | 类型 | 必填 | 默认值 | 描述 | 29 | |-------|------|------|--------|------| 30 | | text | string | 是 | - | 要分享的文本内容 | 31 | | expire_value | integer | 否 | 1 | 过期时间值 | 32 | | expire_style | string | 否 | "day" | 过期时间单位(day/hour/minute) | 33 | 34 | **响应示例:** 35 | 36 | ```json 37 | { 38 | "code": 200, 39 | "msg": "success", 40 | "detail": { 41 | "code": "abc123", 42 | "name": "text.txt" 43 | } 44 | } 45 | ``` 46 | 47 | ### 分享文件 48 | 49 | **POST** `/share/file/` 50 | 51 | 上传并分享文件,获取分享码。 52 | 53 | **请求参数:** 54 | 55 | | 参数名 | 类型 | 必填 | 默认值 | 描述 | 56 | |-------|------|------|--------|------| 57 | | file | file | 是 | - | 要上传的文件 | 58 | | expire_value | integer | 否 | 1 | 过期时间值 | 59 | | expire_style | string | 否 | "day" | 过期时间单位(day/hour/minute) | 60 | 61 | **响应示例:** 62 | 63 | ```json 64 | { 65 | "code": 200, 66 | "msg": "success", 67 | "detail": { 68 | "code": "abc123", 69 | "name": "example.txt" 70 | } 71 | } 72 | ``` 73 | 74 | ### 获取文件信息 75 | 76 | **GET** `/share/select/` 77 | 78 | 通过分享码获取文件信息。 79 | 80 | **请求参数:** 81 | 82 | | 参数名 | 类型 | 必填 | 描述 | 83 | |-------|------|------|------| 84 | | code | string | 是 | 文件分享码 | 85 | 86 | **响应示例:** 87 | 88 | ```json 89 | { 90 | "code": 200, 91 | "msg": "success", 92 | "detail": { 93 | "code": "abc123", 94 | "name": "example.txt", 95 | "size": 1024, 96 | "text": "文件内容或下载链接" 97 | } 98 | } 99 | ``` 100 | 101 | ### 选择文件 102 | 103 | **POST** `/share/select/` 104 | 105 | 通过分享码选择文件。 106 | 107 | **请求参数:** 108 | 109 | | 参数名 | 类型 | 必填 | 描述 | 110 | |-------|------|------|------| 111 | | code | string | 是 | 文件分享码 | 112 | 113 | **响应示例:** 114 | 115 | ```json 116 | { 117 | "code": 200, 118 | "msg": "success", 119 | "detail": { 120 | "code": "abc123", 121 | "name": "example.txt", 122 | "size": 1024, 123 | "text": "文件内容或下载链接" 124 | } 125 | } 126 | ``` 127 | 128 | ### 下载文件 129 | 130 | **GET** `/share/download` 131 | 132 | 下载分享的文件。 133 | 134 | **请求参数:** 135 | 136 | | 参数名 | 类型 | 必填 | 描述 | 137 | |-------|------|------|------| 138 | | key | string | 是 | 下载密钥 | 139 | | code | string | 是 | 文件分享码 | 140 | 141 | ## 管理接口 142 | 143 | ### 管理员登录 144 | 145 | **POST** `/admin/login` 146 | 147 | 管理员登录获取token。 148 | 149 | **请求参数:** 150 | 151 | | 参数名 | 类型 | 必填 | 描述 | 152 | |-------|------|------|------| 153 | | password | string | 是 | 管理员密码 | 154 | 155 | ### 仪表盘数据 156 | 157 | **GET** `/admin/dashboard` 158 | 159 | 获取系统仪表盘数据。 160 | 161 | **响应示例:** 162 | 163 | ```json 164 | { 165 | "code": 200, 166 | "msg": "success", 167 | "detail": { 168 | "totalFiles": 100, 169 | "storageUsed": "1.5GB", 170 | "sysUptime": "10天", 171 | "yesterdayCount": 50, 172 | "yesterdaySize": "500MB", 173 | "todayCount": 30, 174 | "todaySize": "300MB" 175 | } 176 | } 177 | ``` 178 | 179 | ### 文件列表 180 | 181 | **GET** `/admin/file/list` 182 | 183 | 获取系统中的文件列表。 184 | 185 | **请求参数:** 186 | 187 | | 参数名 | 类型 | 必填 | 默认值 | 描述 | 188 | |-------|------|------|--------|------| 189 | | page | integer | 否 | 1 | 当前页码 | 190 | | size | integer | 否 | 10 | 每页数量 | 191 | | keyword | string | 否 | "" | 搜索关键词 | 192 | 193 | **响应示例:** 194 | 195 | ```json 196 | { 197 | "code": 200, 198 | "msg": "success", 199 | "detail": { 200 | "page": 1, 201 | "size": 10, 202 | "total": 100, 203 | "data": [ 204 | { 205 | "id": 1, 206 | "name": "example.txt", 207 | "size": 1024, 208 | "created_at": "2024-01-01 12:00:00" 209 | } 210 | ] 211 | } 212 | } 213 | ``` 214 | 215 | ### 删除文件 216 | 217 | **DELETE** `/admin/file/delete` 218 | 219 | 删除系统中的文件。 220 | 221 | **请求参数:** 222 | 223 | | 参数名 | 类型 | 必填 | 描述 | 224 | |-------|------|------|------| 225 | | id | integer | 是 | 文件ID | 226 | 227 | ### 获取配置 228 | 229 | **GET** `/admin/config/get` 230 | 231 | 获取系统配置信息。 232 | 233 | ### 更新配置 234 | 235 | **PATCH** `/admin/config/update` 236 | 237 | 更新系统配置信息。 238 | 239 | ## 错误响应 240 | 241 | 当发生错误时,API会返回对应的错误信息: 242 | 243 | ```json 244 | { 245 | "code": 422, 246 | "detail": [ 247 | { 248 | "loc": ["body", "password"], 249 | "msg": "密码不能为空", 250 | "type": "value_error" 251 | } 252 | ] 253 | } 254 | ``` 255 | 256 | ## 状态码说明 257 | 258 | - 200: 请求成功 259 | - 401: 未授权 260 | - 403: 禁止访问 261 | - 404: 资源不存在 262 | - 422: 请求参数验证错误 263 | - 500: 服务器内部错误 -------------------------------------------------------------------------------- /docs/changelog.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/docs/changelog.md -------------------------------------------------------------------------------- /docs/contributing.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/docs/contributing.md -------------------------------------------------------------------------------- /docs/en/api/index.md: -------------------------------------------------------------------------------- 1 | # FileCodeBox API Documentation 2 | 3 | ## API Version: 2.1.0 4 | 5 | ## Table of Contents 6 | - [Authentication](#authentication) 7 | - [Share API](#share-api) 8 | - [Admin API](#admin-api) 9 | 10 | ## Authentication 11 | 12 | Some APIs require `Authorization` header for authentication: 13 | 14 | ``` 15 | Authorization: Bearer 16 | ``` 17 | 18 | ## Share API 19 | 20 | ### Share Text 21 | 22 | **POST** `/share/text/` 23 | 24 | Share text content and get a share code. 25 | 26 | **Parameters:** 27 | 28 | | Parameter | Type | Required | Default | Description | 29 | |-----------|------|----------|---------|-------------| 30 | | text | string | Yes | - | Text content to share | 31 | | expire_value | integer | No | 1 | Expiration time value | 32 | | expire_style | string | No | "day" | Expiration time unit(day/hour/minute) | 33 | 34 | **Response Example:** 35 | 36 | ```json 37 | { 38 | "code": 200, 39 | "msg": "success", 40 | "detail": { 41 | "code": "abc123", 42 | "name": "text.txt" 43 | } 44 | } 45 | ``` 46 | 47 | ### Share File 48 | 49 | **POST** `/share/file/` 50 | 51 | Upload and share a file, get a share code. 52 | 53 | **Parameters:** 54 | 55 | | Parameter | Type | Required | Default | Description | 56 | |-----------|------|----------|---------|-------------| 57 | | file | file | Yes | - | File to upload | 58 | | expire_value | integer | No | 1 | Expiration time value | 59 | | expire_style | string | No | "day" | Expiration time unit(day/hour/minute) | 60 | 61 | **Response Example:** 62 | 63 | ```json 64 | { 65 | "code": 200, 66 | "msg": "success", 67 | "detail": { 68 | "code": "abc123", 69 | "name": "example.txt" 70 | } 71 | } 72 | ``` 73 | 74 | ### Get File Info 75 | 76 | **GET** `/share/select/` 77 | 78 | Get file information by share code. 79 | 80 | **Parameters:** 81 | 82 | | Parameter | Type | Required | Description | 83 | |-----------|------|----------|-------------| 84 | | code | string | Yes | File share code | 85 | 86 | **Response Example:** 87 | 88 | ```json 89 | { 90 | "code": 200, 91 | "msg": "success", 92 | "detail": { 93 | "code": "abc123", 94 | "name": "example.txt", 95 | "size": 1024, 96 | "text": "File content or download link" 97 | } 98 | } 99 | ``` 100 | 101 | ### Select File 102 | 103 | **POST** `/share/select/` 104 | 105 | Select file by share code. 106 | 107 | **Parameters:** 108 | 109 | | Parameter | Type | Required | Description | 110 | |-----------|------|----------|-------------| 111 | | code | string | Yes | File share code | 112 | 113 | **Response Example:** 114 | 115 | ```json 116 | { 117 | "code": 200, 118 | "msg": "success", 119 | "detail": { 120 | "code": "abc123", 121 | "name": "example.txt", 122 | "size": 1024, 123 | "text": "File content or download link" 124 | } 125 | } 126 | ``` 127 | 128 | ### Download File 129 | 130 | **GET** `/share/download` 131 | 132 | Download shared file. 133 | 134 | **Parameters:** 135 | 136 | | Parameter | Type | Required | Description | 137 | |-----------|------|----------|-------------| 138 | | key | string | Yes | Download key | 139 | | code | string | Yes | File share code | 140 | 141 | ## Admin API 142 | 143 | ### Admin Login 144 | 145 | **POST** `/admin/login` 146 | 147 | Admin login to get token. 148 | 149 | **Parameters:** 150 | 151 | | Parameter | Type | Required | Description | 152 | |-----------|------|----------|-------------| 153 | | password | string | Yes | Admin password | 154 | 155 | ### Dashboard Data 156 | 157 | **GET** `/admin/dashboard` 158 | 159 | Get system dashboard data. 160 | 161 | **Response Example:** 162 | 163 | ```json 164 | { 165 | "code": 200, 166 | "msg": "success", 167 | "detail": { 168 | "totalFiles": 100, 169 | "storageUsed": "1.5GB", 170 | "sysUptime": "10 days", 171 | "yesterdayCount": 50, 172 | "yesterdaySize": "500MB", 173 | "todayCount": 30, 174 | "todaySize": "300MB" 175 | } 176 | } 177 | ``` 178 | 179 | ### File List 180 | 181 | **GET** `/admin/file/list` 182 | 183 | Get system file list. 184 | 185 | **Parameters:** 186 | 187 | | Parameter | Type | Required | Default | Description | 188 | |-----------|------|----------|---------|-------------| 189 | | page | integer | No | 1 | Current page | 190 | | size | integer | No | 10 | Page size | 191 | | keyword | string | No | "" | Search keyword | 192 | 193 | **Response Example:** 194 | 195 | ```json 196 | { 197 | "code": 200, 198 | "msg": "success", 199 | "detail": { 200 | "page": 1, 201 | "size": 10, 202 | "total": 100, 203 | "data": [ 204 | { 205 | "id": 1, 206 | "name": "example.txt", 207 | "size": 1024, 208 | "created_at": "2024-01-01 12:00:00" 209 | } 210 | ] 211 | } 212 | } 213 | ``` 214 | 215 | ### Delete File 216 | 217 | **DELETE** `/admin/file/delete` 218 | 219 | Delete file from system. 220 | 221 | **Parameters:** 222 | 223 | | Parameter | Type | Required | Description | 224 | |-----------|------|----------|-------------| 225 | | id | integer | Yes | File ID | 226 | 227 | ### Get Config 228 | 229 | **GET** `/admin/config/get` 230 | 231 | Get system configuration. 232 | 233 | ### Update Config 234 | 235 | **PATCH** `/admin/config/update` 236 | 237 | Update system configuration. 238 | 239 | ## Error Response 240 | 241 | When an error occurs, the API will return corresponding error message: 242 | 243 | ```json 244 | { 245 | "code": 422, 246 | "detail": [ 247 | { 248 | "loc": ["body", "password"], 249 | "msg": "Password cannot be empty", 250 | "type": "value_error" 251 | } 252 | ] 253 | } 254 | ``` 255 | 256 | ## Status Codes 257 | 258 | - 200: Success 259 | - 401: Unauthorized 260 | - 403: Forbidden 261 | - 404: Not Found 262 | - 422: Validation Error 263 | - 500: Internal Server Error -------------------------------------------------------------------------------- /docs/en/changelog.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/docs/en/changelog.md -------------------------------------------------------------------------------- /docs/en/contributing.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/docs/en/contributing.md -------------------------------------------------------------------------------- /docs/en/guide/configuration.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/docs/en/guide/configuration.md -------------------------------------------------------------------------------- /docs/en/guide/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ## Introduction 4 | 5 | FileCodeBox is a simple and efficient file sharing tool that supports temporary file transfer, sharing, and management. This guide will help you quickly deploy and use FileCodeBox. 6 | 7 | ## Features 8 | 9 | - 🚀 Quick Deployment: Support Docker one-click deployment 10 | - 🔒 Secure & Reliable: File access requires extraction code 11 | - ⏱️ Time Control: Support setting file expiration time 12 | - 📊 Download Limit: Can limit file download times 13 | - 🖼️ File Preview: Support preview of images, videos, audio, and other formats 14 | - 📱 Responsive Design: Perfect adaptation for mobile and desktop 15 | 16 | ## Deployment Methods 17 | 18 | ### Docker Deployment 19 | 20 | ```bash 21 | docker run -d --restart=always -p 12345:12345 -v /opt/FileCodeBox/:/app/data --name filecodebox lanol/filecodebox:beta 22 | ``` 23 | 24 | ### Manual Deployment 25 | 26 | 1. Clone the repository 27 | ```bash 28 | git clone https://github.com/vastsa/FileCodeBox.git 29 | ``` 30 | 31 | 2. Install dependencies 32 | ```bash 33 | cd FileCodeBox 34 | pip install -r requirements.txt 35 | ``` 36 | 37 | 3. Start the service 38 | ```bash 39 | python main.py 40 | ``` 41 | 42 | ## Usage 43 | 44 | 1. Access the System 45 | Open browser and visit `http://localhost:12345` 46 | 47 | 2. Upload Files 48 | - Click upload button or drag files to upload area 49 | - Set file expiration time and download limit 50 | - Get share link and extraction code 51 | 52 | 3. Download Files 53 | - Visit share link 54 | - Enter extraction code 55 | - Download file 56 | 57 | 4. Admin Panel 58 | - Visit `http://localhost:12345/#/admin` 59 | - Enter admin password: `FileCodeBox2023` 60 | - Enter admin panel 61 | - View system information, file list, user management, etc. 62 | 63 | ## Next Steps 64 | 65 | - [Storage Configuration](/en/guide/storage) - Learn how to configure different storage methods 66 | - [Security Settings](/en/guide/security) - Learn how to enhance system security 67 | - [API Documentation](/en/api/) - Learn how to integrate through API -------------------------------------------------------------------------------- /docs/en/guide/introduction.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | FileCodeBox Logo 4 | 5 |

Share text and files anonymously with a passcode, like picking up a package

6 | 7 | [![GitHub stars](https://img.shields.io/github/stars/vastsa/FileCodeBox)](https://github.com/vastsa/FileCodeBox/stargazers) 8 | [![GitHub forks](https://img.shields.io/github/forks/vastsa/FileCodeBox)](https://github.com/vastsa/FileCodeBox/network) 9 | [![GitHub issues](https://img.shields.io/github/issues/vastsa/FileCodeBox)](https://github.com/vastsa/FileCodeBox/issues) 10 | [![GitHub license](https://img.shields.io/github/license/vastsa/FileCodeBox)](https://github.com/vastsa/FileCodeBox/blob/master/LICENSE) 11 | [![QQ Group](https://img.shields.io/badge/QQ%20Group-739673698-blue.svg)](https://qm.qq.com/q/PemPzhdEIM) 12 | [![Python Version](https://img.shields.io/badge/Python-3.8+-blue.svg)](https://www.python.org) 13 | [![FastAPI](https://img.shields.io/badge/FastAPI-0.68+-green.svg)](https://fastapi.tiangolo.com) 14 | [![Vue Version](https://img.shields.io/badge/Vue.js-3.x-brightgreen.svg)](https://v3.vuejs.org) 15 | 16 |
17 | 18 | ## 📝 Introduction 19 | 20 | FileCodeBox is a lightweight file sharing tool developed with FastAPI + Vue3. It allows users to share text and files easily, where recipients only need a passcode to retrieve the files, just like picking up a package from a delivery locker. 21 | 22 | ## 🖼️ Preview 23 | 24 |
25 |

26 | 27 | Frontend Repository 28 | 29 |     30 | 31 | Demo Site 32 | 33 |

34 |
35 | 36 | 37 | ## 🎯 Use Cases 38 | 39 | 40 | 41 | 45 | 49 | 53 | 54 | 55 | 59 | 63 | 67 | 68 |
42 |

📁 Temporary File Sharing

43 | Quick file sharing without registration 44 |
46 |

📝 Quick Text Sharing

47 | Share code snippets and text content 48 |
50 |

🕶️ Anonymous Transfer

51 | Privacy-protected file transfer 52 |
56 |

💾 Temporary Storage

57 | File storage with expiration time 58 |
60 |

🔄 Cross-platform Transfer

61 | Quick file transfer between devices 62 |
64 |

🌐 Private Share Service

65 | Build your own file sharing service 66 |
69 | 70 | ## ✨ Core Features 71 | 72 | 73 | 74 | 78 | 82 | 86 | 87 | 88 | 95 | 99 | 103 | 104 | 105 | 109 | 113 | 117 | 118 | 119 | 123 | 127 | 131 | 132 |
75 |

🚀 Lightweight

76 | Based on FastAPI + SQLite3 + Vue3 + ElementUI 77 |
79 |

📤 Easy Upload

80 | Support copy-paste and drag-drop 81 |
83 |

📦 Multiple Types

84 | Support text and various file types 85 |
89 |

🔒 Security

90 | 91 | - IP upload limits 92 | - Error attempt limits 93 | - File expiration 94 |
96 |

🎫 Passcode Sharing

97 | Random codes with customizable limits 98 |
100 |

🌍 Multi-language

101 | Support for Simplified Chinese, Traditional Chinese, and English 102 |
106 |

🎭 Anonymous

107 | No registration required 108 |
110 |

🛠 Admin Panel

111 | File and system management 112 |
114 |

🐳 Docker

115 | One-click deployment 116 |
120 |

💾 Storage Options

121 | Local, S3, OneDrive support 122 |
124 |

📱 Responsive

125 | Mobile-friendly design 126 |
128 |

💻 CLI Support

129 | Command-line download 130 |
133 | 134 | ## 🚀 Quick Start 135 | 136 | ### Docker Deployment 137 | 138 | ```bash 139 | docker run -d --restart=always -p 12345:12345 -v /opt/FileCodeBox/:/app/data --name filecodebox lanol/filecodebox:beta 140 | ``` 141 | 142 | ### Manual Deployment 143 | 144 | 1. Clone the repository 145 | ```bash 146 | git clone https://github.com/vastsa/FileCodeBox.git 147 | ``` 148 | 149 | 2. Install dependencies 150 | ```bash 151 | cd FileCodeBox 152 | pip install -r requirements.txt 153 | ``` 154 | 155 | 3. Start the service 156 | ```bash 157 | python main.py 158 | ``` 159 | 160 | ## 📖 Usage Guide 161 | 162 | ### Share Files 163 | 1. Open the website, click "Share File" 164 | 2. Select or drag files 165 | 3. Set expiration time and count 166 | 4. Get the passcode 167 | 168 | ### Retrieve Files 169 | 1. Open the website, enter passcode 170 | 2. Click retrieve 171 | 3. Download file or view text 172 | 173 | ### Admin Panel 174 | 1. Visit `/admin` 175 | 2. Enter admin password 176 | 3. Manage files and settings 177 | 178 | ## 🛠 Development Guide 179 | 180 | ### Project Structure 181 | ``` 182 | FileCodeBox/ 183 | ├── apps/ # Application code 184 | │ ├── admin/ # Admin backend 185 | │ └── base/ # Base functions 186 | ├── core/ # Core functions 187 | ├── data/ # Data directory 188 | └── fcb-fronted/ # Frontend code 189 | ``` 190 | 191 | ### Development Environment 192 | - Python 3.8+ 193 | - Node.js 14+ 194 | - Vue 3 195 | - FastAPI 196 | 197 | ### Local Development 198 | 1. Backend development 199 | ```bash 200 | python main.py 201 | ``` 202 | 203 | 2. Frontend development 204 | ```bash 205 | cd fcb-fronted 206 | npm install 207 | npm run dev 208 | ``` 209 | 210 | ## 🤝 Contributing 211 | 212 | 1. Fork the project 213 | 2. Create your feature branch `git checkout -b feature/xxx` 214 | 3. Commit your changes `git commit -m 'Add xxx'` 215 | 4. Push to the branch `git push origin feature/xxx` 216 | 5. Open a Pull Request 217 | 218 | ## ❓ FAQ 219 | 220 | ### Q: How to modify upload size limit? 221 | A: Change `uploadSize` in admin panel 222 | 223 | ### Q: How to configure storage engine? 224 | A: Select storage engine and configure parameters in admin panel 225 | 226 | ### Q: How to backup data? 227 | A: Backup the `data` directory 228 | 229 | For more questions, visit [Wiki](https://github.com/vastsa/FileCodeBox/wiki/常见问题) 230 | 231 | ## 😀 Project Statistics and Analytics 232 | 233 |
234 | Featured|HelloGitHub 235 | 236 | ![Repobeats](https://repobeats.axiom.co/api/embed/7a6c92f1d96ee57e6fb67f0df371528397b0c9ac.svg) 237 | 238 | [![Star History](https://api.star-history.com/svg?repos=vastsa/FileCodeBox&type=Date)](https://star-history.com/#vastsa/FileCodeBox&Date) 239 |
240 | 241 | ## 📜 Disclaimer 242 | 243 | This project is open-source for learning purposes only. It should not be used for any illegal purposes. The author is not responsible for any consequences. Please retain the project address and copyright information when using it. -------------------------------------------------------------------------------- /docs/en/guide/management.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/docs/en/guide/management.md -------------------------------------------------------------------------------- /docs/en/guide/security.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/docs/en/guide/security.md -------------------------------------------------------------------------------- /docs/en/guide/share.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/docs/en/guide/share.md -------------------------------------------------------------------------------- /docs/en/guide/storage.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/docs/en/guide/storage.md -------------------------------------------------------------------------------- /docs/en/guide/upload copy.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/docs/en/guide/upload copy.md -------------------------------------------------------------------------------- /docs/en/guide/upload.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/docs/en/guide/upload.md -------------------------------------------------------------------------------- /docs/en/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | 4 | hero: 5 | name: "FileCodeBox" 6 | text: "File Express Box" 7 | tagline: Share text and files anonymously with access codes, just like picking up a package 8 | image: 9 | src: /logo_small.png 10 | alt: FileCodeBox 11 | actions: 12 | - theme: brand 13 | text: Get Started 14 | link: /en/guide/getting-started 15 | - theme: alt 16 | text: Live Demo 17 | link: https://share.lanol.cn 18 | - theme: alt 19 | text: View on GitHub 20 | link: https://github.com/vastsa/FileCodeBox 21 | 22 | features: 23 | - icon: 🚀 24 | title: Quick Deployment 25 | details: Supports one-click Docker deployment, simple and fast, no complex configuration needed 26 | - icon: 🔒 27 | title: Secure & Reliable 28 | details: File access requires an access code, supports expiration time and download limit settings 29 | - icon: 💻 30 | title: Clean Interface 31 | details: Clean user interface with drag-and-drop upload support for excellent user experience 32 | - icon: 🛠️ 33 | title: Feature Rich 34 | details: Supports file preview, online playback, image processing, and many other features 35 | - icon: 📦 36 | title: Storage Extensions 37 | details: Supports various storage methods including local storage and object storage 38 | - icon: 🔌 39 | title: API Support 40 | details: Provides complete REST API for easy integration with other systems 41 | --- -------------------------------------------------------------------------------- /docs/guide/configuration.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/docs/guide/configuration.md -------------------------------------------------------------------------------- /docs/guide/getting-started.md: -------------------------------------------------------------------------------- 1 | # 快速开始 2 | 3 | ## 简介 4 | 5 | FileCodeBox 是一个简单高效的文件分享工具,支持文件临时中转、分享和管理。本指南将帮助您快速部署和使用 FileCodeBox。 6 | 7 | ## 特性 8 | 9 | - 🚀 快速部署:支持 Docker 一键部署 10 | - 🔒 安全可靠:文件访问需要提取码 11 | - ⏱️ 时效控制:支持设置文件有效期 12 | - 📊 下载限制:可限制文件下载次数 13 | - 🖼️ 文件预览:支持图片、视频、音频等多种格式预览 14 | - 📱 响应式设计:完美适配移动端和桌面端 15 | 16 | ## 部署方式 17 | 18 | ### Docker 部署(推荐) 19 | 20 | ```bash 21 | docker run -d --restart=always -p 12345:12345 -v /opt/FileCodeBox/:/app/data --name filecodebox lanol/filecodebox:beta 22 | ``` 23 | 24 | ### 手动部署 25 | 26 | 1. 克隆项目 27 | ```bash 28 | git clone https://github.com/vastsa/FileCodeBox.git 29 | ``` 30 | 31 | 2. 安装依赖 32 | ```bash 33 | cd FileCodeBox 34 | pip install -r requirements.txt 35 | ``` 36 | 37 | 3. 启动服务 38 | ```bash 39 | python main.py 40 | ``` 41 | 42 | 43 | ## 使用方法 44 | 45 | 1. 访问系统 46 | 打开浏览器访问 `http://localhost:12345` 47 | 48 | 2. 上传文件 49 | - 点击上传按钮或拖拽文件到上传区域 50 | - 设置文件有效期和下载次数限制 51 | - 获取分享链接和提取码 52 | 53 | 3. 下载文件 54 | - 访问分享链接 55 | - 输入提取码 56 | - 下载文件 57 | 58 | 4. 后台管理 59 | - 访问 `http://localhost:12345/#/admin` 60 | - 输入管理员密码:`FileCodeBox2023` 61 | - 进入后台管理页面 62 | - 查看系统信息、文件列表、用户管理等 63 | 64 | ## 下一步 65 | 66 | - [存储配置](/guide/storage) - 了解如何配置不同的存储方式 67 | - [安全设置](/guide/security) - 了解如何增强系统安全性 68 | - [API 文档](/api/) - 了解如何通过 API 集成 -------------------------------------------------------------------------------- /docs/guide/introduction.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | FileCodeBox Logo 4 | 5 |

匿名口令分享文本和文件,像拿快递一样取文件

6 | 7 | [![GitHub stars](https://img.shields.io/github/stars/vastsa/FileCodeBox)](https://github.com/vastsa/FileCodeBox/stargazers) 8 | [![GitHub forks](https://img.shields.io/github/forks/vastsa/FileCodeBox)](https://github.com/vastsa/FileCodeBox/network) 9 | [![GitHub issues](https://img.shields.io/github/issues/vastsa/FileCodeBox)](https://github.com/vastsa/FileCodeBox/issues) 10 | [![GitHub license](https://img.shields.io/github/license/vastsa/FileCodeBox)](https://github.com/vastsa/FileCodeBox/blob/master/LICENSE) 11 | [![QQ Group](https://img.shields.io/badge/QQ%20Group-739673698-blue.svg)](https://qm.qq.com/q/PemPzhdEIM) 12 | [![Python Version](https://img.shields.io/badge/Python-3.8+-blue.svg)](https://www.python.org) 13 | [![FastAPI](https://img.shields.io/badge/FastAPI-0.68+-green.svg)](https://fastapi.tiangolo.com) 14 | [![Vue Version](https://img.shields.io/badge/Vue.js-3.x-brightgreen.svg)](https://v3.vuejs.org) 15 |
16 | 17 | 18 | ## 🚀 更新计划 19 | - [ ] 切片上传,同文件秒传,断点续传 20 | - [ ] 用户登录重构 21 | - [x] webdav存储 22 | - [x] 存储支持自定义路径 23 | - [x] s3优化,不修改昵称为uuid,新建目录 24 | 25 | ## 📝 项目简介 26 | 27 | FileCodeBox 是一个基于 FastAPI + Vue3 开发的轻量级文件分享工具。它允许用户通过简单的方式分享文本和文件,接收者只需要一个提取码就可以取得文件,就像从快递柜取出快递一样简单。 28 | 29 | ## 🎯 应用场景 30 | 31 | 32 | 33 | 37 | 41 | 45 | 46 | 47 | 51 | 55 | 59 | 60 |
34 |

📁 临时文件分享

35 | 快速分享单个文件,无需注册登录 36 |
38 |

📝 文本快速分享

39 | 分享代码片段、文本内容等 40 |
42 |

🕶️ 匿名文件传输

43 | 保护隐私的文件传输方式 44 |
48 |

💾 临时文件存储

49 | 支持设置过期时间的文件存储 50 |
52 |

🔄 跨平台传输

53 | 在不同设备间快速传输文件 54 |
56 |

🌐 小型分享服务

57 | 搭建私有的文件分享服务 58 |
61 | 62 | ## ✨ 核心特性 63 | 64 | 65 | 66 | 70 | 74 | 78 | 79 | 80 | 87 | 91 | 95 | 96 | 97 | 101 | 105 | 109 | 110 | 111 | 115 | 119 | 123 | 124 |
67 |

🚀 轻量简洁

68 | 基于 FastAPI + SQLite3 + Vue3 + ElementUI,部署简单,性能出色 69 |
71 |

📤 便捷上传

72 | 支持复制粘贴、拖拽上传,操作简单直观 73 |
75 |

📦 多种类型

76 | 支持文本和各类文件的分享 77 |
81 |

🔒 安全机制

82 | 83 | - IP 限制上传次数 84 | - 错误次数限制 85 | - 文件过期机制 86 |
88 |

🎫 提取码分享

89 | 随机提取码,可自定义次数及有效期 90 |
92 |

🌍 多语言支持

93 | 支持中文简体、繁体及英文 94 |
98 |

🎭 匿名分享

99 | 无需注册登录,保护隐私 100 |
102 |

🛠 管理面板

103 | 文件管理和系统配置 104 |
106 |

🐳 容器部署

107 | 支持 Docker 一键部署 108 |
112 |

💾 存储扩展

113 | 支持本地存储、S3 协议、OneDrive 等 114 |
116 |

📱 响应式设计

117 | 支持移动端访问 118 |
120 |

💻 终端支持

121 | 支持命令行下载 122 |
125 | 126 | ## 🚀 快速开始 127 | 128 | ### Docker 部署 129 | 130 | ```bash 131 | docker run -d --restart=always -p 12345:12345 -v /opt/FileCodeBox/:/app/data --name filecodebox lanol/filecodebox:beta 132 | ``` 133 | 134 | ### 手动部署 135 | 136 | 1. 克隆项目 137 | ```bash 138 | git clone https://github.com/vastsa/FileCodeBox.git 139 | ``` 140 | 141 | 2. 安装依赖 142 | ```bash 143 | cd FileCodeBox 144 | pip install -r requirements.txt 145 | ``` 146 | 147 | 3. 启动服务 148 | ```bash 149 | python main.py 150 | ``` 151 | 152 | ## 📖 使用说明 153 | 154 | ### 分享文件 155 | 1. 打开网页,点击"分享文件" 156 | 2. 选择或拖拽文件 157 | 3. 设置过期时间和次数 158 | 4. 获取提取码 159 | 160 | ### 获取文件 161 | 1. 打开网页,输入提取码 162 | 2. 点击获取 163 | 3. 下载文件或查看文本 164 | 165 | ### 管理面板 166 | 1. 访问 `/admin` 167 | 2. 输入管理员密码 168 | 3. 管理文件和配置 169 | 170 | ## 🛠 开发指南 171 | 172 | ### 项目结构 173 | ``` 174 | FileCodeBox/ 175 | ├── apps/ # 应用代码 176 | │ ├── admin/ # 管理后台 177 | │ └── base/ # 基础功能 178 | ├── core/ # 核心功能 179 | ├── data/ # 数据目录 180 | └── fcb-fronted/ # 前端代码 181 | ``` 182 | 183 | ### 开发环境 184 | - Python 3.8+ 185 | - Node.js 14+ 186 | - Vue 3 187 | - FastAPI 188 | 189 | ### 本地开发 190 | 1. 后端开发 191 | ```bash 192 | python main.py 193 | ``` 194 | 195 | 2. 前端开发 196 | ```bash 197 | cd fcb-fronted 198 | npm install 199 | npm run dev 200 | ``` 201 | 202 | ## 🤝 贡献指南 203 | 204 | 1. Fork 本项目 205 | 2. 创建新分支 `git checkout -b feature/xxx` 206 | 3. 提交更改 `git commit -m 'Add xxx'` 207 | 4. 推送到分支 `git push origin feature/xxx` 208 | 5. 提交 Pull Request 209 | 210 | ## ❓ 常见问题 211 | 212 | ### Q: 如何修改上传大小限制? 213 | A: 在管理面板中修改配置项 `uploadSize` 214 | 215 | ### Q: 如何配置存储引擎? 216 | A: 在管理面板中选择存储引擎并配置相应参数 217 | 218 | ### Q: 如何备份数据? 219 | A: 备份 `data` 目录即可 220 | 221 | 更多问题请访问 [Wiki](https://github.com/vastsa/FileCodeBox/wiki/常见问题) 222 | 223 | ## 📊 项目统计 224 | 225 |
226 | Featured|HelloGitHub 227 | 228 | ![Repobeats](https://repobeats.axiom.co/api/embed/7a6c92f1d96ee57e6fb67f0df371528397b0c9ac.svg) 229 | 230 | [![Star History](https://api.star-history.com/svg?repos=vastsa/FileCodeBox&type=Date)](https://star-history.com/#vastsa/FileCodeBox&Date) 231 |
232 | 233 | ## 📜 免责声明 234 | 235 | 本项目开源仅供学习使用,不得用于任何违法用途,否则后果自负,与作者无关。使用时请保留项目地址和版权信息。 236 | -------------------------------------------------------------------------------- /docs/guide/management.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/docs/guide/management.md -------------------------------------------------------------------------------- /docs/guide/security.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/docs/guide/security.md -------------------------------------------------------------------------------- /docs/guide/share.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/docs/guide/share.md -------------------------------------------------------------------------------- /docs/guide/storage.md: -------------------------------------------------------------------------------- 1 | # 阿里云设置 2 | S3 AccessKeyId: `AccessKeyId` 3 | 4 | S3 SecretAccessKey: `SecretAccessKey` 5 | 6 | S3 BucketName: `bucket-name` 7 | 8 | S3 EndpointUrl: `https://..aliyuncs.com` 9 | 10 | S3 Signature Version: `s3v4` 11 | 12 | S3 Region Name:`region` 13 | 14 | # Minio设置 15 | 16 | S3 AccessKeyId: `AccessKeyId` 17 | 18 | S3 SecretAccessKey: `SecretAccessKey` 19 | 20 | S3 BucketName: `bucket-name` 21 | 22 | S3 EndpointUrl: api接口地址 23 | 24 | S3 Signature Version: `s3v4` 25 | 26 | S3 Region Name:根据`configurations`里面设置的 `Server Location` -------------------------------------------------------------------------------- /docs/guide/upload.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/docs/guide/upload.md -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | 4 | hero: 5 | name: "FileCodeBox" 6 | text: "文件快递柜" 7 | tagline: 匿名口令分享文本,文件,像拿快递一样取文件 8 | image: 9 | src: /logo_small.png 10 | alt: FileCodeBox 11 | actions: 12 | - theme: brand 13 | text: 快速开始 14 | link: /guide/getting-started 15 | - theme: alt 16 | text: 在线体验 17 | link: https://share.lanol.cn 18 | - theme: alt 19 | text: 在 GitHub 上查看 20 | link: https://github.com/vastsa/FileCodeBox 21 | 22 | features: 23 | - icon: 🚀 24 | title: 快速部署 25 | details: 支持 Docker 一键部署,简单快捷,无需复杂配置 26 | - icon: 🔒 27 | title: 安全可靠 28 | details: 文件访问需要提取码,支持设置有效期和下载次数限制 29 | - icon: 💻 30 | title: 简洁界面 31 | details: 清爽的用户界面,支持拖拽上传,使用体验极佳 32 | - icon: 🛠️ 33 | title: 功能丰富 34 | details: 支持文件预览、在线播放、图片处理等多种功能 35 | - icon: 📦 36 | title: 存储扩展 37 | details: 支持本地存储、对象存储等多种存储方式 38 | - icon: 🔌 39 | title: API 支持 40 | details: 提供完整的 REST API,方便与其他系统集成 41 | --- 42 | 43 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "vitepress": "^1.6.3" 4 | }, 5 | "scripts": { 6 | "docs:dev": "vitepress dev", 7 | "docs:build": "vitepress build", 8 | "docs:preview": "vitepress preview" 9 | } 10 | } -------------------------------------------------------------------------------- /docs/public/logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/docs/public/logo_small.png -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # @Time : 2023/8/9 23:23 2 | # @Author : Lan 3 | # @File : main.py 4 | # @Software: PyCharm 5 | import asyncio 6 | import time 7 | 8 | from fastapi import FastAPI 9 | 10 | from fastapi.middleware.cors import CORSMiddleware 11 | from fastapi.responses import HTMLResponse 12 | from fastapi.staticfiles import StaticFiles 13 | from tortoise.contrib.fastapi import register_tortoise 14 | 15 | from apps.base.models import KeyValue 16 | from apps.base.utils import ip_limit 17 | from apps.base.views import share_api, chunk_api 18 | from apps.admin.views import admin_api 19 | from core.database import init_db 20 | from core.response import APIResponse 21 | from core.settings import data_root, settings, BASE_DIR, DEFAULT_CONFIG 22 | from core.tasks import delete_expire_files 23 | from core.logger import logger 24 | 25 | from contextlib import asynccontextmanager 26 | from tortoise import Tortoise 27 | 28 | 29 | @asynccontextmanager 30 | async def lifespan(app: FastAPI): 31 | logger.info("正在初始化应用...") 32 | # 初始化数据库 33 | await init_db() 34 | 35 | # 加载配置 36 | await load_config() 37 | app.mount( 38 | "/assets", 39 | StaticFiles(directory=f"./{settings.themesSelect}/assets"), 40 | name="assets", 41 | ) 42 | 43 | # 启动后台任务 44 | task = asyncio.create_task(delete_expire_files()) 45 | logger.info("应用初始化完成") 46 | 47 | try: 48 | yield 49 | finally: 50 | # 清理操作 51 | logger.info("正在关闭应用...") 52 | task.cancel() 53 | await asyncio.gather(task, return_exceptions=True) 54 | await Tortoise.close_connections() 55 | logger.info("应用已关闭") 56 | 57 | 58 | async def load_config(): 59 | user_config, _ = await KeyValue.get_or_create( 60 | key="settings", defaults={"value": DEFAULT_CONFIG} 61 | ) 62 | await KeyValue.update_or_create( 63 | key="sys_start", defaults={"value": int(time.time() * 1000)} 64 | ) 65 | settings.user_config = user_config.value 66 | # 更新 ip_limit 配置 67 | ip_limit["error"].minutes = settings.errorMinute 68 | ip_limit["error"].count = settings.errorCount 69 | ip_limit["upload"].minutes = settings.uploadMinute 70 | ip_limit["upload"].count = settings.uploadCount 71 | 72 | 73 | app = FastAPI(lifespan=lifespan) 74 | 75 | app.add_middleware( 76 | CORSMiddleware, 77 | allow_origins=["*"], 78 | allow_credentials=True, 79 | allow_methods=["*"], 80 | allow_headers=["*"], 81 | ) 82 | 83 | # 使用 register_tortoise 来添加异常处理器 84 | register_tortoise( 85 | app, 86 | config={ 87 | "connections": {"default": f"sqlite://{data_root}/filecodebox.db"}, 88 | "apps": { 89 | "models": { 90 | "models": ["apps.base.models"], 91 | "default_connection": "default", 92 | }, 93 | }, 94 | }, 95 | generate_schemas=False, 96 | add_exception_handlers=True, 97 | ) 98 | 99 | app.include_router(share_api) 100 | app.include_router(chunk_api) 101 | app.include_router(admin_api) 102 | 103 | 104 | @app.exception_handler(404) 105 | @app.get("/") 106 | async def index(request=None, exc=None): 107 | return HTMLResponse( 108 | content=open( 109 | BASE_DIR / f"{settings.themesSelect}/index.html", "r", encoding="utf-8" 110 | ) 111 | .read() 112 | .replace("{{title}}", str(settings.name)) 113 | .replace("{{description}}", str(settings.description)) 114 | .replace("{{keywords}}", str(settings.keywords)) 115 | .replace("{{opacity}}", str(settings.opacity)) 116 | .replace('"/assets/', '"assets/') 117 | .replace("{{background}}", str(settings.background)), 118 | media_type="text/html", 119 | headers={"Cache-Control": "no-cache"}, 120 | ) 121 | 122 | 123 | @app.get("/robots.txt") 124 | async def robots(): 125 | return HTMLResponse(content=settings.robotsText, media_type="text/plain") 126 | 127 | 128 | @app.post("/") 129 | async def get_config(): 130 | return APIResponse( 131 | detail={ 132 | "name": settings.name, 133 | "description": settings.description, 134 | "explain": settings.page_explain, 135 | "uploadSize": settings.uploadSize, 136 | "expireStyle": settings.expireStyle, 137 | "enableChunk": settings.enableChunk if settings.file_storage == "local" and settings.enableChunk else 0, 138 | "openUpload": settings.openUpload, 139 | "notify_title": settings.notify_title, 140 | "notify_content": settings.notify_content, 141 | "show_admin_address": settings.showAdminAddr, 142 | "max_save_seconds": settings.max_save_seconds, 143 | } 144 | ) 145 | 146 | 147 | if __name__ == "__main__": 148 | import uvicorn 149 | 150 | uvicorn.run( 151 | app="main:app", host="0.0.0.0", port=settings.port, reload=False, workers=1 152 | ) 153 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # FileCodeBox - 文件快递柜 2 | 3 |
4 | 5 | FileCodeBox Logo 6 | 7 |

匿名口令分享文本和文件,像拿快递一样取文件

8 | 9 | [![GitHub stars](https://img.shields.io/github/stars/vastsa/FileCodeBox)](https://github.com/vastsa/FileCodeBox/stargazers) 10 | [![GitHub forks](https://img.shields.io/github/forks/vastsa/FileCodeBox)](https://github.com/vastsa/FileCodeBox/network) 11 | [![GitHub issues](https://img.shields.io/github/issues/vastsa/FileCodeBox)](https://github.com/vastsa/FileCodeBox/issues) 12 | [![GitHub license](https://img.shields.io/github/license/vastsa/FileCodeBox)](https://github.com/vastsa/FileCodeBox/blob/master/LICENSE) 13 | [![QQ Group](https://img.shields.io/badge/QQ%20Group-739673698-blue.svg)](https://qm.qq.com/q/PemPzhdEIM) 14 | [![Python Version](https://img.shields.io/badge/Python-3.8+-blue.svg)](https://www.python.org) 15 | [![FastAPI](https://img.shields.io/badge/FastAPI-0.68+-green.svg)](https://fastapi.tiangolo.com) 16 | [![Vue Version](https://img.shields.io/badge/Vue.js-3.x-brightgreen.svg)](https://v3.vuejs.org) 17 | 18 | [English](./readme_en.md) | [部署教程](https://github.com/vastsa/FileCodeBox/wiki/部署教程) | [常见问题](https://github.com/vastsa/FileCodeBox/wiki/常见问题) 19 | 20 |
21 | 22 | ## 🚀 更新计划 23 | 24 | - [ ] 切片上传,同文件秒传,断点续传 25 | - [ ] 文件收集功能 26 | 27 | ## 📝 项目简介 28 | 29 | FileCodeBox 是一个基于 FastAPI + Vue3 开发的轻量级文件分享工具。它允许用户通过简单的方式分享文本和文件,接收者只需要一个提取码就可以取得文件,就像从快递柜取出快递一样简单。 30 | 31 | ## 🖼️ 功能预览 32 | 33 | 别问前端源码怎么是js了,麻烦仔细看下面的内容 34 |
35 |

36 | 37 | 前端仓库2024 38 | 39 | 40 | 前端仓库2023 41 | 42 |      43 | 44 | 演示站点 45 | 46 |

47 |
48 | 49 | ### 新版界面 50 | 51 |
52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 |
文件上传文本分享
文件管理系统设置
移动端深色模式
国际化响应式
70 |
71 | 72 | ### 经典界面 73 | 74 |
75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 |
首页上传
管理设置
分享下载
89 |
90 | 91 | ## 🎯 应用场景 92 | 93 | 94 | 95 | 99 | 103 | 107 | 108 | 109 | 113 | 117 | 121 | 122 |
96 |

📁 临时文件分享

97 | 快速分享单个文件,无需注册登录 98 |
100 |

📝 文本快速分享

101 | 分享代码片段、文本内容等 102 |
104 |

🕶️ 匿名文件传输

105 | 保护隐私的文件传输方式 106 |
110 |

💾 临时文件存储

111 | 支持设置过期时间的文件存储 112 |
114 |

🔄 跨平台传输

115 | 在不同设备间快速传输文件 116 |
118 |

🌐 小型分享服务

119 | 搭建私有的文件分享服务 120 |
123 | 124 | ## ✨ 核心特性 125 | 126 | 127 | 128 | 132 | 136 | 140 | 141 | 142 | 150 | 154 | 158 | 159 | 160 | 164 | 168 | 172 | 173 | 174 | 178 | 182 | 186 | 187 |
129 |

🚀 轻量简洁

130 | 基于 FastAPI + SQLite3 + Vue3 + ElementUI,部署简单,性能出色 131 |
133 |

📤 便捷上传

134 | 支持复制粘贴、拖拽上传,操作简单直观 135 |
137 |

📦 多种类型

138 | 支持文本和各类文件的分享 139 |
143 |

🔒 安全机制

144 | 145 | - IP 限制上传次数 146 | - 错误次数限制 147 | - 文件过期机制 148 | 149 |
151 |

🎫 提取码分享

152 | 随机提取码,可自定义次数及有效期 153 |
155 |

🌍 多语言支持

156 | 支持中文简体、繁体及英文 157 |
161 |

🎭 匿名分享

162 | 无需注册登录,保护隐私 163 |
165 |

🛠 管理面板

166 | 文件管理和系统配置 167 |
169 |

🐳 容器部署

170 | 支持 Docker 一键部署 171 |
175 |

💾 存储扩展

176 | 支持本地存储、S3 协议、OneDrive 等 177 |
179 |

📱 响应式设计

180 | 支持移动端访问 181 |
183 |

💻 终端支持

184 | 支持命令行下载 185 |
188 | 189 | ## 🚀 快速开始 190 | 191 | ### Docker 部署 192 | 193 | #### Docker CLI 194 | 195 | ```bash 196 | docker run -d --restart=always -p 12345:12345 -v /opt/FileCodeBox/:/app/data --name filecodebox lanol/filecodebox:beta 197 | ``` 198 | 199 | #### Docker Compose 200 | 201 | ```yml 202 | version: "3" 203 | services: 204 | file-code-box: 205 | image: lanol/filecodebox:latest 206 | volumes: 207 | - fcb-data:/app/data:rw 208 | restart: unless-stopped 209 | ports: 210 | - "12345:12345" 211 | volumes: 212 | fcb-data: 213 | external: false 214 | ``` 215 | 216 | ### 配置反向代理(Nginx示例) 217 | 218 | 请注意,必须添加以下配置来确保正确处理客户端IP和代理请求: 219 | 220 | ```nginx 221 | location / { 222 | proxy_set_header X-Real-IP $remote_addr; # 设置真实客户端IP 223 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 224 | proxy_pass http://localhost:12345; 225 | } 226 | ``` 227 | 228 | ### 手动部署 229 | 230 | 1. 克隆项目 231 | 232 | ```bash 233 | git clone https://github.com/vastsa/FileCodeBox.git 234 | ``` 235 | 236 | 2. 安装依赖 237 | 238 | ```bash 239 | cd FileCodeBox 240 | pip install -r requirements.txt 241 | ``` 242 | 243 | 3. 启动服务 244 | 245 | ```bash 246 | python main.py 247 | ``` 248 | 249 | ## 📖 使用说明 250 | 251 | ### 分享文件 252 | 253 | 1. 打开网页,点击"分享文件" 254 | 2. 选择或拖拽文件 255 | 3. 设置过期时间和次数 256 | 4. 获取提取码 257 | 258 | ### 获取文件 259 | 260 | 1. 打开网页,输入提取码 261 | 2. 点击获取 262 | 3. 下载文件或查看文本 263 | 264 | ### 管理面板 265 | 266 | 1. 访问 `/#/admin` 267 | 2. 输入管理员密码 `FileCodeBox2023` 268 | 3. 管理文件和配置 269 | 270 | ## 🛠 开发指南 271 | 272 | ### 项目结构 273 | 274 | ``` 275 | FileCodeBox/ 276 | ├── apps/ # 应用代码 277 | │ ├── admin/ # 管理后台 278 | │ └── base/ # 基础功能 279 | ├── core/ # 核心功能 280 | ├── data/ # 数据目录 281 | └── fcb-fronted/ # 前端代码 282 | ``` 283 | 284 | ### 开发环境 285 | 286 | - Python 3.8+ 287 | - Node.js 14+ 288 | - Vue 3 289 | - FastAPI 290 | 291 | ### 本地开发 292 | 293 | 1. 后端开发 294 | 295 | ```bash 296 | python main.py 297 | ``` 298 | 299 | 2. 前端开发 300 | 301 | ```bash 302 | cd fcb-fronted 303 | npm install 304 | npm run dev 305 | ``` 306 | 307 | ## 🤝 贡献指南 308 | 309 | 1. Fork 本项目 310 | 2. 创建新分支 `git checkout -b feature/xxx` 311 | 3. 提交更改 `git commit -m 'Add xxx'` 312 | 4. 推送到分支 `git push origin feature/xxx` 313 | 5. 提交 Pull Request 314 | 315 | ## ❓ 常见问题 316 | 317 | ### Q: 如何修改上传大小限制? 318 | 319 | A: 在管理面板中修改配置项 `uploadSize` 320 | 321 | ### Q: 如何配置存储引擎? 322 | 323 | A: 在管理面板中选择存储引擎并配置相应参数 324 | 325 | ### Q: 如何备份数据? 326 | 327 | A: 备份 `data` 目录即可 328 | 329 | 更多问题请访问 [Wiki](https://github.com/vastsa/FileCodeBox/wiki/常见问题) 330 | 331 | ## 📊 项目统计 332 | 333 |
334 | Featured|HelloGitHub 335 | 336 | ![Repobeats](https://repobeats.axiom.co/api/embed/7a6c92f1d96ee57e6fb67f0df371528397b0c9ac.svg) 337 | 338 | [![Star History](https://api.star-history.com/svg?repos=vastsa/FileCodeBox&type=Date)](https://star-history.com/#vastsa/FileCodeBox&Date) 339 |
340 | 341 | ## 📜 免责声明 342 | 343 | 本项目开源仅供学习使用,不得用于任何违法用途,否则后果自负,与作者无关。使用时请保留项目地址和版权信息。 344 | -------------------------------------------------------------------------------- /readme_onedrive.md: -------------------------------------------------------------------------------- 1 | # OneDrive作为存储的配置方法 2 | 3 | **仅支持工作或学校账户,并且需要有管理员权限以授权API** 4 | 5 | ## 1. 需要配置的参数 6 | 7 | ``` 8 | file_storage=onedrive 9 | onedrive_domain=XXXXXX 10 | onedrive_client_id=XXXXXX-XXXXXX-XXXXXX-XXXXXX 11 | onedrive_username=XXXXXX@XXXXXX 12 | onedrive_password=XXXXXX 13 | ``` 14 | 15 | `onedrive_username`和`onedrive_password`是你的账户名(邮箱)和密码,另外两个参数需要在[微软Azure门户](https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade)中注册应用后获取。 16 | 17 | ## 2. 应用注册 18 | 19 | 1. 登录[https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade](https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade),鼠标置于右上角账号处,浮窗将显示的`域`即为`onedrive_domain`的值。 20 | ![onedrive_domain](https://api.onedrive.com/v1.0/shares/s!Au-BDzXcM6_VmGCiErO85doq9Tcu/root/content) 21 | 22 | 2. 点击左上角的`+新注册`,输入名称, 23 | * 受支持的帐户类型:选择任何组织目录(任何 Azure AD 目录 - 多租户)中的帐户和个人 Microsoft 帐户(例如,Skype、Xbox) 24 | * 重定向 URI (可选):选择`Web`,并输入`http://localhost` 25 | 26 | 3. 完成注册后进入概述页面,在概要中找到`应用程序(客户端)ID`,即为`onedrive_client_id`的值。 27 | ![onedrive_client_id](https://api.onedrive.com/v1.0/shares/s!Au-BDzXcM6_VmGHD4CNyJxm_QBb8/root/content) 28 | 29 | 4. 此时还需要配置允许公共客户端流和API权限 30 | * 在左侧选择`身份验证`,找到`允许的客户端流`,选择`是`,并**点击`保存`**。 31 | ![允许的客户端流](https://api.onedrive.com/v1.0/shares/s!Au-BDzXcM6_VmGJQMOlOCb2-L0Lh/root/content) 32 | * 在左侧选择`API权限`,点击`+添加权限`,选择`Microsoft Graph`->`委托的权限`,并勾选下述权限:openid、Files中所有权限、User.Read,如下图所示。最后**点击下方的`添加权限`**。 33 | ![添加权限](https://api.onedrive.com/v1.0/shares/s!Au-BDzXcM6_VmGOZzz7sIrdXkD4w/root/content) 34 | * 最后点击`授予管理员同意`,并**点击`是`**,最终状态变为`已授予`。 35 | ![授予管理员同意](https://api.onedrive.com/v1.0/shares/s!Au-BDzXcM6_VmGSOAnjnHUlbirbU/root/content) 36 | 37 | ## 3. 使用下述代码测试是否配置成功 38 | 39 | 安装依赖:`pip install Office365-REST-Python-Client` 40 | 41 | ```python 42 | # common.py 43 | import msal 44 | domain = 'XXXXXX' 45 | client_id = 'XXXXXX' 46 | username = 'XXXXXX' 47 | password = 'XXXXXX' 48 | 49 | def acquire_token_pwd(): 50 | authority_url = f'https://login.microsoftonline.com/{domain}' 51 | app = msal.PublicClientApplication( 52 | authority=authority_url, 53 | client_id=client_id 54 | ) 55 | result = app.acquire_token_by_username_password( 56 | username=username, 57 | password=password, 58 | scopes=['https://graph.microsoft.com/.default'] 59 | ) 60 | return result 61 | ``` 62 | 63 | 测试登录,如果成功打印出账户名,说明配置成功。 64 | 65 | ```python 66 | from common import acquire_token_pwd 67 | 68 | from office365.graph_client import GraphClient 69 | try: 70 | client = GraphClient(acquire_token_pwd) 71 | me = client.me.get().execute_query() 72 | print(me.user_principal_name) 73 | except Exception as e: 74 | print(e) 75 | ``` 76 | 77 | 测试文件上传 78 | 79 | ```python 80 | import os 81 | from office365.graph_client import GraphClient 82 | from common import acquire_token_pwd 83 | 84 | remote_path = 'tmp' 85 | local_path = '.tmp/1689843925000.png' 86 | 87 | def convert_link_to_download_link(link): 88 | import re 89 | p1 = re.search(r'https:\/\/(.+)\.sharepoint\.com', link).group(1) 90 | p2 = re.search(r'personal\/(.+)\/', link).group(1) 91 | p3 = re.search(rf'{p2}\/(.+)', link).group(1) 92 | return f'https://{p1}.sharepoint.com/personal/{p2}/_layouts/52/download.aspx?share={p3}' 93 | 94 | client = GraphClient(acquire_token_pwd) 95 | folder = client.me.drive.root.get_by_path(remote_path) 96 | # 1. upload 97 | file = folder.upload_file(local_path).execute_query() 98 | print(f'File {file.web_url} has been uploaded') 99 | # 2. create sharing link 100 | remote_file = folder.get_by_path(os.path.basename(local_path)) 101 | permission = remote_file.create_link("view", "anonymous").execute_query() 102 | print(f"sharing link: {convert_link_to_download_link(permission.link.webUrl)}") 103 | ``` 104 | 105 | 测试文件下载 106 | 107 | ```python 108 | import os 109 | from office365.graph_client import GraphClient 110 | from common import acquire_token_pwd 111 | 112 | remote_path = 'tmp/1689843925000.png' 113 | local_path = '.tmp' 114 | if not os.path.exists(local_path): 115 | os.makedirs(local_path) 116 | 117 | client = GraphClient(acquire_token_pwd) 118 | remote_file = client.me.drive.root.get_by_path(remote_path).get().execute_query() 119 | with open(os.path.join(local_path, os.path.basename(remote_path)), 'wb') as local_file: 120 | remote_file.download(local_file).execute_query() 121 | print(f'{remote_file.name} has been downloaded into {local_file.name}') 122 | ``` 123 | 124 | 测试删除文件 125 | 126 | ```python 127 | from office365.graph_client import GraphClient 128 | from common import acquire_token_pwd 129 | 130 | remote_path = 'tmp/1689843925000.png' 131 | 132 | client = GraphClient(acquire_token_pwd) 133 | file = client.me.drive.root.get_by_path(remote_path) 134 | file.delete_object().execute_query() 135 | ``` 136 | -------------------------------------------------------------------------------- /readme_opendal.md: -------------------------------------------------------------------------------- 1 | # 通过 OpenDAL 集成存储的配置方法 2 | 3 | ## 需要配置的参数 4 | 5 | ```dotenv 6 | file_storage=opendal 7 | opendal_scheme= 8 | opendal__=... 9 | ``` 10 | 11 | 以 Gcs 为例,需要配置的参数如下: 12 | ```dotenv 13 | file_storage=opendal 14 | opendal_scheme=gcs 15 | opendal_gcs_root= 16 | opendal_gcs_bucket= 17 | opendal_gcs_credential= 18 | ``` 19 | 20 | 所有支持的服务可以在[此处](https://opendal.apache.org/docs/rust/opendal/services/index.html)查看。 21 | 具体服务的配置参数与 OpenDAL 文档一致。 22 | 23 | ## 补充说明 24 | 25 | 通过 OpenDAL 集成的服务均通过服务器中转下载。因此,每次下载既消耗存储服务的流量,也消耗服务器的流量。 26 | 27 | OpenDAL 和该项目本身都支持本地存储、`s3`、`onedrive`。不同之处有以下几点: 28 | 1. 项目的支持通过预签名实现,不消耗服务器流量。而 OpenDAL 通过服务器中转下载,消耗服务器流量。(本地存储除外) 29 | 2. 项目的支持对于异常情况可能会有更多的调试信息,方便排查问题。 30 | 3. OpenDAL 项目本身采用 Rust 编写,性能更好。 -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aioboto3==11.2.0 2 | aiohttp==3.10.11 3 | aiofiles==24.1.0 4 | botocore==1.29.76 5 | fastapi==0.115.0 6 | pydantic==2.4.0 7 | uvicorn==0.23.2 8 | tortoise-orm==0.20.0 9 | python-multipart==0.0.20 10 | -------------------------------------------------------------------------------- /themes/2023/assets/AboutView-DYjyUJF_.js: -------------------------------------------------------------------------------- 1 | import{a as s,a8 as n,P as c,N as e,U as r,R as a,j as o,o as i}from"./index-CxMsK_Ni.js";const l={style:{"text-align":"center"}},u={style:{color:"#333"},href:"https://github.com/vastsa/FileCodeBox"},f=s({__name:"AboutView",setup(_){const{t}=n();return(m,p)=>(i(),c("main",l,[e("span",null,[r(a(o(t)("admin.about.source1"))+" ",1),e("em",null,[e("a",u,a(o(t)("admin.about.source2")),1)])])]))}});export{f as default}; 2 | -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_AMS-Regular-DMm9YOAa.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_AMS-Regular-DMm9YOAa.woff -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_AMS-Regular-DRggAlZN.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_AMS-Regular-DRggAlZN.ttf -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Fraktur-Regular-CB_wures.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Fraktur-Regular-CB_wures.ttf -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Main-Bold-Cx986IdX.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Main-Bold-Cx986IdX.woff2 -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Main-Bold-Jm3AIy58.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Main-Bold-Jm3AIy58.woff -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Main-Bold-waoOVXN0.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Main-Bold-waoOVXN0.ttf -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Main-Italic-3WenGoN9.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Main-Italic-3WenGoN9.ttf -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Main-Italic-BMLOBm91.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Main-Italic-BMLOBm91.woff -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Main-Regular-B22Nviop.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Main-Regular-B22Nviop.woff2 -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Main-Regular-Dr94JaBh.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Main-Regular-Dr94JaBh.woff -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Main-Regular-ypZvNtVU.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Main-Regular-ypZvNtVU.ttf -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Math-Italic-DA0__PXp.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Math-Italic-DA0__PXp.woff -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Math-Italic-flOr_0UB.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Math-Italic-flOr_0UB.ttf -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Math-Italic-t53AETM-.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Math-Italic-t53AETM-.woff2 -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Script-Regular-C5JkGWo-.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Script-Regular-C5JkGWo-.ttf -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Script-Regular-D3wIWfF6.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Script-Regular-D5yQViql.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Script-Regular-D5yQViql.woff -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Size1-Regular-C195tn64.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Size1-Regular-C195tn64.woff -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Size2-Regular-oD1tc_U0.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Size2-Regular-oD1tc_U0.woff -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Size3-Regular-CTq5MqoE.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Size3-Regular-CTq5MqoE.woff -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Size4-Regular-BF-4gkZK.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Size4-Regular-BF-4gkZK.woff -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Size4-Regular-DWFBv043.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Size4-Regular-DWFBv043.ttf -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 -------------------------------------------------------------------------------- /themes/2023/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf -------------------------------------------------------------------------------- /themes/2023/assets/LocalView-Bi6sx1Wt.css: -------------------------------------------------------------------------------- 1 | .el-empty{--el-empty-padding:40px 0;--el-empty-image-width:160px;--el-empty-description-margin-top:20px;--el-empty-bottom-margin-top:20px;--el-empty-fill-color-0:var(--el-color-white);--el-empty-fill-color-1:#fcfcfd;--el-empty-fill-color-2:#f8f9fb;--el-empty-fill-color-3:#f7f8fc;--el-empty-fill-color-4:#eeeff3;--el-empty-fill-color-5:#edeef2;--el-empty-fill-color-6:#e9ebef;--el-empty-fill-color-7:#e5e7e9;--el-empty-fill-color-8:#e0e3e9;--el-empty-fill-color-9:#d5d7de;align-items:center;box-sizing:border-box;display:flex;flex-direction:column;justify-content:center;padding:var(--el-empty-padding);text-align:center}.el-empty__image{width:var(--el-empty-image-width)}.el-empty__image img{height:100%;-o-object-fit:contain;object-fit:contain;-webkit-user-select:none;-moz-user-select:none;user-select:none;vertical-align:top;width:100%}.el-empty__image svg{color:var(--el-svg-monochrome-grey);fill:currentColor;height:100%;vertical-align:top;width:100%}.el-empty__description{margin-top:var(--el-empty-description-margin-top)}.el-empty__description p{color:var(--el-text-color-secondary);font-size:var(--el-font-size-base);margin:0}.el-empty__bottom{margin-top:var(--el-empty-bottom-margin-top)}.file-list[data-v-d08589ab]{display:grid;width:100%;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:16px;padding:16px}.file-card[data-v-d08589ab]{align-items:center;justify-content:space-between;border-radius:10px;transition:all .3s ease}.file-card[data-v-d08589ab]:hover{box-shadow:0 6px 15px #00000040}.file-info[data-v-d08589ab]{flex:1}.file-name[data-v-d08589ab]{font-size:14px;font-weight:700;white-space:nowrap}.file-date[data-v-d08589ab]{font-size:12px;color:#a0a0b2} 2 | -------------------------------------------------------------------------------- /themes/2023/assets/LocalView-Co9a5QvS.js: -------------------------------------------------------------------------------- 1 | import{u as q,E as H,b as Q}from"./el-input-CNNwOXGP.js";import{c as Z,h as A,d as J}from"./config-CZWWa62X.js";import{E as K,a as W}from"./el-form-item-llYGp2SQ.js";import"./el-tag-5pOVEKUY.js";import{E as X,a as Y}from"./el-select-LRJf3l5x.js";import"./el-popper-C96qJ_vz.js";import{_ as R,a as g,u as G,P as c,o as n,N as l,j as e,b as ee,ao as le,c as B,aH as te,Q as C,r as x,B as d,n as ae,h,R as m,l as oe,a8 as se,s as N,t as ne,g as V,ab as b,ac as F,w as i,af as $,U as k,aF as I}from"./index-CxMsK_Ni.js";import"./vnode-Cc0RQjVK.js";import"./_baseClone-DD6IrDYX.js";const re=g({name:"ImgEmpty"}),ie=g({...re,setup(_){const t=G("empty"),r=q();return(p,u)=>(n(),c("svg",{viewBox:"0 0 79 86",version:"1.1",xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink"},[l("defs",null,[l("linearGradient",{id:`linearGradient-1-${e(r)}`,x1:"38.8503086%",y1:"0%",x2:"61.1496914%",y2:"100%"},[l("stop",{"stop-color":`var(${e(t).cssVarBlockName("fill-color-1")})`,offset:"0%"},null,8,["stop-color"]),l("stop",{"stop-color":`var(${e(t).cssVarBlockName("fill-color-4")})`,offset:"100%"},null,8,["stop-color"])],8,["id"]),l("linearGradient",{id:`linearGradient-2-${e(r)}`,x1:"0%",y1:"9.5%",x2:"100%",y2:"90.5%"},[l("stop",{"stop-color":`var(${e(t).cssVarBlockName("fill-color-1")})`,offset:"0%"},null,8,["stop-color"]),l("stop",{"stop-color":`var(${e(t).cssVarBlockName("fill-color-6")})`,offset:"100%"},null,8,["stop-color"])],8,["id"]),l("rect",{id:`path-3-${e(r)}`,x:"0",y:"0",width:"17",height:"36"},null,8,["id"])]),l("g",{id:"Illustrations",stroke:"none","stroke-width":"1",fill:"none","fill-rule":"evenodd"},[l("g",{id:"B-type",transform:"translate(-1268.000000, -535.000000)"},[l("g",{id:"Group-2",transform:"translate(1268.000000, 535.000000)"},[l("path",{id:"Oval-Copy-2",d:"M39.5,86 C61.3152476,86 79,83.9106622 79,81.3333333 C79,78.7560045 57.3152476,78 35.5,78 C13.6847524,78 0,78.7560045 0,81.3333333 C0,83.9106622 17.6847524,86 39.5,86 Z",fill:`var(${e(t).cssVarBlockName("fill-color-3")})`},null,8,["fill"]),l("polygon",{id:"Rectangle-Copy-14",fill:`var(${e(t).cssVarBlockName("fill-color-7")})`,transform:"translate(27.500000, 51.500000) scale(1, -1) translate(-27.500000, -51.500000) ",points:"13 58 53 58 42 45 2 45"},null,8,["fill"]),l("g",{id:"Group-Copy",transform:"translate(34.500000, 31.500000) scale(-1, 1) rotate(-25.000000) translate(-34.500000, -31.500000) translate(7.000000, 10.000000)"},[l("polygon",{id:"Rectangle-Copy-10",fill:`var(${e(t).cssVarBlockName("fill-color-7")})`,transform:"translate(11.500000, 5.000000) scale(1, -1) translate(-11.500000, -5.000000) ",points:"2.84078316e-14 3 18 3 23 7 5 7"},null,8,["fill"]),l("polygon",{id:"Rectangle-Copy-11",fill:`var(${e(t).cssVarBlockName("fill-color-5")})`,points:"-3.69149156e-15 7 38 7 38 43 -3.69149156e-15 43"},null,8,["fill"]),l("rect",{id:"Rectangle-Copy-12",fill:`url(#linearGradient-1-${e(r)})`,transform:"translate(46.500000, 25.000000) scale(-1, 1) translate(-46.500000, -25.000000) ",x:"38",y:"7",width:"17",height:"36"},null,8,["fill"]),l("polygon",{id:"Rectangle-Copy-13",fill:`var(${e(t).cssVarBlockName("fill-color-2")})`,transform:"translate(39.500000, 3.500000) scale(-1, 1) translate(-39.500000, -3.500000) ",points:"24 7 41 7 55 -3.63806207e-12 38 -3.63806207e-12"},null,8,["fill"])]),l("rect",{id:"Rectangle-Copy-15",fill:`url(#linearGradient-2-${e(r)})`,x:"13",y:"45",width:"40",height:"36"},null,8,["fill"]),l("g",{id:"Rectangle-Copy-17",transform:"translate(53.000000, 45.000000)"},[l("use",{id:"Mask",fill:`var(${e(t).cssVarBlockName("fill-color-8")})`,transform:"translate(8.500000, 18.000000) scale(-1, 1) translate(-8.500000, -18.000000) ","xlink:href":`#path-3-${e(r)}`},null,8,["fill","xlink:href"]),l("polygon",{id:"Rectangle-Copy",fill:`var(${e(t).cssVarBlockName("fill-color-9")})`,mask:`url(#mask-4-${e(r)})`,transform:"translate(12.000000, 9.000000) scale(-1, 1) translate(-12.000000, -9.000000) ",points:"7 0 24 0 20 18 7 16.5"},null,8,["fill","mask"])]),l("polygon",{id:"Rectangle-Copy-18",fill:`var(${e(t).cssVarBlockName("fill-color-2")})`,transform:"translate(66.000000, 51.500000) scale(-1, 1) translate(-66.000000, -51.500000) ",points:"62 45 79 45 70 58 53 58"},null,8,["fill"])])])])]))}});var ce=R(ie,[["__file","img-empty.vue"]]);const de=ee({image:{type:String,default:""},imageSize:Number,description:{type:String,default:""}}),pe=g({name:"ElEmpty"}),me=g({...pe,props:de,setup(_){const t=_,{t:r}=le(),p=G("empty"),u=B(()=>t.description||r("el.table.emptyText")),y=B(()=>({width:te(t.imageSize)}));return(a,w)=>(n(),c("div",{class:h(e(p).b())},[l("div",{class:h(e(p).e("image")),style:ae(e(y))},[a.image?(n(),c("img",{key:0,src:a.image,ondragstart:"return false"},null,8,["src"])):x(a.$slots,"image",{key:1},()=>[d(ce)])],6),l("div",{class:h(e(p).e("description"))},[a.$slots.description?x(a.$slots,"description",{key:0}):(n(),c("p",{key:1},m(e(u)),1))],2),a.$slots.default?(n(),c("div",{key:0,class:h(e(p).e("bottom"))},[x(a.$slots,"default")],2)):C("v-if",!0)],2))}});var ue=R(me,[["__file","empty.vue"]]);const fe=oe(ue),ye={class:"file-list"},_e={class:"file-info"},ge={class:"file-name"},ve={class:"file-date"},he={style:{width:"100%","text-align":"right"}},ke={key:0},xe={key:1},Ve={key:2},$e={key:3},Ce={key:4},we={class:"dialog-footer"},Ee=g({__name:"LocalView",setup(_){const{t}=se(),{config:r}=Z(),p=N([]),u=()=>{$({url:"/admin/local/lists",method:"get"}).then(f=>{p.value=f.detail})},y=N(!1),a=ne({name:"1",expireStyle:"day",expireValue:1});u();const w=f=>{$({url:"/admin/local/delete",method:"delete",data:{filename:f.file}}).then(s=>{I.success(s.detail),u()})},L=f=>{a.name=f.file,y.value=!0},U=()=>{$({url:"/admin/local/share",method:"post",data:{filename:a.name,expire_style:a.expireStyle,expire_value:a.expireValue}}).then(f=>{y.value=!1,I.success({showClose:!0,message:"Code:"+f.detail.code,duration:0}),u()})};return(f,s)=>{const z=fe,v=Q,D=J,E=H,S=W,M=Y,O=X,P=K,T=A;return n(),c("div",ye,[p.value.length===0?(n(),V(z,{key:0,style:{width:"90vw"},description:"请在/opt/filecodebox/local目录上传您需要分享的文件"})):C("",!0),(n(!0),c(b,null,F(p.value,o=>(n(),V(D,{key:o.name,class:"file-card",shadow:"hover"},{default:i(()=>[l("div",_e,[l("div",ge,m(o.file),1),l("div",ve,m(o.ctime),1),l("div",he,[d(v,{type:"primary",style:{"margin-top":"1rem"},onClick:j=>L(o),plain:""},{default:i(()=>s[6]||(s[6]=[k("分享")])),_:2},1032,["onClick"]),d(v,{type:"danger",style:{"margin-top":"1rem"},onClick:j=>w(o),plain:""},{default:i(()=>s[7]||(s[7]=[k("删除")])),_:2},1032,["onClick"])])])]),_:2},1024))),128)),d(T,{modelValue:y.value,"onUpdate:modelValue":s[5]||(s[5]=o=>y.value=o),width:"500"},{footer:i(()=>[l("div",we,[d(v,{onClick:s[3]||(s[3]=o=>y.value=!1)},{default:i(()=>[k(m(e(t)("admin.local.Cancel")),1)]),_:1}),d(v,{type:"primary",onClick:s[4]||(s[4]=o=>U())},{default:i(()=>[k(m(e(t)("admin.local.Confirm")),1)]),_:1})])]),default:i(()=>[d(P,{model:a},{default:i(()=>[d(S,{label:e(t)("admin.local.Name")},{default:i(()=>[d(E,{modelValue:a.name,"onUpdate:modelValue":s[0]||(s[0]=o=>a.name=o),readonly:"",autocomplete:"off"},null,8,["modelValue"])]),_:1},8,["label"]),d(S,{label:e(t)("admin.local.Expire")},{default:i(()=>[d(E,{modelValue:a.expireValue,"onUpdate:modelValue":s[2]||(s[2]=o=>a.expireValue=o),style:{width:"200px"},placeholder:e(t)("send.pleaseInputExpireValue")},{prepend:i(()=>[d(O,{modelValue:a.expireStyle,"onUpdate:modelValue":s[1]||(s[1]=o=>a.expireStyle=o),placeholder:e(t)("send.expireStyle"),style:{width:"75px"}},{default:i(()=>[(n(!0),c(b,null,F(e(r).expireStyle,o=>(n(),V(M,{key:o,label:e(t)(`send.expireData.${o}`),value:o},null,8,["label","value"]))),128))]),_:1},8,["modelValue","placeholder"])]),append:i(()=>[a.expireStyle==="day"?(n(),c("span",ke,m(e(t)("send.expireValue.day")),1)):a.expireStyle==="hour"?(n(),c("span",xe,m(e(t)("send.expireValue.hour")),1)):a.expireStyle==="minute"?(n(),c("span",Ve,m(e(t)("send.expireValue.minute")),1)):a.expireStyle==="forever"?(n(),c("span",$e,"👌")):a.expireStyle==="count"?(n(),c("span",Ce,m(e(t)("send.expireValue.count")),1)):C("",!0)]),_:1},8,["modelValue","placeholder"])]),_:1},8,["label"])]),_:1},8,["model"])]),_:1},8,["modelValue"])])}}}),Se=(_,t)=>{const r=_.__vccOpts||_;for(const[p,u]of t)r[p]=u;return r},ze=Se(Ee,[["__scopeId","data-v-d08589ab"]]);export{ze as default}; 2 | -------------------------------------------------------------------------------- /themes/2023/assets/SettingView-Ba6-Wyi6.css: -------------------------------------------------------------------------------- 1 | .left-menu{margin-bottom:1rem;cursor:pointer;text-align:center;font-weight:700;font-size:1rem;letter-spacing:.4rem}small,.el-form-item__content{color:#909399;margin-left:.4rem} 2 | -------------------------------------------------------------------------------- /themes/2023/assets/_baseClone-DD6IrDYX.js: -------------------------------------------------------------------------------- 1 | import{c as y,k as F,a as l,m as C,s as _,g as v,n as N,o as E,h as K,p as j,q as x,r as u,e as R,b as q,f as W,j as Y,S as H,t as J}from"./el-popper-C96qJ_vz.js";import{bv as $,b6 as B,b3 as Q,b9 as V,bw as X}from"./index-CxMsK_Ni.js";import{x as Z}from"./el-input-CNNwOXGP.js";function z(e,r){for(var n=-1,s=e==null?0:e.length;++n.el-form-item__label-wrap>.el-form-item__label:before,.el-form-item.is-required:not(.is-no-asterisk).asterisk-left>.el-form-item__label:before{color:var(--el-color-danger);content:"*";margin-right:4px}.el-form-item.is-required:not(.is-no-asterisk).asterisk-right>.el-form-item__label-wrap>.el-form-item__label:after,.el-form-item.is-required:not(.is-no-asterisk).asterisk-right>.el-form-item__label:after{color:var(--el-color-danger);content:"*";margin-left:4px}.el-form-item.is-error .el-input-tag__wrapper,.el-form-item.is-error .el-input-tag__wrapper.is-focus,.el-form-item.is-error .el-input-tag__wrapper:focus,.el-form-item.is-error .el-input-tag__wrapper:hover,.el-form-item.is-error .el-input__wrapper,.el-form-item.is-error .el-input__wrapper.is-focus,.el-form-item.is-error .el-input__wrapper:focus,.el-form-item.is-error .el-input__wrapper:hover,.el-form-item.is-error .el-select__wrapper,.el-form-item.is-error .el-select__wrapper.is-focus,.el-form-item.is-error .el-select__wrapper:focus,.el-form-item.is-error .el-select__wrapper:hover,.el-form-item.is-error .el-textarea__inner,.el-form-item.is-error .el-textarea__inner.is-focus,.el-form-item.is-error .el-textarea__inner:focus,.el-form-item.is-error .el-textarea__inner:hover{box-shadow:0 0 0 1px var(--el-color-danger) inset}.el-form-item.is-error .el-input-group__append .el-input__wrapper,.el-form-item.is-error .el-input-group__prepend .el-input__wrapper{box-shadow:inset 0 0 0 1px transparent}.el-form-item.is-error .el-input-group__append .el-input__validateIcon,.el-form-item.is-error .el-input-group__prepend .el-input__validateIcon{display:none}.el-form-item.is-error .el-input__validateIcon{color:var(--el-color-danger)}.el-form-item--feedback .el-input__validateIcon{display:inline-flex} 2 | -------------------------------------------------------------------------------- /themes/2023/assets/el-popper-DG5wR-qi.css: -------------------------------------------------------------------------------- 1 | .el-popper{--el-popper-border-radius:var(--el-popover-border-radius,4px);border-radius:var(--el-popper-border-radius);font-size:12px;line-height:20px;min-width:10px;overflow-wrap:break-word;padding:5px 11px;position:absolute;visibility:visible;z-index:2000}.el-popper.is-dark{color:var(--el-bg-color)}.el-popper.is-dark,.el-popper.is-dark>.el-popper__arrow:before{background:var(--el-text-color-primary);border:1px solid var(--el-text-color-primary)}.el-popper.is-dark>.el-popper__arrow:before{right:0}.el-popper.is-light,.el-popper.is-light>.el-popper__arrow:before{background:var(--el-bg-color-overlay);border:1px solid var(--el-border-color-light)}.el-popper.is-light>.el-popper__arrow:before{right:0}.el-popper.is-pure{padding:0}.el-popper__arrow,.el-popper__arrow:before{height:10px;position:absolute;width:10px;z-index:-1}.el-popper__arrow:before{background:var(--el-text-color-primary);box-sizing:border-box;content:" ";transform:rotate(45deg)}.el-popper[data-popper-placement^=top]>.el-popper__arrow{bottom:-5px}.el-popper[data-popper-placement^=top]>.el-popper__arrow:before{border-bottom-right-radius:2px}.el-popper[data-popper-placement^=bottom]>.el-popper__arrow{top:-5px}.el-popper[data-popper-placement^=bottom]>.el-popper__arrow:before{border-top-left-radius:2px}.el-popper[data-popper-placement^=left]>.el-popper__arrow{right:-5px}.el-popper[data-popper-placement^=left]>.el-popper__arrow:before{border-top-right-radius:2px}.el-popper[data-popper-placement^=right]>.el-popper__arrow{left:-5px}.el-popper[data-popper-placement^=right]>.el-popper__arrow:before{border-bottom-left-radius:2px}.el-popper[data-popper-placement^=top]>.el-popper__arrow:before{border-left-color:transparent!important;border-top-color:transparent!important}.el-popper[data-popper-placement^=bottom]>.el-popper__arrow:before{border-bottom-color:transparent!important;border-right-color:transparent!important}.el-popper[data-popper-placement^=left]>.el-popper__arrow:before{border-bottom-color:transparent!important;border-left-color:transparent!important}.el-popper[data-popper-placement^=right]>.el-popper__arrow:before{border-right-color:transparent!important;border-top-color:transparent!important} 2 | -------------------------------------------------------------------------------- /themes/2023/assets/el-tag-5pOVEKUY.js: -------------------------------------------------------------------------------- 1 | import{E,b as P,q as V,_ as W,a as N,u as M,c as $,P as H,g as v,o as m,N as y,Q as k,r as C,h as c,j as l,w as b,B as T,as as _,O as w,W as B,n as S,T as I,l as R}from"./index-CxMsK_Ni.js";import{c as j}from"./el-input-CNNwOXGP.js";let h;const G=s=>{var n;if(!E)return 0;if(h!==void 0)return h;const t=document.createElement("div");t.className=`${s}-scrollbar__wrap`,t.style.visibility="hidden",t.style.width="100px",t.style.position="absolute",t.style.top="-9999px",document.body.appendChild(t);const a=t.offsetWidth;t.style.overflow="scroll";const e=document.createElement("div");e.style.width="100%",t.appendChild(e);const i=e.offsetWidth;return(n=t.parentNode)==null||n.removeChild(t),h=a-i,h};function J(s,n){if(!E)return;if(!n){s.scrollTop=0;return}const t=[];let a=n.offsetParent;for(;a!==null&&s!==a&&s.contains(a);)t.push(a),a=a.offsetParent;const e=n.offsetTop+t.reduce((g,o)=>g+o.offsetTop,0),i=e+n.offsetHeight,r=s.scrollTop,p=r+s.clientHeight;ep&&(s.scrollTop=i-s.clientHeight)}const q=P({type:{type:String,values:["primary","success","info","warning","danger"],default:"primary"},closable:Boolean,disableTransitions:Boolean,hit:Boolean,color:String,size:{type:String,values:V},effect:{type:String,values:["dark","light","plain"],default:"light"},round:Boolean}),F={close:s=>s instanceof MouseEvent,click:s=>s instanceof MouseEvent},K=N({name:"ElTag"}),O=N({...K,props:q,emits:F,setup(s,{emit:n}){const t=s,a=j(),e=M("tag"),i=$(()=>{const{type:o,hit:u,effect:d,closable:f,round:z}=t;return[e.b(),e.is("closable",f),e.m(o||"primary"),e.m(a.value),e.m(d),e.is("hit",u),e.is("round",z)]}),r=o=>{n("close",o)},p=o=>{n("click",o)},g=o=>{var u,d,f;(f=(d=(u=o==null?void 0:o.component)==null?void 0:u.subTree)==null?void 0:d.component)!=null&&f.bum&&(o.component.subTree.component.bum=null)};return(o,u)=>o.disableTransitions?(m(),H("span",{key:0,class:c(l(i)),style:S({backgroundColor:o.color}),onClick:p},[y("span",{class:c(l(e).e("content"))},[C(o.$slots,"default")],2),o.closable?(m(),v(l(B),{key:0,class:c(l(e).e("close")),onClick:w(r,["stop"])},{default:b(()=>[T(l(_))]),_:1},8,["class","onClick"])):k("v-if",!0)],6)):(m(),v(I,{key:1,name:`${l(e).namespace.value}-zoom-in-center`,appear:"",onVnodeMounted:g},{default:b(()=>[y("span",{class:c(l(i)),style:S({backgroundColor:o.color}),onClick:p},[y("span",{class:c(l(e).e("content"))},[C(o.$slots,"default")],2),o.closable?(m(),v(l(B),{key:0,class:c(l(e).e("close")),onClick:w(r,["stop"])},{default:b(()=>[T(l(_))]),_:1},8,["class","onClick"])):k("v-if",!0)],6)]),_:3},8,["name"]))}});var Q=W(O,[["__file","tag.vue"]]);const L=R(Q);export{L as E,G as g,J as s,q as t}; 2 | -------------------------------------------------------------------------------- /themes/2023/assets/el-tag-DljBBxJR.css: -------------------------------------------------------------------------------- 1 | .el-tag{--el-tag-font-size:12px;--el-tag-border-radius:4px;--el-tag-border-radius-rounded:9999px;align-items:center;background-color:var(--el-tag-bg-color);border-color:var(--el-tag-border-color);border-radius:var(--el-tag-border-radius);border-style:solid;border-width:1px;box-sizing:border-box;color:var(--el-tag-text-color);display:inline-flex;font-size:var(--el-tag-font-size);height:24px;justify-content:center;line-height:1;padding:0 9px;vertical-align:middle;white-space:nowrap;--el-icon-size:14px}.el-tag,.el-tag.el-tag--primary{--el-tag-bg-color:var(--el-color-primary-light-9);--el-tag-border-color:var(--el-color-primary-light-8);--el-tag-hover-color:var(--el-color-primary)}.el-tag.el-tag--success{--el-tag-bg-color:var(--el-color-success-light-9);--el-tag-border-color:var(--el-color-success-light-8);--el-tag-hover-color:var(--el-color-success)}.el-tag.el-tag--warning{--el-tag-bg-color:var(--el-color-warning-light-9);--el-tag-border-color:var(--el-color-warning-light-8);--el-tag-hover-color:var(--el-color-warning)}.el-tag.el-tag--danger{--el-tag-bg-color:var(--el-color-danger-light-9);--el-tag-border-color:var(--el-color-danger-light-8);--el-tag-hover-color:var(--el-color-danger)}.el-tag.el-tag--error{--el-tag-bg-color:var(--el-color-error-light-9);--el-tag-border-color:var(--el-color-error-light-8);--el-tag-hover-color:var(--el-color-error)}.el-tag.el-tag--info{--el-tag-bg-color:var(--el-color-info-light-9);--el-tag-border-color:var(--el-color-info-light-8);--el-tag-hover-color:var(--el-color-info)}.el-tag.is-hit{border-color:var(--el-color-primary)}.el-tag.is-round{border-radius:var(--el-tag-border-radius-rounded)}.el-tag .el-tag__close{color:var(--el-tag-text-color);flex-shrink:0}.el-tag .el-tag__close:hover{background-color:var(--el-tag-hover-color);color:var(--el-color-white)}.el-tag.el-tag--primary{--el-tag-text-color:var(--el-color-primary)}.el-tag.el-tag--success{--el-tag-text-color:var(--el-color-success)}.el-tag.el-tag--warning{--el-tag-text-color:var(--el-color-warning)}.el-tag.el-tag--danger{--el-tag-text-color:var(--el-color-danger)}.el-tag.el-tag--error{--el-tag-text-color:var(--el-color-error)}.el-tag.el-tag--info{--el-tag-text-color:var(--el-color-info)}.el-tag .el-icon{border-radius:50%;cursor:pointer;font-size:calc(var(--el-icon-size) - 2px);height:var(--el-icon-size);width:var(--el-icon-size)}.el-tag .el-tag__close{margin-left:6px}.el-tag--dark{--el-tag-text-color:var(--el-color-white)}.el-tag--dark,.el-tag--dark.el-tag--primary{--el-tag-bg-color:var(--el-color-primary);--el-tag-border-color:var(--el-color-primary);--el-tag-hover-color:var(--el-color-primary-light-3)}.el-tag--dark.el-tag--success{--el-tag-bg-color:var(--el-color-success);--el-tag-border-color:var(--el-color-success);--el-tag-hover-color:var(--el-color-success-light-3)}.el-tag--dark.el-tag--warning{--el-tag-bg-color:var(--el-color-warning);--el-tag-border-color:var(--el-color-warning);--el-tag-hover-color:var(--el-color-warning-light-3)}.el-tag--dark.el-tag--danger{--el-tag-bg-color:var(--el-color-danger);--el-tag-border-color:var(--el-color-danger);--el-tag-hover-color:var(--el-color-danger-light-3)}.el-tag--dark.el-tag--error{--el-tag-bg-color:var(--el-color-error);--el-tag-border-color:var(--el-color-error);--el-tag-hover-color:var(--el-color-error-light-3)}.el-tag--dark.el-tag--info{--el-tag-bg-color:var(--el-color-info);--el-tag-border-color:var(--el-color-info);--el-tag-hover-color:var(--el-color-info-light-3)}.el-tag--dark.el-tag--danger,.el-tag--dark.el-tag--error,.el-tag--dark.el-tag--info,.el-tag--dark.el-tag--primary,.el-tag--dark.el-tag--success,.el-tag--dark.el-tag--warning{--el-tag-text-color:var(--el-color-white)}.el-tag--plain,.el-tag--plain.el-tag--primary{--el-tag-bg-color:var(--el-fill-color-blank);--el-tag-border-color:var(--el-color-primary-light-5);--el-tag-hover-color:var(--el-color-primary)}.el-tag--plain.el-tag--success{--el-tag-bg-color:var(--el-fill-color-blank);--el-tag-border-color:var(--el-color-success-light-5);--el-tag-hover-color:var(--el-color-success)}.el-tag--plain.el-tag--warning{--el-tag-bg-color:var(--el-fill-color-blank);--el-tag-border-color:var(--el-color-warning-light-5);--el-tag-hover-color:var(--el-color-warning)}.el-tag--plain.el-tag--danger{--el-tag-bg-color:var(--el-fill-color-blank);--el-tag-border-color:var(--el-color-danger-light-5);--el-tag-hover-color:var(--el-color-danger)}.el-tag--plain.el-tag--error{--el-tag-bg-color:var(--el-fill-color-blank);--el-tag-border-color:var(--el-color-error-light-5);--el-tag-hover-color:var(--el-color-error)}.el-tag--plain.el-tag--info{--el-tag-bg-color:var(--el-fill-color-blank);--el-tag-border-color:var(--el-color-info-light-5);--el-tag-hover-color:var(--el-color-info)}.el-tag.is-closable{padding-right:5px}.el-tag--large{height:32px;padding:0 11px;--el-icon-size:16px}.el-tag--large .el-tag__close{margin-left:8px}.el-tag--large.is-closable{padding-right:7px}.el-tag--small{height:20px;padding:0 7px;--el-icon-size:12px}.el-tag--small .el-tag__close{margin-left:4px}.el-tag--small.is-closable{padding-right:3px}.el-tag--small .el-icon-close{transform:scale(.8)}.el-tag.el-tag--primary.is-hit{border-color:var(--el-color-primary)}.el-tag.el-tag--success.is-hit{border-color:var(--el-color-success)}.el-tag.el-tag--warning.is-hit{border-color:var(--el-color-warning)}.el-tag.el-tag--danger.is-hit{border-color:var(--el-color-danger)}.el-tag.el-tag--error.is-hit{border-color:var(--el-color-error)}.el-tag.el-tag--info.is-hit{border-color:var(--el-color-info)} 2 | -------------------------------------------------------------------------------- /themes/2023/assets/el-tooltip-l0sNRNKZ.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /themes/2023/assets/index-CBpXPsVN.js: -------------------------------------------------------------------------------- 1 | import{J as q,s as _,aZ as b,aA as ee,a_ as $,a$ as te,a0 as H,F as G,$ as z,aT as P,b0 as K,i as ne,b1 as re,b2 as ae,c as A,aw as R,aW as oe,j as se}from"./index-CxMsK_Ni.js";function ie(e){return re()?(ae(e),!0):!1}const V=new WeakMap,ue=(...e)=>{var t;const n=e[0],r=(t=P())==null?void 0:t.proxy;if(r==null&&!K())throw new Error("injectLocal must be called in setup");return r&&V.has(r)&&n in V.get(r)?V.get(r)[n]:ne(...e)},le=typeof window<"u"&&typeof document<"u";typeof WorkerGlobalScope<"u"&&globalThis instanceof WorkerGlobalScope;const ce=Object.prototype.toString,fe=e=>ce.call(e)==="[object Object]",de=()=>{};function pe(e,t){function n(...r){return new Promise((s,u)=>{Promise.resolve(e(()=>t.apply(this,r),{fn:t,thisArg:this,args:r})).then(s).catch(u)})}return n}const Q=e=>e();function me(e=Q,t={}){const{initialState:n="active"}=t,r=Z(n==="active");function s(){r.value=!1}function u(){r.value=!0}const l=(...a)=>{r.value&&e(...a)};return{isActive:$(r),pause:s,resume:u,eventFilter:l}}function I(e){return e.endsWith("rem")?Number.parseFloat(e)*16:Number.parseFloat(e)}function he(e){return P()}function J(e){return Array.isArray(e)?e:[e]}function Z(...e){if(e.length!==1)return ee(...e);const t=e[0];return typeof t=="function"?$(te(()=>({get:t,set:de}))):_(t)}function ve(e,t,n={}){const{eventFilter:r=Q,...s}=n;return z(e,pe(r,t),s)}function ye(e,t,n={}){const{eventFilter:r,initialState:s="active",...u}=n,{eventFilter:l,pause:a,resume:i,isActive:c}=me(r,{initialState:s});return{stop:ve(e,t,{...u,eventFilter:l}),pause:a,resume:i,isActive:c}}function U(e,t=!0,n){he()?H(e,n):t?e():G(e)}function Fe(e=!1,t={}){const{truthyValue:n=!0,falsyValue:r=!1}=t,s=q(e),u=_(e);function l(a){if(arguments.length)return u.value=a,u.value;{const i=b(n);return u.value=u.value===i?b(r):i,u.value}}return s?l:[u,l]}function ge(e,t,n){return z(e,t,{...n,immediate:!0})}const D=le?window:void 0;function X(e){var t;const n=b(e);return(t=n==null?void 0:n.$el)!=null?t:n}function x(...e){const t=[],n=()=>{t.forEach(a=>a()),t.length=0},r=(a,i,c,m)=>(a.addEventListener(i,c,m),()=>a.removeEventListener(i,c,m)),s=A(()=>{const a=J(b(e[0])).filter(i=>i!=null);return a.every(i=>typeof i!="string")?a:void 0}),u=ge(()=>{var a,i;return[(i=(a=s.value)==null?void 0:a.map(c=>X(c)))!=null?i:[D].filter(c=>c!=null),J(b(s.value?e[1]:e[0])),J(se(s.value?e[2]:e[1])),b(s.value?e[3]:e[2])]},([a,i,c,m])=>{if(n(),!(a!=null&&a.length)||!(i!=null&&i.length)||!(c!=null&&c.length))return;const v=fe(m)?{...m}:m;t.push(...a.flatMap(k=>i.flatMap(y=>c.map(d=>r(k,y,d,v)))))},{flush:"post"}),l=()=>{u(),n()};return ie(n),l}function we(){const e=R(!1),t=P();return t&&H(()=>{e.value=!0},t),e}function Se(e){const t=we();return A(()=>(t.value,!!e()))}const be=Symbol("vueuse-ssr-width");function ke(){const e=K()?ue(be,null):null;return typeof e=="number"?e:void 0}function Ce(e,t={}){const{window:n=D,ssrWidth:r=ke()}=t,s=Se(()=>n&&"matchMedia"in n&&typeof n.matchMedia=="function"),u=_(typeof r=="number"),l=R(),a=R(!1),i=c=>{a.value=c.matches};return oe(()=>{if(u.value){u.value=!s.value;const c=b(e).split(",");a.value=c.some(m=>{const v=m.includes("not all"),k=m.match(/\(\s*min-width:\s*(-?\d+(?:\.\d*)?[a-z]+\s*)\)/),y=m.match(/\(\s*max-width:\s*(-?\d+(?:\.\d*)?[a-z]+\s*)\)/);let d=!!(k||y);return k&&d&&(d=r>=I(k[1])),y&&d&&(d=r<=I(y[1])),v?!d:d});return}s.value&&(l.value=n.matchMedia(b(e)),a.value=l.value.matches)}),x(l,"change",i,{passive:!0}),A(()=>a.value)}const F=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{},L="__vueuse_ssr_handlers__",Ae=Me();function Me(){return L in F||(F[L]=F[L]||{}),F[L]}function Y(e,t){return Ae[e]||t}function Te(e){return Ce("(prefers-color-scheme: dark)",e)}function Oe(e){return e==null?"any":e instanceof Set?"set":e instanceof Map?"map":e instanceof Date?"date":typeof e=="boolean"?"boolean":typeof e=="string"?"string":typeof e=="object"?"object":Number.isNaN(e)?"any":"number"}const We={boolean:{read:e=>e==="true",write:e=>String(e)},object:{read:e=>JSON.parse(e),write:e=>JSON.stringify(e)},number:{read:e=>Number.parseFloat(e),write:e=>String(e)},any:{read:e=>e,write:e=>String(e)},string:{read:e=>e,write:e=>String(e)},map:{read:e=>new Map(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e.entries()))},set:{read:e=>new Set(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e))},date:{read:e=>new Date(e),write:e=>e.toISOString()}},B="vueuse-storage";function Ee(e,t,n,r={}){var s;const{flush:u="pre",deep:l=!0,listenToStorageChanges:a=!0,writeDefaults:i=!0,mergeDefaults:c=!1,shallow:m,window:v=D,eventFilter:k,onError:y=o=>{console.error(o)},initOnMounted:d}=r,g=(m?R:_)(typeof t=="function"?t():t),w=A(()=>b(e));if(!n)try{n=Y("getDefaultStorage",()=>{var o;return(o=D)==null?void 0:o.localStorage})()}catch(o){y(o)}if(!n)return g;const S=b(t),E=Oe(S),T=(s=r.serializer)!=null?s:We[E],{pause:h,resume:M}=ye(g,()=>W(g.value),{flush:u,deep:l,eventFilter:k});z(w,()=>C(),{flush:u}),v&&a&&U(()=>{n instanceof Storage?x(v,"storage",C,{passive:!0}):x(v,B,O),d&&C()}),d||C();function j(o,f){if(v){const p={key:w.value,oldValue:o,newValue:f,storageArea:n};v.dispatchEvent(n instanceof Storage?new StorageEvent("storage",p):new CustomEvent(B,{detail:p}))}}function W(o){try{const f=n.getItem(w.value);if(o==null)j(f,null),n.removeItem(w.value);else{const p=T.write(o);f!==p&&(n.setItem(w.value,p),j(f,p))}}catch(f){y(f)}}function N(o){const f=o?o.newValue:n.getItem(w.value);if(f==null)return i&&S!=null&&n.setItem(w.value,T.write(S)),S;if(!o&&c){const p=T.read(f);return typeof c=="function"?c(p,S):E==="object"&&!Array.isArray(p)?{...S,...p}:p}else return typeof f!="string"?f:T.read(f)}function C(o){if(!(o&&o.storageArea!==n)){if(o&&o.key==null){g.value=S;return}if(!(o&&o.key!==w.value)){h();try{(o==null?void 0:o.newValue)!==T.write(g.value)&&(g.value=N(o))}catch(f){y(f)}finally{o?G(M):M()}}}}function O(o){C(o.detail)}return g}const je="*,*::before,*::after{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;-ms-transition:none!important;transition:none!important}";function Ne(e={}){const{selector:t="html",attribute:n="class",initialValue:r="auto",window:s=D,storage:u,storageKey:l="vueuse-color-scheme",listenToStorageChanges:a=!0,storageRef:i,emitAuto:c,disableTransition:m=!0}=e,v={auto:"",light:"light",dark:"dark",...e.modes||{}},k=Te({window:s}),y=A(()=>k.value?"dark":"light"),d=i||(l==null?Z(r):Ee(l,r,u,{window:s,listenToStorageChanges:a})),g=A(()=>d.value==="auto"?y.value:d.value),w=Y("updateHTMLAttrs",(h,M,j)=>{const W=typeof h=="string"?s==null?void 0:s.document.querySelector(h):X(h);if(!W)return;const N=new Set,C=new Set;let O=null;if(M==="class"){const f=j.split(/\s/g);Object.values(v).flatMap(p=>(p||"").split(/\s/g)).filter(Boolean).forEach(p=>{f.includes(p)?N.add(p):C.add(p)})}else O={key:M,value:j};if(N.size===0&&C.size===0&&O===null)return;let o;m&&(o=s.document.createElement("style"),o.appendChild(document.createTextNode(je)),s.document.head.appendChild(o));for(const f of N)W.classList.add(f);for(const f of C)W.classList.remove(f);O&&W.setAttribute(O.key,O.value),m&&(s.getComputedStyle(o).opacity,document.head.removeChild(o))});function S(h){var M;w(t,n,(M=v[h])!=null?M:h)}function E(h){e.onChanged?e.onChanged(h,S):S(h)}z(g,E,{flush:"post",immediate:!0}),U(()=>E(g.value));const T=A({get(){return c?d.value:g.value},set(h){d.value=h}});return Object.assign(T,{store:d,system:y,state:g})}function Le(e={}){const{valueDark:t="dark",valueLight:n=""}=e,r=Ne({...e,onChanged:(l,a)=>{var i;e.onChanged?(i=e.onChanged)==null||i.call(e,l==="dark",a,l):a(l)},modes:{dark:t,light:n}}),s=A(()=>r.system.value);return A({get(){return r.value==="dark"},set(l){const a=l?"dark":"light";s.value===a?r.value="auto":r.value=a}})}export{Fe as a,Le as u}; 2 | -------------------------------------------------------------------------------- /themes/2023/assets/logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2023/assets/logo_small.png -------------------------------------------------------------------------------- /themes/2023/assets/vnode-Cc0RQjVK.js: -------------------------------------------------------------------------------- 1 | import{al as S,a3 as N}from"./index-CxMsK_Ni.js";var o=(E=>(E[E.TEXT=1]="TEXT",E[E.CLASS=2]="CLASS",E[E.STYLE=4]="STYLE",E[E.PROPS=8]="PROPS",E[E.FULL_PROPS=16]="FULL_PROPS",E[E.HYDRATE_EVENTS=32]="HYDRATE_EVENTS",E[E.STABLE_FRAGMENT=64]="STABLE_FRAGMENT",E[E.KEYED_FRAGMENT=128]="KEYED_FRAGMENT",E[E.UNKEYED_FRAGMENT=256]="UNKEYED_FRAGMENT",E[E.NEED_PATCH=512]="NEED_PATCH",E[E.DYNAMIC_SLOTS=1024]="DYNAMIC_SLOTS",E[E.HOISTED=-1]="HOISTED",E[E.BAIL=-2]="BAIL",E))(o||{});const A=E=>{const _=S(E)?E:[E],e=[];return _.forEach(T=>{var r;S(T)?e.push(...A(T)):N(T)&&((r=T.component)!=null&&r.subTree)?e.push(T,...A(T.component.subTree)):N(T)&&S(T.children)?e.push(...A(T.children)):e.push(T)}),e};export{o as P,A as f}; 2 | -------------------------------------------------------------------------------- /themes/2023/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | {{title}} 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /themes/2024/assets/AdminLayout-BA8HwEVA.css: -------------------------------------------------------------------------------- 1 | .switch{position:relative;display:inline-block;width:60px;height:34px}.switch input{opacity:0;width:0;height:0}.slider{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background-color:#e5e7eb;transition:.4s}.dark .slider{background-color:#4b5563}input:checked+.slider{background-color:#4f46e5}.dark input:checked+.slider{background-color:#4f46e5}.slider:before{position:absolute;content:"";height:26px;width:26px;left:4px;bottom:4px;background-color:#fff;transition:.4s}.dark .slider:before{background-color:#e5e7eb}.slider.round{border-radius:34px}.slider.round:before{border-radius:50%}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.animate-spin-slow{animation:spin 8s linear infinite}.transition-colors{transition-property:background-color,border-color,color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.3s}.custom-scrollbar::-webkit-scrollbar{width:8px}.custom-scrollbar::-webkit-scrollbar-track{background:transparent}.custom-scrollbar::-webkit-scrollbar-thumb{background-color:#cbd5e0;border-radius:4px}:is():hover{background-color:#a0aec0}.custom-scrollbar :deep(.dark &::-webkit-scrollbar-thumb){background-color:#4a5568}.custom-scrollbar :deep(.dark &::-webkit-scrollbar-thumb):hover{background-color:#2d3748}.space-y-6{margin-bottom:5rem} 2 | -------------------------------------------------------------------------------- /themes/2024/assets/AdminLayout-DDCPshEE.js: -------------------------------------------------------------------------------- 1 | import{c as i,d as k,r as h,o as u,I as v,a as x,b as t,n as o,h as e,i as w,g as n,X as _,F as M,q as C,x as z,z as L,J as B,s as D,K as F,t as I,e as d}from"./index-CgqZQa96.js";import{B as j}from"./box-DBrNLmg8.js";/** 2 | * @license lucide-vue-next v0.445.0 - ISC 3 | * 4 | * This source code is licensed under the ISC license. 5 | * See the LICENSE file in the root directory of this source tree. 6 | */const S=i("CogIcon",[["path",{d:"M12 20a8 8 0 1 0 0-16 8 8 0 0 0 0 16Z",key:"sobvz5"}],["path",{d:"M12 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z",key:"11i496"}],["path",{d:"M12 2v2",key:"tus03m"}],["path",{d:"M12 22v-2",key:"1osdcq"}],["path",{d:"m17 20.66-1-1.73",key:"eq3orb"}],["path",{d:"M11 10.27 7 3.34",key:"16pf9h"}],["path",{d:"m20.66 17-1.73-1",key:"sg0v6f"}],["path",{d:"m3.34 7 1.73 1",key:"1ulond"}],["path",{d:"M14 12h8",key:"4f43i9"}],["path",{d:"M2 12h2",key:"1t8f8n"}],["path",{d:"m20.66 7-1.73 1",key:"1ow05n"}],["path",{d:"m3.34 17 1.73-1",key:"nuk764"}],["path",{d:"m17 3.34-1 1.73",key:"2wel8s"}],["path",{d:"m11 13.73-4 6.93",key:"794ttg"}]]);/** 7 | * @license lucide-vue-next v0.445.0 - ISC 8 | * 9 | * This source code is licensed under the ISC license. 10 | * See the LICENSE file in the root directory of this source tree. 11 | */const V=i("FolderIcon",[["path",{d:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z",key:"1kt360"}]]);/** 12 | * @license lucide-vue-next v0.445.0 - ISC 13 | * 14 | * This source code is licensed under the ISC license. 15 | * See the LICENSE file in the root directory of this source tree. 16 | */const q=i("LayoutDashboardIcon",[["rect",{width:"7",height:"9",x:"3",y:"3",rx:"1",key:"10lvy0"}],["rect",{width:"7",height:"5",x:"14",y:"3",rx:"1",key:"16une8"}],["rect",{width:"7",height:"9",x:"14",y:"12",rx:"1",key:"1hutg5"}],["rect",{width:"7",height:"5",x:"3",y:"16",rx:"1",key:"ldoo1y"}]]);/** 17 | * @license lucide-vue-next v0.445.0 - ISC 18 | * 19 | * This source code is licensed under the ISC license. 20 | * See the LICENSE file in the root directory of this source tree. 21 | */const E=i("MenuIcon",[["line",{x1:"4",x2:"20",y1:"12",y2:"12",key:"1e0a9i"}],["line",{x1:"4",x2:"20",y1:"6",y2:"6",key:"1owob3"}],["line",{x1:"4",x2:"20",y1:"18",y2:"18",key:"yk5zj1"}]]),N={class:"flex items-center"},R={class:"rounded-full bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500 p-1 animate-spin-slow"},Z={class:"flex-1 overflow-y-auto"},A={class:"p-4 space-y-2"},H=["onClick"],J={class:"flex-1 flex flex-col min-h-screen"},K={class:"flex items-center justify-between h-16 px-4"},$=k({__name:"AdminLayout",setup(O){const c=L(),a=w("isDarkMode"),p=[{id:"Dashboard",name:"仪表盘",icon:q,redirect:"/admin/dashboard"},{id:"FileManage",name:"文件管理",icon:V,redirect:"/admin/files"},{id:"Settings",name:"系统设置",icon:S,redirect:"/admin/settings"}],s=h(!0),y=()=>{s.value=!s.value},l=()=>{window.innerWidth>=1024?s.value=!0:s.value=!1};u(()=>{l(),window.addEventListener("resize",l)}),v(()=>{window.removeEventListener("resize",l)});const m=h({page:1,size:10,total:0}),b=async()=>{try{m.value.total=85}catch(g){console.error("加载文件列表失败:",g)}};return u(()=>{b()}),(g,T)=>{const f=z("router-view");return d(),x("div",{class:o(["min-h-screen flex flex-col lg:flex-row transition-colors duration-300",[e(a)?"bg-gray-900":"bg-gray-50"]])},[t("aside",{class:o(["fixed inset-y-0 left-0 z-50 w-64 transform transition-all duration-300 ease-in-out lg:relative lg:translate-x-0 border-r",[e(a)?"bg-gray-800 bg-opacity-90 backdrop-filter backdrop-blur-xl border-gray-700":"bg-white border-gray-200",{"-translate-x-full":!s.value}]])},[t("div",{class:o(["flex items-center justify-between h-16 px-4 border-b",[e(a)?"border-gray-700":"border-gray-200"]])},[t("div",N,[t("div",R,[t("div",{class:o(["rounded-full p-1",[e(a)?"bg-gray-800":"bg-white"]])},[n(e(j),{class:o(["w-6 h-6",[e(a)?"text-indigo-400":"text-indigo-600"]])},null,8,["class"])],2)]),t("h1",{class:o(["ml-2 text-xl font-semibold",[e(a)?"text-white":"text-gray-800"]])}," FileCodeBox ",2)]),t("button",{onClick:y,class:"lg:hidden"},[n(e(_),{class:o(["w-6 h-6",[e(a)?"text-gray-400":"text-gray-600"]])},null,8,["class"])])],2),t("nav",Z,[t("ul",A,[(d(),x(M,null,C(p,r=>t("li",{key:r.id},[t("a",{onClick:U=>e(c).push(r.redirect),class:o(["flex items-center p-2 rounded-lg transition-colors duration-200",[e(c).currentRoute.value.name===r.id?e(a)?"bg-indigo-900 text-indigo-400":"bg-indigo-100 text-indigo-600":e(a)?"text-gray-400 hover:bg-gray-700":"text-gray-600 hover:bg-gray-100"]])},[(d(),B(F(r.icon),{class:"w-5 h-5 mr-3"})),D(" "+I(r.name),1)],10,H)])),64))])])],2),t("div",J,[t("header",{class:o(["shadow-md border-b transition-colors duration-300",[e(a)?"bg-gray-800 border-gray-700":"bg-white border-gray-200"]])},[t("div",K,[t("button",{onClick:y,class:"lg:hidden"},[n(e(E),{class:o(["w-6 h-6",[e(a)?"text-gray-400":"text-gray-600"]])},null,8,["class"])])])],2),t("main",{class:o(["flex-1 p-6 overflow-y-auto transition-colors duration-300",[e(a)?"bg-gray-900":"bg-gray-50"]])},[n(f)],2)])],2)}}});export{$ as default}; 22 | -------------------------------------------------------------------------------- /themes/2024/assets/DashboardView-Cp5dD9KH.js: -------------------------------------------------------------------------------- 1 | import{c as x,d as m,H as b,o as h,a as f,b as t,n as a,h as e,i as v,t as n,g,s as w,A as _,e as F}from"./index-CgqZQa96.js";import{F as k}from"./file-oKDOd1HK.js";import{H as C}from"./hard-drive-DZ7L4y0D.js";/** 2 | * @license lucide-vue-next v0.445.0 - ISC 3 | * 4 | * This source code is licensed under the ISC license. 5 | * See the LICENSE file in the root directory of this source tree. 6 | */const U=x("ActivityIcon",[["path",{d:"M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2",key:"169zse"}]]);/** 7 | * @license lucide-vue-next v0.445.0 - ISC 8 | * 9 | * This source code is licensed under the ISC license. 10 | * See the LICENSE file in the root directory of this source tree. 11 | */const z=x("UsersIcon",[["path",{d:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2",key:"1yyitq"}],["circle",{cx:"9",cy:"7",r:"4",key:"nufk8"}],["path",{d:"M22 21v-2a4 4 0 0 0-3-3.87",key:"kshegd"}],["path",{d:"M16 3.13a4 4 0 0 1 0 7.75",key:"1da9ce"}]]),D={class:"p-6 h-screen overflow-y-auto custom-scrollbar"},M={class:"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8"},S={class:"flex items-center justify-between"},B={class:"flex items-center justify-between"},$={class:"flex items-center justify-between"},j={class:"flex items-center justify-between"},H={class:"text-sm mt-1"},N=m({__name:"DashboardView",setup(A){const s=v("isDarkMode"),o=b({totalFiles:0,storageUsed:0,yesterdayCount:0,todayCount:0,yesterdaySize:0,todaySize:0,sysUptime:0}),u=l=>{const i=new Date().getTime()-l,d=Math.floor(i/(24*60*60*1e3)),c=Math.floor(i%(24*60*60*1e3)/(60*60*1e3));return`${d}天${c}小时`},y=l=>{const r=parseInt(l)/1024,i=r/1024,d=i/1024,c=d/1024;return c>1?`${c.toFixed(2)}TB`:d>1?`${d.toFixed(2)}GB`:i>1?`${i.toFixed(2)}MB`:r>1?`${r.toFixed(2)}KB`:`${l}B`},p=async()=>{const l=await _.get("admin/dashboard");o.totalFiles=l.detail.totalFiles,o.storageUsed=y(l.detail.storageUsed),o.yesterdaySize=y(l.detail.yesterdaySize),o.todaySize=y(l.detail.todaySize),o.yesterdayCount=l.detail.yesterdayCount,o.todayCount=l.detail.todayCount,o.sysUptime=u(l.detail.sysUptime)};return h(()=>{p()}),(l,r)=>(F(),f("div",D,[t("h2",{class:a(["text-2xl font-bold mb-6",[e(s)?"text-white":"text-gray-800"]])}," 仪表盘 ",2),t("div",M,[t("div",{class:a(["p-6 rounded-lg shadow-md transition-colors duration-300",[e(s)?"bg-gray-800 bg-opacity-70":"bg-white"]])},[t("div",S,[t("div",null,[t("p",{class:a(["text-sm",[e(s)?"text-gray-400":"text-gray-600"]])}," 总文件数 ",2),t("h3",{class:a(["text-2xl font-bold mt-1",[e(s)?"text-white":"text-gray-800"]])},n(o.totalFiles),3)]),t("div",{class:a(["p-3 rounded-full",[e(s)?"bg-indigo-900":"bg-indigo-100"]])},[g(e(k),{class:a(["w-6 h-6",[e(s)?"text-indigo-400":"text-indigo-600"]])},null,8,["class"])],2)]),t("p",{class:a(["text-sm mt-2",[e(s)?"text-green-400":"text-green-600"]])},[t("span",{class:a([e(s)?"text-gray-400":"text-gray-600"])},"昨天:",2),t("span",null,n(o.yesterdayCount),1),t("span",{class:a(["ml-2",[e(s)?"text-gray-400":"text-gray-600"]])},"今天:",2),t("span",null,n(o.todayCount),1)],2)],2),t("div",{class:a(["p-6 rounded-lg shadow-md transition-colors duration-300",[e(s)?"bg-gray-800 bg-opacity-70":"bg-white"]])},[t("div",B,[t("div",null,[t("p",{class:a(["text-sm",[e(s)?"text-gray-400":"text-gray-600"]])}," 存储空间 ",2),t("h3",{class:a(["text-2xl font-bold mt-1",[e(s)?"text-white":"text-gray-800"]])},n(o.storageUsed),3)]),t("div",{class:a(["p-3 rounded-full",[e(s)?"bg-purple-900":"bg-purple-100"]])},[g(e(C),{class:a(["w-6 h-6",[e(s)?"text-purple-400":"text-purple-600"]])},null,8,["class"])],2)]),t("p",{class:a(["text-sm mt-2",[e(s)?"text-green-400":"text-green-600"]])},[t("span",{class:a([e(s)?"text-gray-400":"text-gray-600"])},"昨天:",2),t("span",null,n(o.yesterdaySize),1),t("span",{class:a(["ml-2",[e(s)?"text-gray-400":"text-gray-600"]])},"今天:",2),t("span",null,n(o.todaySize),1)],2)],2),t("div",{class:a(["p-6 rounded-lg shadow-md transition-colors duration-300",[e(s)?"bg-gray-800 bg-opacity-70":"bg-white"]])},[t("div",$,[t("div",null,[t("p",{class:a(["text-sm",[e(s)?"text-gray-400":"text-gray-600"]])}," 活跃用户 ",2),t("h3",{class:a(["text-2xl font-bold mt-1",[e(s)?"text-white":"text-gray-800"]])}," 25 ",2)]),t("div",{class:a(["p-3 rounded-full",[e(s)?"bg-green-900":"bg-green-100"]])},[g(e(z),{class:a(["w-6 h-6",[e(s)?"text-green-400":"text-green-600"]])},null,8,["class"])],2)]),t("p",{class:a(["text-sm mt-2",[e(s)?"text-red-400":"text-red-600"]])},[r[0]||(r[0]=t("span",null,"↓ 5% ",-1)),t("span",{class:a([e(s)?"text-gray-400":"text-gray-600"])},"较上周",2)],2)],2),t("div",{class:a(["p-6 rounded-lg shadow-md transition-colors duration-300",[e(s)?"bg-gray-800 bg-opacity-70":"bg-white"]])},[t("div",j,[t("div",null,[t("p",{class:a(["text-sm",[e(s)?"text-gray-400":"text-gray-600"]])}," 系统状态 ",2),t("h3",{class:a(["text-2xl font-bold mt-1",[e(s)?"text-white":"text-gray-800"]])}," 正常 ",2)]),t("div",{class:a(["p-3 rounded-full",[e(s)?"bg-blue-900":"bg-blue-100"]])},[g(e(U),{class:a(["w-6 h-6",[e(s)?"text-blue-400":"text-blue-600"]])},null,8,["class"])],2)]),t("p",{class:a(["text-sm mt-2",[e(s)?"text-gray-400":"text-gray-600"]])}," 服务器运行时间: "+n(o.sysUptime),3)],2)]),t("div",{class:a(["mt-auto text-center py-4",[e(s)?"text-gray-400":"text-gray-600"]])},[r[2]||(r[2]=t("p",{class:"text-sm"}," 版本 v2.0.3 更新时间:2025-03-03 ",-1)),t("p",H,[w(" © "+n(new Date().getFullYear())+" ",1),r[1]||(r[1]=t("a",{href:"https://github.com/vastsa/FileCodeBox"},"FileCodeBox",-1))])],2)]))}});export{N as default}; 12 | -------------------------------------------------------------------------------- /themes/2024/assets/DingTalk-CT5a5scH.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2024/assets/DingTalk-CT5a5scH.ttf -------------------------------------------------------------------------------- /themes/2024/assets/FileManageView-DrjnVkAt.css: -------------------------------------------------------------------------------- 1 | .animate-modal-scale{animation:modal-scale .3s ease-out}@keyframes modal-scale{0%{transform:scale(.95);opacity:0}to{transform:scale(1);opacity:1}}input:focus{transform:scale(1.002)} 2 | -------------------------------------------------------------------------------- /themes/2024/assets/LoginView-CUDwFSJJ.css: -------------------------------------------------------------------------------- 1 | @keyframes spin-66eb7914{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.animate-spin-slow[data-v-66eb7914]{animation:spin-66eb7914 8s linear infinite}.fade-enter-active[data-v-66eb7914],.fade-leave-active[data-v-66eb7914]{transition:opacity .3s ease}.fade-enter-from[data-v-66eb7914],.fade-leave-to[data-v-66eb7914]{opacity:0}input[data-v-66eb7914]:focus{box-shadow:0 0 15px #6366f14d}button[data-v-66eb7914]:active:not(:disabled){transform:scale(.98)}.cyber-grid[data-v-66eb7914]{background-image:linear-gradient(transparent 95%,#6366f11a 50%),linear-gradient(90deg,transparent 95%,rgba(99,102,241,.1) 50%);background-size:30px 30px;width:100%;height:100%;position:absolute;opacity:.5}.floating-particles[data-v-66eb7914]{position:absolute;width:100%;height:100%;background:radial-gradient(circle at center,transparent 0%,transparent 100%);filter:url(#gooey)}.floating-particles[data-v-66eb7914]:before,.floating-particles[data-v-66eb7914]:after{content:"";position:absolute;width:100%;height:100%;background-image:radial-gradient(circle at center,rgba(99,102,241,.1) 0%,transparent 50%);animation:float-66eb7914 20s infinite linear}.floating-particles[data-v-66eb7914]:after{animation-delay:-10s;opacity:.5}@keyframes float-66eb7914{0%{transform:translate(0) scale(1)}50%{transform:translate(50px,50px) scale(1.5)}to{transform:translate(0) scale(1)}}button[data-v-66eb7914]:hover:not(:disabled){box-shadow:0 0 25px #6366f180}.fade-enter-active[data-v-66eb7914],.fade-leave-active[data-v-66eb7914]{transition:all .5s cubic-bezier(.4,0,.2,1)} 2 | -------------------------------------------------------------------------------- /themes/2024/assets/LoginView-CZDEkhdS.js: -------------------------------------------------------------------------------- 1 | import{G as y,r as u,d as b,u as v,a as w,b as e,n as l,h as o,i as x,g as h,j as k,m as S,v as A,s as V,t as D,z as j,e as B,A as P,_}from"./index-CgqZQa96.js";import{B as z}from"./box-DBrNLmg8.js";const M=y("adminData",()=>{const d=u(localStorage.getItem("adminPassword")||"");function n(a){d.value=a,localStorage.setItem("token",a)}return{adminPassword:d,updateAdminPwd:n}}),I={class:"mx-auto h-16 w-16 relative"},L={class:"rounded-md shadow-sm -space-y-px"},N=["disabled"],T=b({__name:"LoginView",setup(d){const n=v(),a=u(""),i=u(!1),s=x("isDarkMode"),c=M(),p=()=>{let r=!0;return a.value?a.value.length<6&&(n.showAlert("密码长度至少为6位","error"),r=!1):(n.showAlert("无效的密码","error"),r=!1),r},m=j(),f=async()=>{if(p()){P.post("/admin/login",{password:a.value}).then(r=>{c.updateAdminPwd(r.detail.token),m.push("/admin")}).catch(r=>{n.showAlert(r.response.data.detail,"error")}),i.value=!0;try{await new Promise(r=>setTimeout(r,2e3))}catch{}finally{i.value=!1}}};return(r,t)=>(B(),w("div",{class:l(["min-h-screen flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8 transition-colors duration-200 relative overflow-hidden",o(s)?"bg-gray-900":"bg-gray-50"])},[t[6]||(t[6]=e("div",{class:"absolute inset-0 z-0"},[e("div",{class:"cyber-grid"}),e("div",{class:"floating-particles"})],-1)),e("div",{class:l(["max-w-md w-full space-y-8 backdrop-blur-lg bg-opacity-20 p-8 rounded-xl border border-opacity-20",[o(s)?"bg-gray-800 border-gray-600":"bg-white/70 border-gray-200"]])},[e("div",null,[e("div",I,[t[1]||(t[1]=e("div",{class:"absolute inset-0 bg-gradient-to-r from-cyan-500 via-purple-500 to-pink-500 rounded-full animate-spin-slow"},null,-1)),t[2]||(t[2]=e("div",{class:"absolute -inset-2 bg-gradient-to-r from-cyan-500 via-purple-500 to-pink-500 rounded-full opacity-50 blur-md animate-pulse"},null,-1)),e("div",{class:l(["absolute inset-1 rounded-full flex items-center justify-center",o(s)?"bg-gray-800":"bg-white"])},[h(o(z),{class:l(["h-8 w-8",o(s)?"text-cyan-400":"text-cyan-600"])},null,8,["class"])],2)]),e("h2",{class:l(["mt-6 text-center text-3xl font-extrabold",o(s)?"text-white":"text-gray-900"])}," 登录 ",2)]),e("form",{class:"mt-8 space-y-6",onSubmit:k(f,["prevent"])},[t[5]||(t[5]=e("input",{type:"hidden",name:"remember",value:"true"},null,-1)),e("div",L,[e("div",null,[t[3]||(t[3]=e("label",{for:"password",class:"sr-only"},"密码",-1)),S(e("input",{id:"password",name:"password",type:"password",autocomplete:"current-password",required:"","onUpdate:modelValue":t[0]||(t[0]=g=>a.value=g),class:l(["appearance-none rounded-t-md relative block w-full px-4 py-3 border transition-all duration-200 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-cyan-500 focus:border-cyan-500 focus:z-10 sm:text-sm backdrop-blur-sm",o(s)?"bg-gray-800/50 border-gray-600 text-white placeholder-gray-400 hover:border-gray-500":"bg-white/50 border-gray-300 text-gray-900 hover:border-gray-400"]),placeholder:"密码"},null,2),[[A,a.value]])])]),e("div",null,[e("button",{type:"submit",class:l(["group relative w-full flex justify-center py-3 px-4 border border-transparent text-sm font-medium rounded-md text-white transition-all duration-300 transform hover:scale-[1.02] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-cyan-500 shadow-lg hover:shadow-cyan-500/50",o(s)?"bg-gradient-to-r from-cyan-500 to-purple-500 hover:from-cyan-600 hover:to-purple-600":"bg-gradient-to-r from-cyan-600 to-purple-600 hover:from-cyan-700 hover:to-purple-700",i.value?"opacity-75 cursor-not-allowed":""]),disabled:i.value},[t[4]||(t[4]=e("span",{class:"absolute left-0 inset-y-0 flex items-center pl-3"},null,-1)),V(" "+D(i.value?"登录中...":"登录"),1)],10,N)])],32)],2)],2))}}),E=_(T,[["__scopeId","data-v-66eb7914"]]);export{E as default}; 2 | -------------------------------------------------------------------------------- /themes/2024/assets/RetrievewFileView-Csu6yejH.css: -------------------------------------------------------------------------------- 1 | @keyframes blob-5e666f3a{0%,to{transform:translate(0) scale(1)}25%{transform:translate(20px,-50px) scale(1.1)}50%{transform:translate(-20px,20px) scale(.9)}75%{transform:translate(50px,50px) scale(1.05)}}.animate-blob-1[data-v-5e666f3a]{animation:blob-5e666f3a 25s infinite}.animate-blob-2[data-v-5e666f3a]{animation:blob-5e666f3a 30s infinite}.animate-blob-3[data-v-5e666f3a]{animation:blob-5e666f3a 35s infinite}.animate-blob-4[data-v-5e666f3a]{animation:blob-5e666f3a 40s infinite}.animate-spin-slow[data-v-5e666f3a]{animation:spin-5e666f3a 8s linear infinite}@keyframes spin-5e666f3a{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.fade-enter-active[data-v-5e666f3a],.fade-leave-active[data-v-5e666f3a]{transition:opacity .3s ease}.fade-enter-from[data-v-5e666f3a],.fade-leave-to[data-v-5e666f3a]{opacity:0}.list-enter-active[data-v-5e666f3a],.list-leave-active[data-v-5e666f3a]{transition:all .5s ease}.list-enter-from[data-v-5e666f3a],.list-leave-to[data-v-5e666f3a]{opacity:0;transform:translate(30px)}.drawer-enter-active[data-v-5e666f3a],.drawer-leave-active[data-v-5e666f3a]{transition:transform .3s ease}.drawer-enter-from[data-v-5e666f3a],.drawer-leave-to[data-v-5e666f3a]{transform:translate(100%)}.w-97-100[data-v-5e666f3a]{width:97%}[data-v-5e666f3a] .prose{text-align:left}[data-v-5e666f3a] .prose h1,[data-v-5e666f3a] .prose h2,[data-v-5e666f3a] .prose h3,[data-v-5e666f3a] .prose h4,[data-v-5e666f3a] .prose h5,[data-v-5e666f3a] .prose h6{color:#4f46e5}@media (prefers-color-scheme: dark){[data-v-5e666f3a] .prose h1,[data-v-5e666f3a] .prose h2,[data-v-5e666f3a] .prose h3,[data-v-5e666f3a] .prose h4,[data-v-5e666f3a] .prose h5,[data-v-5e666f3a] .prose h6{color:#818cf8}}@media (min-width: 640px){.sm\:w-120[data-v-5e666f3a]{width:30rem}}.custom-scrollbar[data-v-5e666f3a]{scrollbar-width:thin;scrollbar-color:rgba(156,163,175,.3) transparent}.custom-scrollbar[data-v-5e666f3a]::-webkit-scrollbar{width:6px;height:6px}.custom-scrollbar[data-v-5e666f3a]::-webkit-scrollbar-track{background:transparent}.custom-scrollbar[data-v-5e666f3a]::-webkit-scrollbar-thumb{background-color:#9ca3af4d;border-radius:3px;-webkit-transition:background-color .3s;transition:background-color .3s}.custom-scrollbar[data-v-5e666f3a]::-webkit-scrollbar-thumb:hover{background-color:#9ca3af80}[data-v-5e666f3a] [class*=dark] .custom-scrollbar{scrollbar-color:rgba(75,85,99,.3) transparent}[data-v-5e666f3a] [class*=dark] .custom-scrollbar::-webkit-scrollbar-thumb{background-color:#4b55634d}[data-v-5e666f3a] [class*=dark] .custom-scrollbar::-webkit-scrollbar-thumb:hover{background-color:#4b556380} 2 | -------------------------------------------------------------------------------- /themes/2024/assets/SendFileView-XxX3DVxA.css: -------------------------------------------------------------------------------- 1 | .border-progress-container[data-v-2fbf5085]{position:relative;width:100%;height:100%}.border-progress-canvas[data-v-2fbf5085]{position:absolute;top:0;left:0;width:100%;height:100%;transition:all .3s ease}.fade-enter-active[data-v-aaf18f62],.fade-leave-active[data-v-aaf18f62]{transition:opacity .3s ease,transform .3s ease}.fade-enter-from[data-v-aaf18f62],.fade-leave-to[data-v-aaf18f62]{opacity:0;transform:translateY(10px)}@media (min-width: 640px){.sm\:w-120[data-v-aaf18f62]{width:30rem}}.fade-enter-to[data-v-aaf18f62],.fade-leave-from[data-v-aaf18f62]{opacity:1;transform:translateY(0)}.drawer-enter-active[data-v-aaf18f62],.drawer-leave-active[data-v-aaf18f62]{transition:transform .3s ease}.drawer-enter-from[data-v-aaf18f62],.drawer-leave-to[data-v-aaf18f62]{transform:translate(100%)}.list-enter-active[data-v-aaf18f62],.list-leave-active[data-v-aaf18f62]{transition:all .5s ease}.list-enter-from[data-v-aaf18f62],.list-leave-to[data-v-aaf18f62]{opacity:0;transform:translate(30px)}select option[data-v-aaf18f62]{padding:8px;margin:4px;border-radius:6px}select option[data-v-aaf18f62]:checked{background:linear-gradient(to right,#6366f180,#a855f780)!important;color:#fff!important}.dark select option[data-v-aaf18f62]:checked{background:linear-gradient(to right,#6366f1b3,#a855f7b3)!important}select option[data-v-aaf18f62]:hover{background-color:#6366f11a}.dark select option[data-v-aaf18f62]:hover{background-color:#6366f133} 2 | -------------------------------------------------------------------------------- /themes/2024/assets/box-DBrNLmg8.js: -------------------------------------------------------------------------------- 1 | import{c as a}from"./index-CgqZQa96.js";/** 2 | * @license lucide-vue-next v0.445.0 - ISC 3 | * 4 | * This source code is licensed under the ISC license. 5 | * See the LICENSE file in the root directory of this source tree. 6 | */const o=a("BoxIcon",[["path",{d:"M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z",key:"hh9hay"}],["path",{d:"m3.3 7 8.7 5 8.7-5",key:"g66t2b"}],["path",{d:"M12 22V12",key:"d0xqtd"}]]);export{o as B}; 7 | -------------------------------------------------------------------------------- /themes/2024/assets/file-oKDOd1HK.js: -------------------------------------------------------------------------------- 1 | import{c as a}from"./index-CgqZQa96.js";/** 2 | * @license lucide-vue-next v0.445.0 - ISC 3 | * 4 | * This source code is licensed under the ISC license. 5 | * See the LICENSE file in the root directory of this source tree. 6 | */const t=a("FileIcon",[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z",key:"1rqfz7"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}]]);export{t as F}; 7 | -------------------------------------------------------------------------------- /themes/2024/assets/hard-drive-DZ7L4y0D.js: -------------------------------------------------------------------------------- 1 | import{c as e}from"./index-CgqZQa96.js";/** 2 | * @license lucide-vue-next v0.445.0 - ISC 3 | * 4 | * This source code is licensed under the ISC license. 5 | * See the LICENSE file in the root directory of this source tree. 6 | */const a=e("HardDriveIcon",[["line",{x1:"22",x2:"2",y1:"12",y2:"12",key:"1y58io"}],["path",{d:"M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z",key:"oot6mr"}],["line",{x1:"6",x2:"6.01",y1:"16",y2:"16",key:"sgf278"}],["line",{x1:"10",x2:"10.01",y1:"16",y2:"16",key:"1l4acy"}]]);export{a as H}; 7 | -------------------------------------------------------------------------------- /themes/2024/assets/logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vastsa/FileCodeBox/9a80d7a9e0bcdd9012527d543d8e1e0f7fdb0258/themes/2024/assets/logo_small.png -------------------------------------------------------------------------------- /themes/2024/assets/trash-cjXYkUOS.js: -------------------------------------------------------------------------------- 1 | import{c}from"./index-CgqZQa96.js";/** 2 | * @license lucide-vue-next v0.445.0 - ISC 3 | * 4 | * This source code is licensed under the ISC license. 5 | * See the LICENSE file in the root directory of this source tree. 6 | */const e=c("TrashIcon",[["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6",key:"4alrt4"}],["path",{d:"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2",key:"v07s0e"}]]);export{e as T}; 7 | -------------------------------------------------------------------------------- /themes/2024/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | {{title}} 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | --------------------------------------------------------------------------------