beta v0.3.7 release (#286)

This commit is contained in:
Graham Steffaniak 2025-01-12 19:50:22 -05:00 committed by GitHub
parent bd4d108b58
commit 1e35473998
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 300 additions and 222 deletions

View File

@ -6,7 +6,32 @@ on:
- "main" - "main"
jobs: jobs:
test_frontend:
name: Test Playwright
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3.0.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.0.0
- uses: actions/setup-node@v4
- working-directory: frontend
run: npm i && npm run build
- uses: actions/setup-go@v5
with:
go-version: 'stable'
- working-directory: backend
run: go build -o filebrowser .
- name: Build
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile.playwright
push: false
push_latest_to_registry: push_latest_to_registry:
needs: [ test_frontend ]
name: Push latest name: Push latest
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -14,9 +39,9 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Find latest tag - name: Find latest beta tag
run: | run: |
echo "LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`)" >> $GITHUB_ENV echo "LATEST_TAG=$(git describe --tags $(git rev-list --tags="*-beta*" --max-count=1)) >> $GITHUB_ENV
echo "latest tag is $LATEST_TAG" echo "latest tag is $LATEST_TAG"
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3.0.0 uses: docker/setup-qemu-action@v3.0.0

View File

@ -1,28 +1,37 @@
name: pr-request name: PR Request
on: on:
pull_request: pull_request:
branches: branches:
- "main" - "dev/v[0-9]+.[0-9]+.[0-9]+"
- "v[0-9]+.[0-9]+.[0-9]+" - "beta/v[0-9]+.[0-9]+.[0-9]+"
- "stable/v[0-9]+.[0-9]+.[0-9]+"
jobs: jobs:
#test_frontend: test_frontend:
# name: Push release name: Test Playwright
# runs-on: ubuntu-latest runs-on: ubuntu-latest
# steps: steps:
# - name: Checkout - name: Checkout
# uses: actions/checkout@v4 uses: actions/checkout@v4
# - name: Set up QEMU - name: Set up QEMU
# uses: docker/setup-qemu-action@v3.0.0 uses: docker/setup-qemu-action@v3.0.0
# - name: Set up Docker Buildx - name: Set up Docker Buildx
# uses: docker/setup-buildx-action@v3.0.0 uses: docker/setup-buildx-action@v3.0.0
# - name: Build - uses: actions/setup-node@v4
# uses: docker/build-push-action@v6 - working-directory: frontend
# with: run: npm i && npm run build
# context: . - uses: actions/setup-go@v5
# file: ./Dockerfile.playwright with:
# push: false go-version: 'stable'
- working-directory: backend
run: go build -o filebrowser .
- name: Build
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile.playwright
push: false
push_pr_to_registry: push_pr_to_registry:
name: Push PR name: Push PR
runs-on: ubuntu-latest runs-on: ubuntu-latest

View File

@ -9,7 +9,32 @@ permissions:
contents: write contents: write
jobs: jobs:
test_frontend:
name: Test Playwright
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3.0.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.0.0
- uses: actions/setup-node@v4
- working-directory: frontend
run: npm i && npm run build
- uses: actions/setup-go@v5
with:
go-version: 'stable'
- working-directory: backend
run: go build -o filebrowser .
- name: Build
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile.playwright
push: false
create_release_tag: create_release_tag:
needs: [ test_frontend ]
name: Create Release name: Create Release
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -41,6 +66,7 @@ jobs:
name: ${{ steps.extract_branch.outputs.tag_name }} name: ${{ steps.extract_branch.outputs.tag_name }}
push_release_to_registry: push_release_to_registry:
needs: [ test_frontend ]
name: Push release name: Push release
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:

View File

@ -9,7 +9,32 @@ permissions:
contents: write contents: write
jobs: jobs:
test_frontend:
name: Test Playwright
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3.0.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.0.0
- uses: actions/setup-node@v4
- working-directory: frontend
run: npm i && npm run build
- uses: actions/setup-go@v5
with:
go-version: 'stable'
- working-directory: backend
run: go build -o filebrowser .
- name: Build
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile.playwright
push: false
create_release_tag: create_release_tag:
needs: [ test_frontend ]
name: Create Release name: Create Release
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -40,6 +65,7 @@ jobs:
name: ${{ steps.extract_branch.outputs.tag_name }} name: ${{ steps.extract_branch.outputs.tag_name }}
push_release_to_registry: push_release_to_registry:
needs: [ test_frontend ]
name: Push release name: Push release
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:

1
.gitignore vendored
View File

@ -8,6 +8,7 @@ rice-box.go
/frontend/dist /frontend/dist
/frontend/pkg /frontend/pkg
/frontend/test-results /frontend/test-results
/frontend/loginAuth.json
/frontend/package-lock.json /frontend/package-lock.json
/backend/vendor /backend/vendor
/backend/*.cov /backend/*.cov

View File

@ -2,7 +2,18 @@
All notable changes to this project will be documented in this file. For commit guidelines, please refer to [Standard Version](https://github.com/conventional-changelog/standard-version). All notable changes to this project will be documented in this file. For commit guidelines, please refer to [Standard Version](https://github.com/conventional-changelog/standard-version).
## v0.3.6 ## v0.3.7-beta
**Notes**:
- Adding windows builds back to automated process... will replace manually if they throw malicious defender warnings.
- Adding playwright tests to all pr's against dev/beta/release branches.
- These playwright tests should help keep release more reliably stable.
**Bugfixes**:
- closing with the default bar issue.
- tar.gz archive creation issue
## v0.3.6-beta
**New Features** **New Features**
- Adds "externalUrl" server config https://github.com/gtsteffaniak/filebrowser/issues/272 - Adds "externalUrl" server config https://github.com/gtsteffaniak/filebrowser/issues/272

View File

@ -1,19 +1,5 @@
FROM golang:1.23-alpine AS base FROM gtstef/playwright-base
WORKDIR /app WORKDIR /app
COPY ./backend ./ COPY [ "./backend/filebrowser*", "./"]
RUN go build -ldflags="-w -s" -o filebrowser . COPY [ "./frontend/", "./" ]
RUN ./filebrowser -c filebrowser-playwright.yaml & sleep 2 && npx playwright test
FROM node:slim
WORKDIR /app
COPY ./frontend/package.json ./
RUN npm i --maxsockets 1
RUN npx playwright install --with-deps firefox
COPY [ "backend/config.yaml", "./" ]
COPY ./frontend/ ./frontend
WORKDIR /app/frontend
RUN npm run build-docker
WORKDIR /app
COPY --from=base /app/filebrowser* ./
RUN cp -R frontend/tests/ srv
ENV FILEBROWSER_NO_EMBEDED="true"
RUN ./filebrowser & sleep 2 && cd frontend && npx playwright test

View File

@ -0,0 +1,5 @@
FROM node:22-slim
WORKDIR /app
COPY ./frontend/package.json ./
RUN npm i @playwright/test
RUN npx playwright install --with-deps firefox

View File

@ -153,3 +153,6 @@ Starred/pinned files | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ |
Content preview icons | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | Content preview icons | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ |
Plugins support | ❌ | ❌ | ✅ | ✅ | ❌ | ✅ | Plugins support | ❌ | ❌ | ✅ | ✅ | ❌ | ✅ |
Chromecast support | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | Chromecast support | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
Share collections of files | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
Can archive selected files | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
Can browse archive files | ❌ | ❌ | ❌ | ❌ | ❌ | ✅

View File

@ -24,14 +24,18 @@ builds:
- upx --force-macos {{ .Path }} # Compress the binary with UPX - upx --force-macos {{ .Path }} # Compress the binary with UPX
# Build configuration for windows without arm # Build configuration for windows without arm
# - id: windows - id: windows
# ldflags: *ldflags ldflags: *ldflags
# main: main.go main: main.go
# binary: filebrowser binary: filebrowser
# goos: goos:
# - windows - windows
# goarch: goarch:
# - amd64 - amd64
- arm64
hooks:
post:
- upx {{ .Path }} # Compress the binary with UPX
archives: archives:
- name_template: "{{.Os}}-{{.Arch}}{{if .Arm}}v{{.Arm}}{{end}}-{{ .ProjectName }}" - name_template: "{{.Os}}-{{.Arch}}{{if .Arm}}v{{.Arm}}{{end}}-{{ .ProjectName }}"

View File

@ -0,0 +1,21 @@
server:
port: 80
baseURL: "/"
root: "./tests/playwright-files"
auth:
method: password
signup: false
userDefaults:
darkMode: true
disableSettings: false
scope: "."
hideDotfiles: true
singleClick: false
permissions:
admin: false
create: false
rename: false
modify: false
delete: false
share: false
download: false

View File

@ -192,7 +192,6 @@ func (idx *Index) indexDirectory(adjustedPath string, quick, recursive bool) err
} }
func (idx *Index) makeIndexPath(subPath string) string { func (idx *Index) makeIndexPath(subPath string) string {
subPath = strings.ReplaceAll(subPath, "\\", "/")
if strings.HasPrefix(subPath, "./") { if strings.HasPrefix(subPath, "./") {
subPath = strings.TrimPrefix(subPath, ".") subPath = strings.TrimPrefix(subPath, ".")
} }
@ -203,6 +202,7 @@ func (idx *Index) makeIndexPath(subPath string) string {
subPath = strings.TrimSuffix(subPath, "/") subPath = strings.TrimSuffix(subPath, "/")
// remove index prefix // remove index prefix
adjustedPath := strings.TrimPrefix(subPath, idx.Source.Path) adjustedPath := strings.TrimPrefix(subPath, idx.Source.Path)
adjustedPath = strings.ReplaceAll(adjustedPath, "\\", "/")
// remove trailing slash // remove trailing slash
adjustedPath = strings.TrimSuffix(adjustedPath, "/") adjustedPath = strings.TrimSuffix(adjustedPath, "/")
if !strings.HasPrefix(adjustedPath, "/") { if !strings.HasPrefix(adjustedPath, "/") {
@ -250,7 +250,6 @@ func (idx *Index) RefreshFileInfo(opts FileOptions) error {
Path: opts.Path, Path: opts.Path,
IsDir: opts.IsDir, IsDir: opts.IsDir,
} }
if !refreshOptions.IsDir { if !refreshOptions.IsDir {
refreshOptions.Path = idx.makeIndexPath(filepath.Dir(refreshOptions.Path)) refreshOptions.Path = idx.makeIndexPath(filepath.Dir(refreshOptions.Path))
refreshOptions.IsDir = true refreshOptions.IsDir = true

View File

@ -91,9 +91,6 @@ func TestMakeIndexPath(t *testing.T) {
{"Trailing slash removed", "/test/", "/test"}, {"Trailing slash removed", "/test/", "/test"},
{"Subpath without root prefix", "/other/test", "/other/test"}, {"Subpath without root prefix", "/other/test", "/other/test"},
{"Complex nested paths", "/nested/path", "/nested/path"}, {"Complex nested paths", "/nested/path", "/nested/path"},
// Windows
{"Mixed slash", "/first\\second", "/first/second"},
{"Windows slash", "\\first\\second", "/first/second"},
} }
for _, tt := range tests { for _, tt := range tests {
@ -105,6 +102,27 @@ func TestMakeIndexPath(t *testing.T) {
} }
}) })
} }
tests = []struct {
name string
subPath string
expected string
}{
// Windows
{"Mixed slash", "/first\\second", "/first/second"},
{"Windows slash", "\\first\\second", "/first/second"},
{"Windows full path", "C:\\Users\\testfolder\\nestedfolder", "/testfolder/nestedfolder"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
idx := &Index{Source: settings.Source{Path: "C:\\Users"}}
result := idx.makeIndexPath(tt.subPath)
if result != tt.expected {
t.Errorf("makeIndexPath(%q)\ngot %q\nwant %q", tt.name, result, tt.expected)
}
})
}
} }
func TestMakeIndexPathRoot(t *testing.T) { func TestMakeIndexPathRoot(t *testing.T) {

View File

@ -17,7 +17,7 @@ require (
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.9.0
github.com/swaggo/http-swagger v1.3.4 github.com/swaggo/http-swagger v1.3.4
github.com/swaggo/swag v1.16.4 github.com/swaggo/swag v1.16.4
golang.org/x/crypto v0.31.0 golang.org/x/crypto v0.32.0
golang.org/x/image v0.23.0 golang.org/x/image v0.23.0
golang.org/x/text v0.21.0 golang.org/x/text v0.21.0
) )
@ -43,9 +43,9 @@ require (
github.com/swaggo/files v1.0.1 // indirect github.com/swaggo/files v1.0.1 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.etcd.io/bbolt v1.3.11 // indirect go.etcd.io/bbolt v1.3.11 // indirect
golang.org/x/net v0.33.0 // indirect golang.org/x/net v0.34.0 // indirect
golang.org/x/sys v0.28.0 // indirect golang.org/x/sys v0.29.0 // indirect
golang.org/x/tools v0.28.0 // indirect golang.org/x/tools v0.29.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

View File

@ -114,8 +114,8 @@ go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68= golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
@ -133,8 +133,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
@ -153,8 +153,8 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@ -168,8 +168,8 @@ golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=

View File

@ -63,8 +63,7 @@ func rawHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (int,
} }
return rawFilesHandler(w, r, d, fileList) return rawFilesHandler(w, r, d, fileList)
} }
func addFile(path string, d *requestContext, tarWriter *tar.Writer, zipWriter *zip.Writer, flatten bool) error {
func addFile(path string, d *requestContext, tarWriter *tar.Writer, zipWriter *zip.Writer) error {
idx := files.GetIndex("default") idx := files.GetIndex("default")
realPath, _, _ := idx.GetRealPath(d.user.Scope, path) realPath, _, _ := idx.GetRealPath(d.user.Scope, path)
if !d.user.Check(realPath) { if !d.user.Check(realPath) {
@ -75,16 +74,36 @@ func addFile(path string, d *requestContext, tarWriter *tar.Writer, zipWriter *z
return err return err
} }
// Get the base name of the top-level folder or file
baseName := filepath.Base(realPath)
if info.IsDir() { if info.IsDir() {
// Walk through directory contents // Walk through directory contents
return filepath.Walk(realPath, func(filePath string, fileInfo os.FileInfo, err error) error { return filepath.Walk(realPath, func(filePath string, fileInfo os.FileInfo, err error) error {
if err != nil { if err != nil {
return err return err
} }
relPath, err := filepath.Rel(filepath.Dir(realPath), filePath) // Relative path including base dir
// Calculate the relative path
relPath, err := filepath.Rel(realPath, filePath) // Use realPath directly
if err != nil { if err != nil {
return err return err
} }
// Normalize for tar: convert \ to /
relPath = filepath.ToSlash(relPath)
// Skip adding `.` (current directory)
if relPath == "." {
return nil
}
// Prepend base folder name unless flatten is true
if !flatten {
relPath = filepath.Join(baseName, relPath)
relPath = filepath.ToSlash(relPath) // Ensure normalized separators
}
if fileInfo.IsDir() { if fileInfo.IsDir() {
if tarWriter != nil { if tarWriter != nil {
header := &tar.Header{ header := &tar.Header{
@ -105,8 +124,8 @@ func addFile(path string, d *requestContext, tarWriter *tar.Writer, zipWriter *z
}) })
} }
// Add a single file if it's not a directory // For a single file, use the base name as the archive path
return addSingleFile(realPath, filepath.Base(realPath), zipWriter, tarWriter) return addSingleFile(realPath, baseName, zipWriter, tarWriter)
} }
func addSingleFile(realPath, archivePath string, zipWriter *zip.Writer, tarWriter *tar.Writer) error { func addSingleFile(realPath, archivePath string, zipWriter *zip.Writer, tarWriter *tar.Writer) error {
@ -122,11 +141,12 @@ func addSingleFile(realPath, archivePath string, zipWriter *zip.Writer, tarWrite
} }
if tarWriter != nil { if tarWriter != nil {
header, err := tar.FileInfoHeader(info, realPath) header, err := tar.FileInfoHeader(info, "")
if err != nil { if err != nil {
return err return err
} }
header.Name = archivePath // Ensure correct relative path
header.Name = filepath.ToSlash(archivePath)
if err = tarWriter.WriteHeader(header); err != nil { if err = tarWriter.WriteHeader(header); err != nil {
return err return err
} }
@ -217,8 +237,10 @@ func createZip(w io.Writer, d *requestContext, filenames ...string) error {
zipWriter := zip.NewWriter(w) zipWriter := zip.NewWriter(w)
defer zipWriter.Close() defer zipWriter.Close()
// Check if we have exactly one directory
//flatten := len(filenames) == 1
for _, fname := range filenames { for _, fname := range filenames {
err := addFile(fname, d, nil, zipWriter) err := addFile(fname, d, nil, zipWriter, false)
if err != nil { if err != nil {
log.Printf("Failed to add %s to ZIP: %v", fname, err) log.Printf("Failed to add %s to ZIP: %v", fname, err)
} }
@ -234,8 +256,10 @@ func createTarGz(w io.Writer, d *requestContext, filenames ...string) error {
tarWriter := tar.NewWriter(gzWriter) tarWriter := tar.NewWriter(gzWriter)
defer tarWriter.Close() defer tarWriter.Close()
// Check if we have exactly one directory
//flatten := len(filenames) == 1
for _, fname := range filenames { for _, fname := range filenames {
err := addFile(fname, d, tarWriter, nil) err := addFile(fname, d, tarWriter, nil, false)
if err != nil { if err != nil {
log.Printf("Failed to add %s to TAR.GZ: %v", fname, err) log.Printf("Failed to add %s to TAR.GZ: %v", fname, err)
} }

View File

@ -114,6 +114,9 @@ func setDefaults() Settings {
Host: "", Host: "",
}, },
}, },
Frontend: Frontend{
Name: "FileBrowser Quantum",
},
UserDefaults: UserDefaults{ UserDefaults: UserDefaults{
StickySidebar: true, StickySidebar: true,
Scope: ".", Scope: ".",

20
frontend/global-setup.ts Normal file
View File

@ -0,0 +1,20 @@
import { Browser, firefox, expect, Page } from "@playwright/test";
async function globalSetup() {
const browser: Browser = await firefox.launch();
const context = await browser.newContext();
const page: Page = await context.newPage();
await page.goto("http://127.0.0.1/login");
await page.getByPlaceholder("Username").fill("admin");
await page.getByPlaceholder("Password").fill("admin");
await page.getByRole("button", { name: "Login" }).click();
await page.waitForURL("**/files/", { timeout: 100 });
await expect(page).toHaveTitle('FileBrowser Quantum - Files');
let cookies = await context.cookies();
expect(cookies.find((c) => c.name == "auth")?.value).toBeDefined();
await page.context().storageState({ path: "./loginAuth.json" });
await browser.close();
}
export default globalSetup

View File

@ -17,10 +17,10 @@
"lint": "eslint --ext .js,.vue,ts src", "lint": "eslint --ext .js,.vue,ts src",
"lint:fix": "eslint --fix src/", "lint:fix": "eslint --fix src/",
"format": "prettier --write .", "format": "prettier --write .",
"integration-test": "npx playwright test",
"test": "vitest run " "test": "vitest run "
}, },
"dependencies": { "dependencies": {
"@playwright/test": "^1.49.1",
"ace-builds": "^1.24.2", "ace-builds": "^1.24.2",
"clipboard": "^2.0.4", "clipboard": "^2.0.4",
"css-vars-ponyfill": "^2.4.3", "css-vars-ponyfill": "^2.4.3",
@ -34,7 +34,6 @@
}, },
"devDependencies": { "devDependencies": {
"@intlify/unplugin-vue-i18n": "^4.0.0", "@intlify/unplugin-vue-i18n": "^4.0.0",
"@playwright/test": "^1.42.1",
"@vitejs/plugin-vue": "^5.0.4", "@vitejs/plugin-vue": "^5.0.4",
"@vue/eslint-config-typescript": "^13.0.0", "@vue/eslint-config-typescript": "^13.0.0",
"eslint": "^8.57.0", "eslint": "^8.57.0",

View File

@ -10,6 +10,8 @@ import { defineConfig, devices } from "@playwright/test";
* See https://playwright.dev/docs/test-configuration. * See https://playwright.dev/docs/test-configuration.
*/ */
export default defineConfig({ export default defineConfig({
globalSetup: "./global-setup",
timeout: 3000,
testDir: "./tests", testDir: "./tests",
/* Run tests in files in parallel */ /* Run tests in files in parallel */
fullyParallel: true, fullyParallel: true,
@ -18,11 +20,13 @@ export default defineConfig({
/* Retry on CI only */ /* Retry on CI only */
retries: process.env.CI ? 2 : 0, retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */ /* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined, workers: process.env.CI ? 1 : 1, // required for now! todo parallel some tests
/* Reporter to use. See https://playwright.dev/docs/test-reporters */ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: "line", reporter: "line",
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: { use: {
actionTimeout: 500,
storageState: "loginAuth.json",
/* Base URL to use in actions like `await page.goto('/')`. */ /* Base URL to use in actions like `await page.goto('/')`. */
baseURL: "http://127.0.0.1", baseURL: "http://127.0.0.1",

View File

@ -32,7 +32,7 @@
<script> <script>
window.FileBrowser = JSON.parse('{{ .globalVars }}'); window.FileBrowser = JSON.parse('{{ .globalVars }}');
var dynamicManifest = { var dynamicManifest = {
"name": window.FileBrowser.Name || '', "name": window.FileBrowser.Name || 'FileBrowser Quantum',
"icons": [ "icons": [
{ {
"src": window.location.origin + "{{ .StaticURL }}/img/icons/android-chrome-256x256.png", "src": window.location.origin + "{{ .StaticURL }}/img/icons/android-chrome-256x256.png",

View File

@ -7,8 +7,8 @@
<i class="material-icons">settings</i> <i class="material-icons">settings</i>
</div> </div>
<div class="inner-card"> <div class="inner-card logout-button" @click="logout" >
<i v-if="canLogout" @click="logout" class="material-icons">exit_to_app</i> <i v-if="canLogout" class="material-icons">exit_to_app</i>
</div> </div>
</div> </div>
</div> </div>

View File

@ -132,7 +132,7 @@ router.beforeResolve(async (to, from, next) => {
// Set the page title using i18n // Set the page title using i18n
const title = i18n.global.t(titles[to.name as keyof typeof titles]); const title = i18n.global.t(titles[to.name as keyof typeof titles]);
document.title = title + " - " + name; document.title = name + " - " + title ;
// Update store with the current route // Update store with the current route
mutations.setRoute(to); mutations.setRoute(to);

View File

@ -67,13 +67,13 @@ export default {
}, },
computed: { computed: {
signup: () => signup, signup: () => signup,
name: () => name, name: () => name || "FileBrowser Quantum",
logoURL: () => logoURL, logoURL: () => logoURL,
isDarkMode() { isDarkMode() {
return darkMode === true; return darkMode === true;
}, },
loginName() { loginName() {
return name || "FileBrowser Quantum" return name
} }
}, },
data: function () { data: function () {

View File

@ -9,7 +9,7 @@
<script> <script>
import { url } from "@/utils"; import { url } from "@/utils";
import router from "@/router"; import router from "@/router";
import { getters, state } from "@/store"; import { getters, state, mutations } from "@/store";
import Action from "@/components/Action.vue"; import Action from "@/components/Action.vue";
export default { export default {

View File

@ -1,6 +1,8 @@
import { test, expect } from "./fixtures/auth"; import { test, expect } from "@playwright/test";
test("redirect to login", async ({ page, context }) => {
await context.clearCookies();
test("redirect to login", async ({ page }) => {
await page.goto("/"); await page.goto("/");
await expect(page).toHaveURL(/\/login/); await expect(page).toHaveURL(/\/login/);
@ -8,24 +10,16 @@ test("redirect to login", async ({ page }) => {
await expect(page).toHaveURL(/\/login\?redirect=\/files\//); await expect(page).toHaveURL(/\/login\?redirect=\/files\//);
}); });
test("login", async ({ authPage, page, context }) => { test("logout", async ({ page, context }) => {
await authPage.goto(); await page.goto('/');
await expect(page).toHaveTitle(/Login - FileBrowser Quantum$/); await expect(page.locator("div.wrong")).toBeHidden();
await page.waitForURL("**/files/", { timeout: 100 });
await authPage.loginAs("fake", "fake"); await expect(page).toHaveTitle('playwright-files - FileBrowser Quantum - Files');
await expect(authPage.wrongCredentials).toBeVisible();
await authPage.loginAs();
await expect(authPage.wrongCredentials).toBeHidden();
// await page.waitForURL("**/files/", { timeout: 5000 });
await expect(page).toHaveTitle(/.*Files - FileBrowser Quantum$/);
let cookies = await context.cookies(); let cookies = await context.cookies();
expect(cookies.find((c) => c.name == "auth")?.value).toBeDefined(); expect(cookies.find((c) => c.name == "auth")?.value).toBeDefined();
await page.locator('div.inner-card.logout-button').click();
// await authPage.logout(); await page.waitForURL("**/login", { timeout: 100 });
// await page.waitForURL("**/login", { timeout: 5000 }); await expect(page).toHaveTitle('FileBrowser Quantum - Login');
// await expect(page).toHaveTitle(/Login - FileBrowser Quantum$/); cookies = await context.cookies();
// cookies = await context.cookies(); expect(cookies.find((c) => c.name == "auth")?.value).toBeUndefined();
// expect(cookies.find((c) => c.name == "auth")?.value).toBeUndefined();
}); });

View File

@ -1,40 +0,0 @@
import {
type Page,
type Locator,
test as base,
expect,
} from "@playwright/test";
export class AuthPage {
public readonly wrongCredentials: Locator;
constructor(public readonly page: Page) {
this.wrongCredentials = this.page.locator("div.wrong");
}
async goto() {
await this.page.goto("/login");
}
async loginAs(username = "admin", password = "admin") {
await this.page.getByPlaceholder("Username").fill(username);
await this.page.getByPlaceholder("Password").fill(password);
await this.page.getByRole("button", { name: "Login" }).click();
}
//async logout() {
// await this.page.getByRole("button", { name: "Logout" }).click();
//}
}
const test = base.extend<{ authPage: AuthPage }>({
authPage: async ({ page }, use) => {
const authPage = new AuthPage(page);
await authPage.goto();
await authPage.loginAs();
await use(authPage);
// await authPage.logout();
},
});
export { test, expect };

Binary file not shown.

View File

@ -0,0 +1,11 @@
import { test, expect } from "@playwright/test";
test("file preview", async ({ page, context }) => {
await page.goto("/files/");
await expect(page).toHaveTitle('playwright-files - FileBrowser Quantum - Files');
await page.locator('a[aria-label="file.tar.gz"]').waitFor({ state: 'visible' });
await page.locator('a[aria-label="file.tar.gz"]').dblclick();
await expect(page).toHaveTitle('file.tar.gz - FileBrowser Quantum - Files');
await page.locator('button[title="Close"]').click();
await expect(page).toHaveTitle('playwright-files - FileBrowser Quantum - Files');
});

View File

@ -20,6 +20,6 @@
}, },
"checkJs": false, // check later "checkJs": false, // check later
}, },
"include": ["src/**/*.ts", "src/**/*.js", "src/**/*.vue"], "include": ["src/**/*.ts", "src/**/*.js", "src/**/*.vue", "tests/auth.spec.ts"],
"exclude": ["node_modules", "dist"] "exclude": ["node_modules", "dist"]
} }

View File

@ -1,74 +0,0 @@
// vite.config.ts
import path from "node:path";
import { defineConfig } from "file:///Users/steffag/git/personal/filebrowser/frontend/node_modules/vite/dist/node/index.js";
import vue from "file:///Users/steffag/git/personal/filebrowser/frontend/node_modules/@vitejs/plugin-vue/dist/index.mjs";
import VueI18nPlugin from "file:///Users/steffag/git/personal/filebrowser/frontend/node_modules/@intlify/unplugin-vue-i18n/lib/vite.mjs";
import { compression } from "file:///Users/steffag/git/personal/filebrowser/frontend/node_modules/vite-plugin-compression2/dist/index.mjs";
var __vite_injected_original_dirname = "/Users/steffag/git/personal/filebrowser/frontend";
var plugins = [
vue(),
VueI18nPlugin({
include: [path.resolve(__vite_injected_original_dirname, "./src/i18n/**/*.json")]
}),
compression({
include: /\.(js|woff2|woff)(\?.*)?$/i,
deleteOriginalAssets: true
})
];
var resolve = {
alias: {
"@": path.resolve(__vite_injected_original_dirname, "src")
}
};
var vite_config_default = defineConfig(({ command }) => {
if (command === "serve") {
return {
plugins,
resolve,
server: {
proxy: {
"/api/command": {
target: "ws://127.0.0.1:8080",
ws: true
},
"/api": "http://127.0.0.1:8080"
}
}
};
} else {
return {
plugins,
resolve,
base: "",
build: {
rollupOptions: {
input: {
index: path.resolve(__vite_injected_original_dirname, "./public/index.html")
},
output: {
manualChunks: (id) => {
if (id.includes("i18n/")) {
return "i18n";
}
}
}
}
},
experimental: {
renderBuiltUrl(filename, { hostType }) {
if (hostType === "js") {
return { runtime: `window.__prependStaticUrl("${filename}")` };
} else if (hostType === "html") {
return `{{ .StaticURL }}/${filename}`;
} else {
return { relative: true };
}
}
}
};
}
});
export {
vite_config_default as default
};
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvVXNlcnMvc3RlZmZhZy9naXQvcGVyc29uYWwvZmlsZWJyb3dzZXIvZnJvbnRlbmRcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIi9Vc2Vycy9zdGVmZmFnL2dpdC9wZXJzb25hbC9maWxlYnJvd3Nlci9mcm9udGVuZC92aXRlLmNvbmZpZy50c1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vVXNlcnMvc3RlZmZhZy9naXQvcGVyc29uYWwvZmlsZWJyb3dzZXIvZnJvbnRlbmQvdml0ZS5jb25maWcudHNcIjtpbXBvcnQgcGF0aCBmcm9tIFwibm9kZTpwYXRoXCI7XG5pbXBvcnQgeyBkZWZpbmVDb25maWcgfSBmcm9tIFwidml0ZVwiO1xuaW1wb3J0IHZ1ZSBmcm9tIFwiQHZpdGVqcy9wbHVnaW4tdnVlXCI7XG5pbXBvcnQgVnVlSTE4blBsdWdpbiBmcm9tIFwiQGludGxpZnkvdW5wbHVnaW4tdnVlLWkxOG4vdml0ZVwiO1xuaW1wb3J0IHsgY29tcHJlc3Npb24gfSBmcm9tIFwidml0ZS1wbHVnaW4tY29tcHJlc3Npb24yXCI7XG5cbmNvbnN0IHBsdWdpbnMgPSBbXG4gIHZ1ZSgpLFxuICBWdWVJMThuUGx1Z2luKHtcbiAgICBpbmNsdWRlOiBbcGF0aC5yZXNvbHZlKF9fZGlybmFtZSwgXCIuL3NyYy9pMThuLyoqLyouanNvblwiKV0sXG4gIH0pLFxuICBjb21wcmVzc2lvbih7XG4gICAgaW5jbHVkZTogL1xcLihqc3x3b2ZmMnx3b2ZmKShcXD8uKik/JC9pLFxuICAgIGRlbGV0ZU9yaWdpbmFsQXNzZXRzOiB0cnVlLFxuICB9KSxcbl07XG5cbmNvbnN0IHJlc29sdmUgPSB7XG4gIGFsaWFzOiB7XG4gICAgXCJAXCI6IHBhdGgucmVzb2x2ZShfX2Rpcm5hbWUsIFwic3JjXCIpLFxuICB9LFxufTtcblxuLy8gaHR0cHM6Ly92aXRlanMuZGV2L2NvbmZpZy9cbmV4cG9ydCBkZWZhdWx0IGRlZmluZUNvbmZpZygoeyBjb21tYW5kIH0pID0+IHtcbiAgaWYgKGNvbW1hbmQgPT09IFwic2VydmVcIikge1xuICAgIHJldHVybiB7XG4gICAgICBwbHVnaW5zLFxuICAgICAgcmVzb2x2ZSxcbiAgICAgIHNlcnZlcjoge1xuICAgICAgICBwcm94eToge1xuICAgICAgICAgIFwiL2FwaS9jb21tYW5kXCI6IHtcbiAgICAgICAgICAgIHRhcmdldDogXCJ3czovLzEyNy4wLjAuMTo4MDgwXCIsXG4gICAgICAgICAgICB3czogdHJ1ZSxcbiAgICAgICAgICB9LFxuICAgICAgICAgIFwiL2FwaVwiOiBcImh0dHA6Ly8xMjcuMC4wLjE6ODA4MFwiLFxuICAgICAgICB9LFxuICAgICAgfSxcbiAgICB9O1xuICB9IGVsc2Uge1xuICAgIC8vIGNvbW1hbmQgPT09ICdidWlsZCdcbiAgICByZXR1cm4ge1xuICAgICAgcGx1Z2lucyxcbiAgICAgIHJlc29sdmUsXG4gICAgICBiYXNlOiBcIlwiLFxuICAgICAgYnVpbGQ6IHtcbiAgICAgICAgcm9sbHVwT3B0aW9uczoge1xuICAgICAgICAgIGlucHV0OiB7XG4gICAgICAgICAgICBpbmRleDogcGF0aC5yZXNvbHZlKF9fZGlybmFtZSwgXCIuL3B1YmxpYy9pbmRleC5odG1sXCIpLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgb3V0cHV0OiB7XG4gICAgICAgICAgICBtYW51YWxDaHVua3M6IChpZCkgPT4ge1xuICAgICAgICAgICAgICBpZiAoaWQuaW5jbHVkZXMoXCJpMThuL1wiKSkge1xuICAgICAgICAgICAgICAgIHJldHVybiBcImkxOG5cIjtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9LFxuICAgICAgICB9LFxuICAgICAgfSxcbiAgICAgIGV4cGVyaW1lbnRhbDoge1xuICAgICAgICByZW5kZXJCdWlsdFVybChmaWxlbmFtZSwgeyBob3N0VHlwZSB9KSB7XG4gICAgICAgICAgaWYgKGhvc3RUeXBlID09PSBcImpzXCIpIHtcbiAgICAgICAgICAgIHJldHVybiB7IHJ1bnRpbWU6IGB3aW5kb3cuX19wcmVwZW5kU3RhdGljVXJsKFwiJHtmaWxlbmFtZX1cIilgIH07XG4gICAgICAgICAgfSBlbHNlIGlmIChob3N0VHlwZSA9PT0gXCJodG1sXCIpIHtcbiAgICAgICAgICAgIHJldHVybiBge3sgLlN0YXRpY1VSTCB9fS8ke2ZpbGVuYW1lfWA7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJldHVybiB7IHJlbGF0aXZlOiB0cnVlIH07XG4gICAgICAgICAgfVxuICAgICAgICB9LFxuICAgICAgfSxcbiAgICB9O1xuICB9XG59KTtcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBa1UsT0FBTyxVQUFVO0FBQ25WLFNBQVMsb0JBQW9CO0FBQzdCLE9BQU8sU0FBUztBQUNoQixPQUFPLG1CQUFtQjtBQUMxQixTQUFTLG1CQUFtQjtBQUo1QixJQUFNLG1DQUFtQztBQU16QyxJQUFNLFVBQVU7QUFBQSxFQUNkLElBQUk7QUFBQSxFQUNKLGNBQWM7QUFBQSxJQUNaLFNBQVMsQ0FBQyxLQUFLLFFBQVEsa0NBQVcsc0JBQXNCLENBQUM7QUFBQSxFQUMzRCxDQUFDO0FBQUEsRUFDRCxZQUFZO0FBQUEsSUFDVixTQUFTO0FBQUEsSUFDVCxzQkFBc0I7QUFBQSxFQUN4QixDQUFDO0FBQ0g7QUFFQSxJQUFNLFVBQVU7QUFBQSxFQUNkLE9BQU87QUFBQSxJQUNMLEtBQUssS0FBSyxRQUFRLGtDQUFXLEtBQUs7QUFBQSxFQUNwQztBQUNGO0FBR0EsSUFBTyxzQkFBUSxhQUFhLENBQUMsRUFBRSxRQUFRLE1BQU07QUFDM0MsTUFBSSxZQUFZLFNBQVM7QUFDdkIsV0FBTztBQUFBLE1BQ0w7QUFBQSxNQUNBO0FBQUEsTUFDQSxRQUFRO0FBQUEsUUFDTixPQUFPO0FBQUEsVUFDTCxnQkFBZ0I7QUFBQSxZQUNkLFFBQVE7QUFBQSxZQUNSLElBQUk7QUFBQSxVQUNOO0FBQUEsVUFDQSxRQUFRO0FBQUEsUUFDVjtBQUFBLE1BQ0Y7QUFBQSxJQUNGO0FBQUEsRUFDRixPQUFPO0FBRUwsV0FBTztBQUFBLE1BQ0w7QUFBQSxNQUNBO0FBQUEsTUFDQSxNQUFNO0FBQUEsTUFDTixPQUFPO0FBQUEsUUFDTCxlQUFlO0FBQUEsVUFDYixPQUFPO0FBQUEsWUFDTCxPQUFPLEtBQUssUUFBUSxrQ0FBVyxxQkFBcUI7QUFBQSxVQUN0RDtBQUFBLFVBQ0EsUUFBUTtBQUFBLFlBQ04sY0FBYyxDQUFDLE9BQU87QUFDcEIsa0JBQUksR0FBRyxTQUFTLE9BQU8sR0FBRztBQUN4Qix1QkFBTztBQUFBLGNBQ1Q7QUFBQSxZQUNGO0FBQUEsVUFDRjtBQUFBLFFBQ0Y7QUFBQSxNQUNGO0FBQUEsTUFDQSxjQUFjO0FBQUEsUUFDWixlQUFlLFVBQVUsRUFBRSxTQUFTLEdBQUc7QUFDckMsY0FBSSxhQUFhLE1BQU07QUFDckIsbUJBQU8sRUFBRSxTQUFTLDhCQUE4QixRQUFRLEtBQUs7QUFBQSxVQUMvRCxXQUFXLGFBQWEsUUFBUTtBQUM5QixtQkFBTyxvQkFBb0IsUUFBUTtBQUFBLFVBQ3JDLE9BQU87QUFDTCxtQkFBTyxFQUFFLFVBQVUsS0FBSztBQUFBLFVBQzFCO0FBQUEsUUFDRjtBQUFBLE1BQ0Y7QUFBQSxJQUNGO0FBQUEsRUFDRjtBQUNGLENBQUM7IiwKICAibmFtZXMiOiBbXQp9Cg==

View File

@ -16,6 +16,9 @@ update:
build: build:
docker build --build-arg="VERSION=testing" --build-arg="REVISION=n/a" -t gtstef/filebrowser . docker build --build-arg="VERSION=testing" --build-arg="REVISION=n/a" -t gtstef/filebrowser .
build-backend:
cd backend && go build -o filebrowser --ldflags="-w -s -X 'github.com/gtsteffaniak/filebrowser/backend/version.CommitSHA=testingCommit' -X 'github.com/gtsteffaniak/filebrowser/backend/version.Version=testing'"
run: run-frontend run: run-frontend
cd backend && swag init --output swagger/docs && \ cd backend && swag init --output swagger/docs && \
if [ "$(shell uname)" = "Darwin" ]; then \ if [ "$(shell uname)" = "Darwin" ]; then \
@ -48,9 +51,9 @@ test-backend:
test-frontend: test-frontend:
cd frontend && npm run test cd frontend && npm run test
test-frontend-playwright: test-playwright: run-frontend build-backend
npx playwright install docker build -t filebrowser-playwright-tests -f Dockerfile.playwright .
docker build -t gtstef/filebrowser-tests -f Dockerfile.playwright . docker run --rm --name filebrowser-playwright-tests filebrowser-playwright-tests
# Run on a windows machine! # Run on a windows machine!
release-windows: release-windows: