Compare commits

..

11 Commits

Author SHA1 Message Date
Henrique Dias
70d59ec03e
chore(release): 2.34.1 2025-06-29 11:28:57 +02:00
Henrique Dias
bf37f88c32
fix: passthrough the minimum password length (#5236) 2025-06-29 11:28:32 +02:00
Foxy Hunter
7354eb6cf9
fix: exclude to-be-moved folder from move dialog (#5235) 2025-06-29 11:23:06 +02:00
Henrique Dias
10684e5390
docs: bring the maintenance warning higher in the page 2025-06-29 10:13:39 +02:00
Henrique Dias
58fe817349
docs: add link to contributing and license in readme 2025-06-29 10:13:01 +02:00
Henrique Dias
d8472e767b
chore(release): 2.34.0 2025-06-29 09:54:35 +02:00
Henrique Dias
8700cb30ff
chore: reuse docker flags 2025-06-29 09:51:44 +02:00
manx98
93c4b2e03c
fix: abort ongoing requests when changing pages (#3927) 2025-06-29 09:38:03 +02:00
Henrique Dias
2d1a82b73f
docs: improvements to building and docs (#5234) 2025-06-29 09:28:39 +02:00
dependabot[bot]
5a07291306
build(deps): bump brace-expansion from 1.1.11 to 1.1.12 in /tools (#5228)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-29 09:20:44 +02:00
transifex-integration[bot]
09f679fae4
feat: Translate frontend/src/i18n/en.json in fa (#5233)
Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-06-29 09:17:05 +02:00
50 changed files with 100320 additions and 226 deletions

View File

@ -1,7 +1,10 @@
name: Build Site Image
name: Build Site
on:
pull_request:
paths:
- 'www'
- '*.md'
jobs:
build:
@ -13,11 +16,5 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Build
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile.site
platforms: linux/amd64,linux/arm64
push: false
- name: Build site
run: make site

View File

@ -1,4 +1,4 @@
name: Build and Push Site Image
name: Build and Deploy Site
on:
push:
@ -6,39 +6,6 @@ on:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Log in to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile.site
push: true
platforms: linux/amd64,linux/arm64
tags: |
filebrowser/site:latest
ghcr.io/filebrowser/site:latest
deploy:
permissions:
contents: read
@ -61,5 +28,5 @@ jobs:
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy site/public/site --project-name=${{ secrets.CLOUDFLARE_PROJECT_NAME }}
command: pages deploy www/public --project-name=${{ secrets.CLOUDFLARE_PROJECT_NAME }}
gitHubToken: ${{ secrets.GITHUB_TOKEN }}

1
.gitignore vendored
View File

@ -42,4 +42,3 @@ build/
default.nix
Dockerfile.dev
site/public/

View File

@ -2,6 +2,46 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [2.34.1](https://github.com/filebrowser/filebrowser/compare/v2.34.0...v2.34.1) (2025-06-29)
### Bug Fixes
* exclude to-be-moved folder from move dialog ([#5235](https://github.com/filebrowser/filebrowser/issues/5235)) ([7354eb6](https://github.com/filebrowser/filebrowser/commit/7354eb6cf966244141277c2808988855c004f908))
* passthrough the minimum password length ([#5236](https://github.com/filebrowser/filebrowser/issues/5236)) ([bf37f88](https://github.com/filebrowser/filebrowser/commit/bf37f88c32222ad9c186482bb97338a9c9b4a93c))
## [2.34.0](https://github.com/filebrowser/filebrowser/compare/v2.33.10...v2.34.0) (2025-06-29)
### Features
* Translate frontend/src/i18n/en.json in fa ([0acd69c](https://github.com/filebrowser/filebrowser/commit/0acd69c537ce2909ff62c4bb6980982524ece221))
* Translate frontend/src/i18n/en.json in fa ([#5233](https://github.com/filebrowser/filebrowser/issues/5233)) ([09f679f](https://github.com/filebrowser/filebrowser/commit/09f679fae43398f5b87d21acc9d974d4d053392f))
* update translations for project File Browser ([#5226](https://github.com/filebrowser/filebrowser/issues/5226)) ([a5ea2a2](https://github.com/filebrowser/filebrowser/commit/a5ea2a266bef619d1c4322266d1aa7d397d2c856))
### Bug Fixes
* abort ongoing requests when changing pages ([#3927](https://github.com/filebrowser/filebrowser/issues/3927)) ([93c4b2e](https://github.com/filebrowser/filebrowser/commit/93c4b2e03c5176da01a7e00a03c03ffcce279bc8))
* add configurable minimum password length ([#5225](https://github.com/filebrowser/filebrowser/issues/5225)) ([464b644](https://github.com/filebrowser/filebrowser/commit/464b644adf22a2178414a6f1e4fa286276de81d2))
* do not expose the name of the root directory ([#5224](https://github.com/filebrowser/filebrowser/issues/5224)) ([0892559](https://github.com/filebrowser/filebrowser/commit/089255997a653c284cd4249990b58bed00086c61))
* Graceful shutdown ([8230eb7](https://github.com/filebrowser/filebrowser/commit/8230eb7ab51ccbd00b03f5b9d6964fa4aae331d4))
### Reverts
* Revert "docs: change cloudflare environment (#5231)" (#5232) ([9e273cd](https://github.com/filebrowser/filebrowser/commit/9e273cd9475d57b9500034e8b341ff8b620bcab8)), closes [#5231](https://github.com/filebrowser/filebrowser/issues/5231) [#5232](https://github.com/filebrowser/filebrowser/issues/5232)
### Build
* add an arm64 target for the site image ([#5229](https://github.com/filebrowser/filebrowser/issues/5229)) ([f5e531c](https://github.com/filebrowser/filebrowser/commit/f5e531c8ae0b9b18717e184856ace0ce19beef82))
* bump golangci-lint to 2.1.6 ([1d494ff](https://github.com/filebrowser/filebrowser/commit/1d494ff3159ef939cfb4980ccde6f27df3e738b5))
* **deps:** bump brace-expansion from 1.1.11 to 1.1.12 in /tools ([#5228](https://github.com/filebrowser/filebrowser/issues/5228)) ([5a07291](https://github.com/filebrowser/filebrowser/commit/5a072913062a6b2b0e5c74a02ca7710218ed3e5e))
* **deps:** bump github.com/go-viper/mapstructure/v2 ([f32f273](https://github.com/filebrowser/filebrowser/commit/f32f27383d1fafa074f038cc873bd37b7f20ee27))
* **deps:** bump github.com/go-viper/mapstructure/v2 in /tools ([5331969](https://github.com/filebrowser/filebrowser/commit/5331969163f5ae1fd2389f665059fc9e4a98db15))
* publish docs to cloudflare pages ([#5230](https://github.com/filebrowser/filebrowser/issues/5230)) ([8861933](https://github.com/filebrowser/filebrowser/commit/8861933cf845b104e072f35e5f37d7c26097c9dc))
### [2.33.10](https://github.com/filebrowser/filebrowser/compare/v2.33.9...v2.33.10) (2025-06-26)

View File

@ -1,6 +1,6 @@
# Contributing
If you're interested in contributing to this project, this is the best place to start. Before contributing to this project, please take a bit of time to read our [Code of Conduct](code-of-conduct.md). Also, note that this project is open-source and licensed under [Apache License 2.0](../LICENSE).
If you're interested in contributing to this project, this is the best place to start. Before contributing to this project, please take a bit of time to read our [Code of Conduct](code-of-conduct.md). Also, note that this project is open-source and licensed under [Apache License 2.0](LICENSE).
## Project Structure

View File

@ -1,22 +0,0 @@
FROM squidfunk/mkdocs-material as build
WORKDIR /build
COPY site/requirements.txt /build/requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
COPY LICENSE /build/docs/LICENSE
COPY site/ /build/
COPY docs/ /build/docs/docs
COPY README.md /build/docs/index.md
RUN mkdocs build
FROM ghcr.io/umputun/reproxy
# enables automatic changelog generation by tools like Dependabot
LABEL org.opencontainers.image.source="https://github.com/filebrowser/filebrowser"
COPY --from=build /build/site /srv/site
EXPOSE 8080
USER app
ENTRYPOINT ["/srv/reproxy", "--assets.location=/srv/site", "--assets.cache=30d", "--assets.cache=text/html:30s"]

View File

@ -1,15 +0,0 @@
FROM squidfunk/mkdocs-material
# Install inotify-tools for watching file changes
RUN apk add --no-cache inotify-tools
WORKDIR /build
COPY site/ /build/
RUN pip install --no-cache-dir -r requirements.txt
# Expose the port for mkdocs serve
EXPOSE 8000
# The entrypoint will run the initial setup and then start the server.
ENTRYPOINT ["mkdocs", "serve", "-a", "0.0.0.0:8000", "--dirtyreload"]

View File

@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2018 File Browser contributors
Copyright 2018 File Browser Contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -3,6 +3,14 @@ include tools.mk
LDFLAGS += -X "$(MODULE)/version.Version=$(VERSION)" -X "$(MODULE)/version.CommitSHA=$(VERSION_HASH)"
SITE_DOCKER_FLAGS = \
-v $(CURDIR)/www:/docs \
-v $(CURDIR)/LICENSE:/docs/docs/LICENSE \
-v $(CURDIR)/SECURITY.md:/docs/docs/security.md \
-v $(CURDIR)/CHANGELOG.md:/docs/docs/changelog.md \
-v $(CURDIR)/CODE-OF-CONDUCT.md:/docs/docs/code-of-conduct.md \
-v $(CURDIR)/CONTRIBUTING.md:/docs/docs/contributing.md
## Build:
.PHONY: build
@ -55,18 +63,14 @@ bump-version: $(standard-version) ## Bump app version
.PHONY: site
site: ## Build site
@rm -rf site/public/site*
@docker rm -f spot-site
docker build -f Dockerfile.site --progress=plain -t filebrowser.site .
docker run -d --name=filebrowser-site filebrowser.site
sleep 3
docker cp "filebrowser-site":/srv/site/ site/public
docker rm -f filebrowser-site
@rm -rf www/public
docker build -f www/Dockerfile --progress=plain -t filebrowser.site www
docker run --rm $(SITE_DOCKER_FLAGS) filebrowser.site build -d "public"
.PHONY: site-serve
site-serve: ## Serve site for development
docker build -f Dockerfile.site.dev -t filebrowser.site.dev .
docker run --rm -it -p 8000:8000 -v $(CURDIR)/docs:/build/docs/docs -v $(CURDIR)/README.md:/build/docs/index.md filebrowser.site.dev
docker build -f www/Dockerfile --progress=plain -t filebrowser.site www
docker run --rm -it -p 8000:8000 $(SITE_DOCKER_FLAGS) filebrowser.site
## Help:
help: ## Show this help

View File

@ -1,17 +1,20 @@
#
<p align="center">
<img src="https://raw.githubusercontent.com/filebrowser/logo/master/banner.png" width="550"/>
</p>
![Preview](https://user-images.githubusercontent.com/5447088/50716739-ebd26700-107a-11e9-9817-14230c53efd2.gif)
[![Build](https://github.com/filebrowser/filebrowser/actions/workflows/main.yaml/badge.svg)](https://github.com/filebrowser/filebrowser/actions/workflows/main.yaml)
[![Go Report Card](https://goreportcard.com/badge/github.com/filebrowser/filebrowser?style=flat-square)](https://goreportcard.com/report/github.com/filebrowser/filebrowser)
[![Documentation](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](http://godoc.org/github.com/filebrowser/filebrowser)
[![Version](https://img.shields.io/github/release/filebrowser/filebrowser.svg?style=flat-square)](https://github.com/filebrowser/filebrowser/releases/latest)
[![Chat IRC](https://img.shields.io/badge/freenode-%23filebrowser-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23filebrowser)
[![Go Report Card](https://goreportcard.com/badge/github.com/filebrowser/filebrowser)](https://goreportcard.com/report/github.com/filebrowser/filebrowser)
[![Documentation](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/filebrowser/filebrowser)
[![Version](https://img.shields.io/github/release/filebrowser/filebrowser.svg)](https://github.com/filebrowser/filebrowser/releases/latest)
[![Chat IRC](https://img.shields.io/badge/freenode-%23filebrowser-blue.svg)](http://webchat.freenode.net/?channels=%23filebrowser)
filebrowser provides a file managing interface within a specified directory and it can be used to upload, delete, preview, rename and edit your files. It allows the creation of multiple users and each user can have its own directory. It can be used as a standalone app.
File Browser provides a file managing interface within a specified directory and it can be used to upload, delete, preview and edit your files. It is a **create-your-own-cloud**-kind of software where you can just install it on your server, direct it to a path and access your files through a nice web interface.
## Documentation
Documentation on how to install, configure, and contribute to this project is hosted at [filebrowser.org](https://filebrowser.org).
## Project Status
> [!WARNING]
>
@ -26,27 +29,10 @@ filebrowser provides a file managing interface within a specified directory and
[issues]: https://github.com/filebrowser/filebrowser/issues
[discussions]: https://github.com/filebrowser/filebrowser/discussions
## Features
File Browser is a **create-your-own-cloud-kind** of software where you can install it on a server, direct it to a path and then access your files through a nice web interface. You have many available features!
| Easy Login System | Sleek Interface | User Management |
| :----------------------: | :----------------------: | :----------------------: |
| ![](docs/assets/1.jpg) | ![](docs/assets/2.jpg) | ![](docs/assets/3.jpg) |
| File Editing | Custom Commands | Customization |
| :----------------------: | :----------------------: | :----------------------: |
| ![](docs/assets/4.jpg) | ![](docs/assets/5.jpg) | ![](docs/assets/6.jpg) |
## Install
For information on how to install File Browser, please check [installation](docs/installation.md).
## Configuration
For information on how to configure File Browser, please check [configuration](docs/configuration.md).
## Contributing
For information on how to contribute to the project, including how translations are managed, please check [contributing](docs/contributing.md).
Contributions are always welcome. To start contributing to this project, read our [guidelines](CONTRIBUTING.md) first.
## License
[Apache License 2.0](LICENSE) © File Browser Contributors

View File

@ -150,7 +150,7 @@ func (a *HookAuth) SaveUser() (*users.User, error) {
}
if u == nil {
pass, err := users.HashAndValidatePwd(a.Cred.Password, a.Settings.MinimumPasswordLength)
pass, err := users.ValidateAndHashPwd(a.Cred.Password, a.Settings.MinimumPasswordLength)
if err != nil {
return nil, err
}
@ -186,7 +186,7 @@ func (a *HookAuth) SaveUser() (*users.User, error) {
// update the password when it doesn't match the current
if p {
pass, err := users.HashAndValidatePwd(a.Cred.Password, a.Settings.MinimumPasswordLength)
pass, err := users.ValidateAndHashPwd(a.Cred.Password, a.Settings.MinimumPasswordLength)
if err != nil {
return nil, err
}

View File

@ -35,7 +35,7 @@ func (a ProxyAuth) createUser(usr users.Store, setting *settings.Settings, srv *
}
var hashedRandomPassword string
hashedRandomPassword, err = users.HashAndValidatePwd(pwd, setting.MinimumPasswordLength)
hashedRandomPassword, err = users.ValidateAndHashPwd(pwd, setting.MinimumPasswordLength)
if err != nil {
return nil, err
}

View File

@ -432,7 +432,7 @@ func quickSetup(flags *pflag.FlagSet, d pythonData) {
log.Println("Randomly generated password for user 'admin':", pwd)
password, err = users.HashAndValidatePwd(pwd, set.MinimumPasswordLength)
password, err = users.ValidateAndHashPwd(pwd, set.MinimumPasswordLength)
checkErr(err)
}

View File

@ -21,7 +21,7 @@ var usersAddCmd = &cobra.Command{
checkErr(err)
getUserDefaults(cmd.Flags(), &s.Defaults, false)
password, err := users.HashAndValidatePwd(args[1], s.MinimumPasswordLength)
password, err := users.ValidateAndHashPwd(args[1], s.MinimumPasswordLength)
checkErr(err)
user := &users.User{

View File

@ -66,7 +66,7 @@ options you want to change.`,
}
if password != "" {
user.Password, err = users.HashAndValidatePwd(password, s.MinimumPasswordLength)
user.Password, err = users.ValidateAndHashPwd(password, s.MinimumPasswordLength)
checkErr(err)
}

View File

@ -1,13 +1,16 @@
package errors
import "errors"
import (
"errors"
"fmt"
)
var (
ErrEmptyKey = errors.New("empty key")
ErrExist = errors.New("the resource already exists")
ErrNotExist = errors.New("the resource does not exist")
ErrEmptyPassword = errors.New("password is empty")
ErrShortPassword = errors.New("password is too short")
ErrEasyPassword = errors.New("password is too easy")
ErrEmptyUsername = errors.New("username is empty")
ErrEmptyRequest = errors.New("empty request")
ErrScopeIsRelative = errors.New("scope is a relative path")
@ -20,3 +23,11 @@ var (
ErrSourceIsParent = errors.New("source is parent")
ErrRootUserDeletion = errors.New("user with id 1 can't be deleted")
)
type ErrShortPassword struct {
MinimumLength uint
}
func (e ErrShortPassword) Error() string {
return fmt.Sprintf("password is too short, minimum length is %d", e.MinimumLength)
}

View File

@ -2,14 +2,22 @@ import { useAuthStore } from "@/stores/auth";
import { useLayoutStore } from "@/stores/layout";
import { baseURL } from "@/utils/constants";
import { upload as postTus, useTus } from "./tus";
import { createURL, fetchURL, removePrefix } from "./utils";
import { createURL, fetchURL, removePrefix, StatusError } from "./utils";
export async function fetch(url: string) {
export async function fetch(url: string, signal?: AbortSignal) {
url = removePrefix(url);
const res = await fetchURL(`/api/resources${url}`, { signal });
const res = await fetchURL(`/api/resources${url}`, {});
const data = (await res.json()) as Resource;
let data: Resource;
try {
data = (await res.json()) as Resource;
} catch (e) {
// Check if the error is an intentional cancellation
if (e instanceof Error && e.name === "AbortError") {
throw new StatusError("000 No connection", 0, true);
}
throw e;
}
data.url = `/files${url}`;
if (data.isDir) {
@ -205,10 +213,18 @@ export function getSubtitlesURL(file: ResourceItem) {
return file.subtitles?.map((d) => createURL("api/subtitle" + d, params));
}
export async function usage(url: string) {
export async function usage(url: string, signal: AbortSignal) {
url = removePrefix(url);
const res = await fetchURL(`/api/usage${url}`, {});
const res = await fetchURL(`/api/usage${url}`, { signal });
return await res.json();
try {
return await res.json();
} catch (e) {
// Check if the error is an intentional cancellation
if (e instanceof Error && e.name == "AbortError") {
throw new StatusError("000 No connection", 0, true);
}
throw e;
}
}

View File

@ -6,7 +6,8 @@ import { encodePath } from "@/utils/url";
export class StatusError extends Error {
constructor(
message: any,
public status?: number
public status?: number,
public is_canceled?: boolean
) {
super(message);
this.name = "StatusError";
@ -33,7 +34,11 @@ export async function fetchURL(
},
...rest,
});
} catch {
} catch (e) {
// Check if the error is an intentional cancellation
if (e instanceof Error && e.name === "AbortError") {
throw new StatusError("000 No connection", 0, true);
}
throw new StatusError("000 No connection", 0);
}

View File

@ -129,6 +129,7 @@ import {
import { files as api } from "@/api";
import ProgressBar from "@/components/ProgressBar.vue";
import prettyBytes from "pretty-bytes";
import { StatusError } from "@/api/utils.js";
const USAGE_DEFAULT = { used: "0 B", total: "0 B", usedPercentage: 0 };
@ -136,7 +137,7 @@ export default {
name: "sidebar",
setup() {
const usage = reactive(USAGE_DEFAULT);
return { usage };
return { usage, usageAbortController: new AbortController() };
},
components: {
ProgressBar,
@ -157,6 +158,9 @@ export default {
},
methods: {
...mapActions(useLayoutStore, ["closeHovers", "showHover"]),
abortOngoingFetchUsage() {
this.usageAbortController.abort();
},
async fetchUsage() {
const path = this.$route.path.endsWith("/")
? this.$route.path
@ -166,13 +170,18 @@ export default {
return Object.assign(this.usage, usageStats);
}
try {
const usage = await api.usage(path);
this.abortOngoingFetchUsage();
this.usageAbortController = new AbortController();
const usage = await api.usage(path, this.usageAbortController.signal);
usageStats = {
used: prettyBytes(usage.used, { binary: true }),
total: prettyBytes(usage.total, { binary: true }),
usedPercentage: Math.round((usage.used / usage.total) * 100),
};
} catch (error) {
if (error instanceof StatusError && error.is_canceled) {
return;
}
this.$showError(error);
}
return Object.assign(this.usage, usageStats);
@ -200,5 +209,8 @@ export default {
immediate: true,
},
},
unmounted() {
this.abortOngoingFetchUsage();
},
};
</script>

View File

@ -31,9 +31,16 @@ import { useFileStore } from "@/stores/file";
import url from "@/utils/url";
import { files } from "@/api";
import { StatusError } from "@/api/utils.js";
export default {
name: "file-list",
props: {
exclude: {
type: Array,
default: () => [],
},
},
data: function () {
return {
items: [],
@ -43,6 +50,7 @@ export default {
},
selected: null,
current: window.location.pathname,
nextAbortController: new AbortController(),
};
},
inject: ["$showError"],
@ -56,7 +64,13 @@ export default {
mounted() {
this.fillOptions(this.req);
},
unmounted() {
this.abortOngoingNext();
},
methods: {
abortOngoingNext() {
this.nextAbortController.abort();
},
fillOptions(req) {
// Sets the current path and resets
// the current items.
@ -82,6 +96,7 @@ export default {
// move options.
for (const item of req.items) {
if (!item.isDir) continue;
if (this.exclude?.includes(item.url)) continue;
this.items.push({
name: item.name,
@ -94,8 +109,17 @@ export default {
// just clicked in and fill the options with its
// content.
const uri = event.currentTarget.dataset.url;
files.fetch(uri).then(this.fillOptions).catch(this.$showError);
this.abortOngoingNext();
this.nextAbortController = new AbortController();
files
.fetch(uri, this.nextAbortController.signal)
.then(this.fillOptions)
.catch((e) => {
if (e instanceof StatusError && e.is_canceled) {
return;
}
this.$showError(e);
});
},
touchstart(event) {
const url = event.currentTarget.dataset.url;

View File

@ -8,6 +8,7 @@
<file-list
ref="fileList"
@update:selected="(val) => (dest = val)"
:exclude="excludedFolders"
tabindex="1"
/>
</div>
@ -76,6 +77,11 @@ export default {
computed: {
...mapState(useFileStore, ["req", "selected"]),
...mapState(useAuthStore, ["user"]),
excludedFolders() {
return this.selected
.filter((idx) => this.req.items[idx].isDir)
.map((idx) => this.req.items[idx].url);
},
},
methods: {
...mapActions(useLayoutStore, ["showHover", "closeHovers"]),

View File

@ -170,7 +170,7 @@
"commandRunnerHelp": "در اینجا می‌توانید دستوراتی را که در رویدادهای نامگذاری شده اجرا می‌شوند، تنظیم کنید. باید در هر خط یکی را بنویسید. متغیرهای محیطی {0} و {1} در دسترس خواهند بود و {0} نسبت به {1} هستند. برای اطلاعات بیشتر در مورد این ویژگی و متغیرهای محیطی موجود، لطفاً {2} را مطالعه کنید.",
"commandsUpdated": "دستورات ویرایش شد!",
"createUserDir": "ایجاد خودکار پوشه برای هر کاربر هنگام اضافه کردن کاربر جدید",
"minimumPasswordLength": "Minimum password length",
"minimumPasswordLength": "حداقل طول رمز عبور",
"tusUploads": "آپلودهای بخش بخش شده",
"tusUploadsHelp": "مرورگر فایل از آپلود فایل بخش بخش شده پشتیبانی می‌کند و امکان ایجاد آپلودهای فایل کارآمد، قابل اعتماد، قابل از سرگیری و بخش بخش شده را حتی در شبکه‌های غیرقابل اعتماد فراهم می‌کند.",
"tusUploadsChunkSize": "حداکثر اندازه یک درخواست را نشان می‌دهد (برای آپلودهای کوچک‌تر از آپلود مستقیم استفاده می‌شود). می‌توانید یک عدد صحیح ساده که نشان‌دهنده اندازه بایت است یا یک رشته مانند ۱۰ مگابایت، ۱ گیگابایت و غیره وارد کنید.",

View File

@ -10,6 +10,7 @@ interface ApiOpts {
method?: ApiMethod;
headers?: object;
body?: any;
signal?: AbortSignal;
}
interface TusSettings {

View File

@ -61,9 +61,7 @@ const route = useRoute();
const { t } = useI18n({});
const clean = (path: string) => {
return path.endsWith("/") ? path.slice(0, -1) : path;
};
let fetchDataController = new AbortController();
const error = ref<StatusError | null>(null);
@ -101,6 +99,7 @@ onUnmounted(() => {
layoutStore.toggleShell();
}
fileStore.updateRequest(null);
fetchDataController.abort();
});
watch(route, (to, from) => {
@ -142,20 +141,21 @@ const fetchData = async () => {
let url = route.path;
if (url === "") url = "/";
if (url[0] !== "/") url = "/" + url;
// Cancel the ongoing request
fetchDataController.abort();
fetchDataController = new AbortController();
try {
const res = await api.fetch(url);
if (clean(res.path) !== clean(`/${[...route.params.path].join("/")}`)) {
throw new Error("Data Mismatch!");
}
const res = await api.fetch(url, fetchDataController.signal);
fileStore.updateRequest(res);
document.title = `${res.name || t("sidebar.myFiles")} - ${t("files.files")} - ${name}`;
layoutStore.loading = false;
} catch (err) {
if (err instanceof StatusError && err.is_canceled) {
return;
}
if (err instanceof Error) {
error.value = err;
}
} finally {
layoutStore.loading = false;
}
};

View File

@ -151,9 +151,9 @@ var signupHandler = func(_ http.ResponseWriter, r *http.Request, d *data) (int,
d.settings.Defaults.Apply(user)
pwd, err := users.HashAndValidatePwd(info.Password, d.settings.MinimumPasswordLength)
pwd, err := users.ValidateAndHashPwd(info.Password, d.settings.MinimumPasswordLength)
if err != nil {
return http.StatusInternalServerError, err
return http.StatusBadRequest, err
}
user.Password = pwd

View File

@ -125,13 +125,9 @@ var userPostHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, d *
return http.StatusBadRequest, fbErrors.ErrEmptyPassword
}
if len(req.Data.Password) < int(d.settings.MinimumPasswordLength) {
return http.StatusBadRequest, fbErrors.ErrShortPassword
}
req.Data.Password, err = users.HashAndValidatePwd(req.Data.Password, d.settings.MinimumPasswordLength)
req.Data.Password, err = users.ValidateAndHashPwd(req.Data.Password, d.settings.MinimumPasswordLength)
if err != nil {
return http.StatusInternalServerError, err
return http.StatusBadRequest, err
}
userHome, err := d.settings.MakeUserDir(req.Data.Username, req.Data.Scope, d.server.Root)
@ -167,17 +163,19 @@ var userPutHandler = withSelfOrAdmin(func(w http.ResponseWriter, r *http.Request
}
if req.Data.Password != "" {
req.Data.Password, err = users.HashAndValidatePwd(req.Data.Password, d.settings.MinimumPasswordLength)
req.Data.Password, err = users.ValidateAndHashPwd(req.Data.Password, d.settings.MinimumPasswordLength)
if err != nil {
return http.StatusBadRequest, err
}
} else {
var suser *users.User
suser, err = d.store.Users.Get(d.server.Root, d.raw.(uint))
if err != nil {
return http.StatusInternalServerError, err
}
req.Data.Password = suser.Password
}
if err != nil {
return http.StatusInternalServerError, err
}
req.Which = []string{}
}
@ -190,13 +188,9 @@ var userPutHandler = withSelfOrAdmin(func(w http.ResponseWriter, r *http.Request
return http.StatusForbidden, nil
}
if len(req.Data.Password) < int(d.settings.MinimumPasswordLength) {
return http.StatusBadRequest, fbErrors.ErrShortPassword
}
req.Data.Password, err = users.HashAndValidatePwd(req.Data.Password, d.settings.MinimumPasswordLength)
req.Data.Password, err = users.ValidateAndHashPwd(req.Data.Password, d.settings.MinimumPasswordLength)
if err != nil {
return http.StatusInternalServerError, err
return http.StatusBadRequest, err
}
}

View File

@ -1,17 +0,0 @@
#!/bin/sh
# Initial setup
cp /tmp/README.md /build/docs/index.md
sed -i 's|site/docs/||g' /build/docs/index.md
# Start mkdocs in the background
mkdocs serve -a 0.0.0.0:8000 --dirtyreload &
# Watch for changes in README.md
while true; do
inotifywait -e modify /tmp/README.md
echo "README.md changed. Updating index.md..."
cp /tmp/README.md /build/docs/index.md
sed -i 's|site/docs/||g' /build/docs/index.md
done

View File

View File

@ -246,9 +246,9 @@ balanced-match@^1.0.0:
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
version "1.1.12"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843"
integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"

26
users/assets.go Normal file
View File

@ -0,0 +1,26 @@
package users
import (
"embed"
"strings"
)
//go:embed assets
var assets embed.FS
var commonPasswords map[string]struct{}
//nolint:gochecknoinits
func init() {
// Password list sourced from:
// https://github.com/danielmiessler/SecLists/blob/master/Passwords/Common-Credentials/100k-most-used-passwords-NCSC.txt
data, err := assets.ReadFile("assets/common-passwords.txt")
if err != nil {
panic(err)
}
passwords := strings.Split(strings.TrimSpace(string(data)), "\n")
commonPasswords = make(map[string]struct{}, len(passwords))
for _, password := range passwords {
commonPasswords[strings.TrimSpace(password)] = struct{}{}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -9,10 +9,14 @@ import (
fbErrors "github.com/filebrowser/filebrowser/v2/errors"
)
// HashPwd hashes a password.
func HashAndValidatePwd(password string, minimumLength uint) (string, error) {
// ValidateAndHashPwd validates and hashes a password.
func ValidateAndHashPwd(password string, minimumLength uint) (string, error) {
if uint(len(password)) < minimumLength {
return "", fbErrors.ErrShortPassword
return "", fbErrors.ErrShortPassword{MinimumLength: minimumLength}
}
if _, ok := commonPasswords[password]; ok {
return "", fbErrors.ErrEasyPassword
}
return HashPwd(password)

6
www/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
docs/LICENSE
docs/code-of-conduct.md
docs/contributing.md
docs/security.md
docs/changelog.md
public

4
www/Dockerfile Normal file
View File

@ -0,0 +1,4 @@
FROM squidfunk/mkdocs-material
COPY requirements.txt requirements.txt
RUN pip install --no-cache-dir -r requirements.txt

View File

@ -10,7 +10,7 @@ You are able to customize your File Browser installation by changing its name to
* **Disable external links:** this will disable any external links (except the ones to this documentation).
* **Folder:** is the path to a directory that can contain two items:
* **custom.css**, containing the styles you want to apply to your installation.
* **img** a directory whose files can replace the [default logotypes](../frontend/public/img) in the application.
* **img** a directory whose files can replace the [default logotypes](https://github.com/filebrowser/filebrowser/tree/master/frontend/public/img) in the application.
These options can be either set via the CLI interface using the following command:

59
www/docs/index.md Normal file
View File

@ -0,0 +1,59 @@
<style>
.md-content .md-typeset h1 {
display: none;
}
</style>
<p align="center">
<img src="https://raw.githubusercontent.com/filebrowser/logo/master/banner.png" width="550"/>
</p>
> [!WARNING]
>
> This project is currently on **maintenance-only** mode. For more information, read the information on [GitHub](https://github.com/filebrowser/filebrowser#project-status).
![Preview](static/example.gif)
File Browser provides a file managing interface within a specified directory and it can be used to upload, delete, preview and edit your files. It is a **create-your-own-cloud**-kind of software where you can just install it on your server, direct it to a path and access your files through a nice web interface.
## Features
<div class="grid cards" markdown>
- **Easy Login System**
---
![](./static/1.jpg)
- **Sleek Interface**
---
![](./static/2.jpg)
- **User Management**
---
![](./static/3.jpg)
- **File Editing**
---
![](./static/4.jpg)
- **Custom Commands**
---
![](./static/5.jpg)
- **Customization**
---
![](./static/6.jpg)
</div>

View File

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 65 KiB

View File

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 116 KiB

View File

Before

Width:  |  Height:  |  Size: 151 KiB

After

Width:  |  Height:  |  Size: 151 KiB

View File

Before

Width:  |  Height:  |  Size: 212 KiB

After

Width:  |  Height:  |  Size: 212 KiB

View File

Before

Width:  |  Height:  |  Size: 130 KiB

After

Width:  |  Height:  |  Size: 130 KiB

View File

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 60 KiB

BIN
www/docs/static/example.gif vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 477 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -1,31 +1,30 @@
site_name: Filebrowser
site_name: File Browser
site_description: 'A web-based file browser and manager for your files'
site_author: 'Filebrowser Community'
site_author: 'File Browser Contributors'
site_url: 'https://filebrowser.org'
repo_name: 'filebrowser/filebrowser'
repo_url: 'https://github.com/filebrowser/filebrowser'
edit_uri: 'edit/master/docs/'
copyright: 'Copyright &copy; 2025 Filebrowser Community'
copyright: 'Copyright &copy; 2025 File Browser Contributors'
theme:
name: material
language: en
logo: docs/assets/logo.png
favicon: docs/assets/favicon.png
logo: static/logo.png
favicon: static/favicon.png
palette:
- scheme: default
primary: blue
accent: light blue
toggle:
icon: material/brightness-7
icon: material/lightbulb
name: Switch to dark mode
- scheme: slate
primary: blue
accent: light blue
toggle:
icon: material/brightness-4
icon: material/lightbulb-outline
name: Switch to light mode
font:
@ -71,15 +70,7 @@ markdown_extensions:
- md_in_html
- toc:
permalink: true
- pymdownx.arithmatex:
generic: true
- pymdownx.betterem:
smart_enable: all
- pymdownx.caret
- pymdownx.details
- pymdownx.emoji:
emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:material.extensions.emoji.to_svg
- pymdownx.highlight:
anchor_linenums: true
use_pygments: true
@ -87,11 +78,7 @@ markdown_extensions:
- pymdownx.keys
- pymdownx.mark
- pymdownx.smartsymbols
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format
- pymdownx.superfences
- pymdownx.tabbed:
alternate_style: true
- pymdownx.tasklist:
@ -110,10 +97,10 @@ extra:
nav:
- Home: index.md
- Getting Started:
- Installation: docs/installation.md
- Configuration: docs/configuration.md
- Security: docs/security.md
- Installation: installation.md
- Configuration: configuration.md
- Contributing:
- Contributing: docs/contributing.md
- Code of Conduct: docs/code-of-conduct.md
- Contributing: contributing.md
- Code of Conduct: code-of-conduct.md
- Security Policy: security.md
- Changelog: changelog.md