mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-21 23:20:24 +00:00
REST API: expose OpenAPI schema and render it using Swagger UI
Fixes #609
This commit is contained in:
parent
fb8f013ea7
commit
3d6b09e949
33 changed files with 244 additions and 51 deletions
47
.github/workflows/development.yml
vendored
47
.github/workflows/development.yml
vendored
|
@ -98,30 +98,13 @@ jobs:
|
|||
cp sftpgo.json output/
|
||||
cp -r templates output/
|
||||
cp -r static output/
|
||||
cp -r openapi output/
|
||||
cp init/com.github.drakkan.sftpgo.plist output/init/
|
||||
./sftpgo gen completion bash > output/bash_completion/sftpgo
|
||||
./sftpgo gen completion zsh > output/zsh_completion/_sftpgo
|
||||
./sftpgo gen man -d output/man/man1
|
||||
gzip output/man/man1/*
|
||||
|
||||
- name: Prepare build artifact for Windows
|
||||
if: startsWith(matrix.os, 'windows-')
|
||||
run: |
|
||||
mkdir output
|
||||
copy .\sftpgo.exe .\output
|
||||
copy .\sftpgo.json .\output
|
||||
mkdir output\templates
|
||||
xcopy .\templates .\output\templates\ /E
|
||||
mkdir output\static
|
||||
xcopy .\static .\output\static\ /E
|
||||
|
||||
- name: Upload build artifact
|
||||
if: startsWith(matrix.os, 'ubuntu-') != true
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: sftpgo-${{ matrix.os }}-go-${{ matrix.go }}
|
||||
path: output
|
||||
|
||||
- name: Prepare Windows installer
|
||||
if: ${{ startsWith(matrix.os, 'windows-') && github.event_name != 'pull_request' }}
|
||||
run: |
|
||||
|
@ -135,6 +118,8 @@ jobs:
|
|||
xcopy .\templates .\output\templates\ /E
|
||||
mkdir output\static
|
||||
xcopy .\static .\output\static\ /E
|
||||
mkdir output\openapi
|
||||
xcopy .\openapi .\output\openapi\ /E
|
||||
$LATEST_TAG = ((git describe --tags $(git rev-list --tags --max-count=1)) | Out-String).Trim()
|
||||
$REV_LIST=$LATEST_TAG+"..HEAD"
|
||||
$COMMITS_FROM_TAG= ((git rev-list $REV_LIST --count) | Out-String).Trim()
|
||||
|
@ -143,8 +128,9 @@ jobs:
|
|||
[IO.File]::WriteAllBytes($CERT_PATH,[System.Convert]::FromBase64String($Env:CERT_DATA))
|
||||
certutil -f -p "$Env:CERT_PASS" -importpfx MY "$CERT_PATH"
|
||||
rm "$CERT_PATH"
|
||||
& 'C:/Program Files (x86)/Windows Kits/10/bin/10.0.17763.0/x86/signtool.exe' sign /sm /tr http://timestamp.sectigo.com /td sha256 /fd sha256 /n "Nicola Murino" /d "SFTPGo" .\sftpgo.exe
|
||||
$INNO_S='/Ssigntool=$qC:/Program Files (x86)/Windows Kits/10/bin/10.0.17763.0/x86/signtool.exe$q sign /sm /tr http://timestamp.sectigo.com /td sha256 /fd sha256 /n $qNicola Murino$q /d $qSFTPGo$q $f'
|
||||
iscc "$INNO_S" windows-installer\sftpgo.iss
|
||||
iscc "$INNO_S" .\windows-installer\sftpgo.iss
|
||||
certutil -delstore MY "Nicola Murino"
|
||||
env:
|
||||
CERT_DATA: ${{ secrets.CERT_DATA }}
|
||||
|
@ -157,6 +143,27 @@ jobs:
|
|||
name: sftpgo_windows_installer_x86_64
|
||||
path: ./sftpgo_windows_x86_64.exe
|
||||
|
||||
- name: Prepare build artifact for Windows
|
||||
if: startsWith(matrix.os, 'windows-')
|
||||
run: |
|
||||
Remove-Item -LiteralPath "output" -Force -Recurse -ErrorAction Ignore
|
||||
mkdir output
|
||||
copy .\sftpgo.exe .\output
|
||||
copy .\sftpgo.json .\output
|
||||
mkdir output\templates
|
||||
xcopy .\templates .\output\templates\ /E
|
||||
mkdir output\static
|
||||
xcopy .\static .\output\static\ /E
|
||||
mkdir output\openapi
|
||||
xcopy .\openapi .\output\openapi\ /E
|
||||
|
||||
- name: Upload build artifact
|
||||
if: startsWith(matrix.os, 'ubuntu-') != true
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: sftpgo-${{ matrix.os }}-go-${{ matrix.go }}
|
||||
path: output
|
||||
|
||||
test-goarch-386:
|
||||
name: Run test cases on 32-bit arch
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -308,6 +315,7 @@ jobs:
|
|||
cp sftpgo.json output/
|
||||
cp -r templates output/
|
||||
cp -r static output/
|
||||
cp -r openapi output/
|
||||
cp init/sftpgo.service output/init/
|
||||
./sftpgo gen completion bash > output/bash_completion/sftpgo
|
||||
./sftpgo gen completion zsh > output/zsh_completion/_sftpgo
|
||||
|
@ -354,6 +362,7 @@ jobs:
|
|||
cp sftpgo.json output/
|
||||
cp -r templates output/
|
||||
cp -r static output/
|
||||
cp -r openapi output/
|
||||
cp init/sftpgo.service output/init/
|
||||
./sftpgo gen completion bash > output/bash_completion/sftpgo
|
||||
./sftpgo gen completion zsh > output/zsh_completion/_sftpgo
|
||||
|
|
26
.github/workflows/release.yml
vendored
26
.github/workflows/release.yml
vendored
|
@ -105,6 +105,7 @@ jobs:
|
|||
cp sftpgo.json output/
|
||||
cp sftpgo.db output/sqlite/
|
||||
cp -r static output/
|
||||
cp -r openapi output/
|
||||
cp -r templates output/
|
||||
cp init/com.github.drakkan.sftpgo.plist output/init/
|
||||
./sftpgo gen completion bash > output/bash_completion/sftpgo
|
||||
|
@ -134,13 +135,25 @@ jobs:
|
|||
xcopy .\templates .\output\templates\ /E
|
||||
mkdir output\static
|
||||
xcopy .\static .\output\static\ /E
|
||||
mkdir output\openapi
|
||||
xcopy .\openapi .\output\openapi\ /E
|
||||
$CERT_PATH=(Get-Location -PSProvider FileSystem).ProviderPath + "\cert.pfx"
|
||||
[IO.File]::WriteAllBytes($CERT_PATH,[System.Convert]::FromBase64String($Env:CERT_DATA))
|
||||
certutil -f -p "$Env:CERT_PASS" -importpfx MY "$CERT_PATH"
|
||||
rm "$CERT_PATH"
|
||||
& 'C:/Program Files (x86)/Windows Kits/10/bin/10.0.17763.0/x86/signtool.exe' sign /sm /tr http://timestamp.sectigo.com /td sha256 /fd sha256 /n "Nicola Murino" /d "SFTPGo" .\sftpgo.exe
|
||||
$INNO_S='/Ssigntool=$qC:/Program Files (x86)/Windows Kits/10/bin/10.0.17763.0/x86/signtool.exe$q sign /sm /tr http://timestamp.sectigo.com /td sha256 /fd sha256 /n $qNicola Murino$q /d $qSFTPGo$q $f'
|
||||
iscc "$INNO_S" windows-installer\sftpgo.iss
|
||||
iscc "$INNO_S" .\windows-installer\sftpgo.iss
|
||||
certutil -delstore MY "Nicola Murino"
|
||||
env:
|
||||
SFTPGO_ISS_VERSION: ${{ steps.get_version.outputs.VERSION }}
|
||||
SFTPGO_ISS_DOC_URL: https://github.com/drakkan/sftpgo/blob/${{ steps.get_version.outputs.VERSION }}/README.md
|
||||
CERT_DATA: ${{ secrets.CERT_DATA }}
|
||||
CERT_PASS: ${{ secrets.CERT_PASS }}
|
||||
|
||||
- name: Prepare Portable Release for Windows
|
||||
if: startsWith(matrix.os, 'windows-')
|
||||
run: |
|
||||
mkdir win-portable
|
||||
copy .\sftpgo.exe .\win-portable
|
||||
copy .\sftpgo.json .\win-portable
|
||||
|
@ -150,14 +163,9 @@ jobs:
|
|||
xcopy .\templates .\win-portable\templates\ /E
|
||||
mkdir win-portable\static
|
||||
xcopy .\static .\win-portable\static\ /E
|
||||
& 'C:/Program Files (x86)/Windows Kits/10/bin/10.0.17763.0/x86/signtool.exe' sign /sm /tr http://timestamp.sectigo.com /td sha256 /fd sha256 /n "Nicola Murino" /d "SFTPGo" .\win-portable\sftpgo.exe
|
||||
mkdir win-portable\openapi
|
||||
xcopy .\openapi .\win-portable\openapi\ /E
|
||||
Compress-Archive .\win-portable\* sftpgo_portable_x86_64.zip
|
||||
certutil -delstore MY "Nicola Murino"
|
||||
env:
|
||||
SFTPGO_ISS_VERSION: ${{ steps.get_version.outputs.VERSION }}
|
||||
SFTPGO_ISS_DOC_URL: https://github.com/drakkan/sftpgo/blob/${{ steps.get_version.outputs.VERSION }}/README.md
|
||||
CERT_DATA: ${{ secrets.CERT_DATA }}
|
||||
CERT_PASS: ${{ secrets.CERT_PASS }}
|
||||
|
||||
- name: Upload macOS x86_64 artifact
|
||||
if: startsWith(matrix.os, 'macos-')
|
||||
|
@ -250,6 +258,7 @@ jobs:
|
|||
cp sftpgo.json output/
|
||||
cp -r templates output/
|
||||
cp -r static output/
|
||||
cp -r openapi output/
|
||||
cp init/sftpgo.service output/init/
|
||||
./sftpgo initprovider
|
||||
./sftpgo gen completion bash > output/bash_completion/sftpgo
|
||||
|
@ -297,6 +306,7 @@ jobs:
|
|||
cp sftpgo.json output/
|
||||
cp -r templates output/
|
||||
cp -r static output/
|
||||
cp -r openapi output/
|
||||
cp init/sftpgo.service output/init/
|
||||
./sftpgo initprovider
|
||||
./sftpgo gen completion bash > output/bash_completion/sftpgo
|
||||
|
|
|
@ -42,6 +42,7 @@ RUN groupadd --system -g 1000 sftpgo && \
|
|||
COPY --from=builder /workspace/sftpgo.json /etc/sftpgo/sftpgo.json
|
||||
COPY --from=builder /workspace/templates /usr/share/sftpgo/templates
|
||||
COPY --from=builder /workspace/static /usr/share/sftpgo/static
|
||||
COPY --from=builder /workspace/openapi /usr/share/sftpgo/openapi
|
||||
COPY --from=builder /workspace/sftpgo /usr/local/bin/
|
||||
|
||||
# Log to the stdout so the logs will be available using docker logs
|
||||
|
@ -50,6 +51,7 @@ ENV SFTPGO_LOG_FILE_PATH=""
|
|||
ENV SFTPGO_HTTPD__TEMPLATES_PATH=/usr/share/sftpgo/templates
|
||||
ENV SFTPGO_SMTP__TEMPLATES_PATH=/usr/share/sftpgo/templates
|
||||
ENV SFTPGO_HTTPD__STATIC_FILES_PATH=/usr/share/sftpgo/static
|
||||
ENV SFTPGO_HTTPD__OPENAPI_PATH=/usr/share/sftpgo/openapi
|
||||
|
||||
# Modify the default configuration file
|
||||
RUN sed -i "s|\"users_base_dir\": \"\",|\"users_base_dir\": \"/srv/sftpgo/data\",|" /etc/sftpgo/sftpgo.json && \
|
||||
|
|
|
@ -47,6 +47,7 @@ RUN addgroup -g 1000 -S sftpgo && \
|
|||
COPY --from=builder /workspace/sftpgo.json /etc/sftpgo/sftpgo.json
|
||||
COPY --from=builder /workspace/templates /usr/share/sftpgo/templates
|
||||
COPY --from=builder /workspace/static /usr/share/sftpgo/static
|
||||
COPY --from=builder /workspace/openapi /usr/share/sftpgo/openapi
|
||||
COPY --from=builder /workspace/sftpgo /usr/local/bin/
|
||||
|
||||
# Log to the stdout so the logs will be available using docker logs
|
||||
|
@ -55,6 +56,7 @@ ENV SFTPGO_LOG_FILE_PATH=""
|
|||
ENV SFTPGO_HTTPD__TEMPLATES_PATH=/usr/share/sftpgo/templates
|
||||
ENV SFTPGO_SMTP__TEMPLATES_PATH=/usr/share/sftpgo/templates
|
||||
ENV SFTPGO_HTTPD__STATIC_FILES_PATH=/usr/share/sftpgo/static
|
||||
ENV SFTPGO_HTTPD__OPENAPI_PATH=/usr/share/sftpgo/openapi
|
||||
|
||||
# Modify the default configuration file
|
||||
RUN sed -i "s|\"users_base_dir\": \"\",|\"users_base_dir\": \"/srv/sftpgo/data\",|" /etc/sftpgo/sftpgo.json && \
|
||||
|
|
|
@ -40,6 +40,7 @@ COPY --from=builder --chown=1000:1000 /var/lib/sftpgo /var/lib/sftpgo
|
|||
COPY --from=builder --chown=1000:1000 /workspace/sftpgo.json /etc/sftpgo/sftpgo.json
|
||||
COPY --from=builder /workspace/templates /usr/share/sftpgo/templates
|
||||
COPY --from=builder /workspace/static /usr/share/sftpgo/static
|
||||
COPY --from=builder /workspace/openapi /usr/share/sftpgo/openapi
|
||||
COPY --from=builder /workspace/sftpgo /usr/local/bin/
|
||||
COPY --from=builder /etc/mime.types /etc/mime.types
|
||||
|
||||
|
@ -49,6 +50,7 @@ ENV SFTPGO_LOG_FILE_PATH=""
|
|||
ENV SFTPGO_HTTPD__TEMPLATES_PATH=/usr/share/sftpgo/templates
|
||||
ENV SFTPGO_SMTP__TEMPLATES_PATH=/usr/share/sftpgo/templates
|
||||
ENV SFTPGO_HTTPD__STATIC_FILES_PATH=/usr/share/sftpgo/static
|
||||
ENV SFTPGO_HTTPD__OPENAPI_PATH=/usr/share/sftpgo/openapi
|
||||
# These env vars are required to avoid the following error when calling user.Current():
|
||||
# unable to get the current user: user: Current requires cgo or $USER set in environment
|
||||
ENV USER=sftpgo
|
||||
|
|
|
@ -194,7 +194,7 @@ After starting SFTPGo you can manage users and folders using:
|
|||
|
||||
To support embedded data providers like `bolt` and `SQLite` we can't have a CLI that directly write users and folders to the data provider, we always have to use the REST API.
|
||||
|
||||
Full details for users, folders, admins and other resources are documented in the [OpenAPI](/httpd/schema/openapi.yaml) schema. If you want to render the schema without importing it manually, you can explore it on [Stoplight](https://sftpgo.stoplight.io/docs/sftpgo/openapi.yaml).
|
||||
Full details for users, folders, admins and other resources are documented in the [OpenAPI](/openapi/openapi.yaml) schema. If you want to render the schema without importing it manually, you can explore it on [Stoplight](https://sftpgo.stoplight.io/docs/sftpgo/openapi.yaml).
|
||||
|
||||
## Tutorials
|
||||
|
||||
|
|
|
@ -78,6 +78,7 @@ var (
|
|||
TLSCipherSuites: nil,
|
||||
ProxyAllowed: nil,
|
||||
HideLoginURL: 0,
|
||||
RenderOpenAPI: true,
|
||||
}
|
||||
defaultRateLimiter = common.RateLimiterConfig{
|
||||
Average: 0,
|
||||
|
@ -273,6 +274,7 @@ func Init() {
|
|||
TemplatesPath: "templates",
|
||||
StaticFilesPath: "static",
|
||||
BackupsPath: "backups",
|
||||
OpenAPIPath: "openapi",
|
||||
WebRoot: "",
|
||||
CertificateFile: "",
|
||||
CertificateKeyFile: "",
|
||||
|
@ -992,6 +994,7 @@ func getHTTPDBindingFromEnv(idx int) {
|
|||
binding := httpd.Binding{
|
||||
EnableWebAdmin: true,
|
||||
EnableWebClient: true,
|
||||
RenderOpenAPI: true,
|
||||
}
|
||||
if len(globalConf.HTTPDConfig.Bindings) > idx {
|
||||
binding = globalConf.HTTPDConfig.Bindings[idx]
|
||||
|
@ -1023,6 +1026,12 @@ func getHTTPDBindingFromEnv(idx int) {
|
|||
isSet = true
|
||||
}
|
||||
|
||||
renderOpenAPI, ok := lookupBoolFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__RENDER_OPENAPI", idx))
|
||||
if ok {
|
||||
binding.RenderOpenAPI = renderOpenAPI
|
||||
isSet = true
|
||||
}
|
||||
|
||||
enableHTTPS, ok := lookupBoolFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__ENABLE_HTTPS", idx))
|
||||
if ok {
|
||||
binding.EnableHTTPS = enableHTTPS
|
||||
|
@ -1219,6 +1228,7 @@ func setViperDefaults() {
|
|||
viper.SetDefault("httpd.templates_path", globalConf.HTTPDConfig.TemplatesPath)
|
||||
viper.SetDefault("httpd.static_files_path", globalConf.HTTPDConfig.StaticFilesPath)
|
||||
viper.SetDefault("httpd.backups_path", globalConf.HTTPDConfig.BackupsPath)
|
||||
viper.SetDefault("httpd.openapi_path", globalConf.HTTPDConfig.OpenAPIPath)
|
||||
viper.SetDefault("httpd.web_root", globalConf.HTTPDConfig.WebRoot)
|
||||
viper.SetDefault("httpd.certificate_file", globalConf.HTTPDConfig.CertificateFile)
|
||||
viper.SetDefault("httpd.certificate_key_file", globalConf.HTTPDConfig.CertificateKeyFile)
|
||||
|
|
|
@ -752,6 +752,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
|
|||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__PORT", "9000")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__ENABLE_WEB_ADMIN", "0")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__ENABLE_WEB_CLIENT", "0")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__RENDER_OPENAPI", "0")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__ENABLE_HTTPS", "1 ")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__CLIENT_AUTH_TYPE", "1")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__TLS_CIPHER_SUITES", " TLS_AES_256_GCM_SHA384 , TLS_CHACHA20_POLY1305_SHA256")
|
||||
|
@ -770,6 +771,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
|
|||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__ENABLE_HTTPS")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__ENABLE_WEB_ADMIN")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__ENABLE_WEB_CLIENT")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__RENDER_OPENAPI")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__CLIENT_AUTH_TYPE")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__TLS_CIPHER_SUITES")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__PROXY_ALLOWED")
|
||||
|
@ -786,6 +788,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
|
|||
require.False(t, bindings[0].EnableHTTPS)
|
||||
require.True(t, bindings[0].EnableWebAdmin)
|
||||
require.True(t, bindings[0].EnableWebClient)
|
||||
require.True(t, bindings[0].RenderOpenAPI)
|
||||
require.Len(t, bindings[0].TLSCipherSuites, 1)
|
||||
require.Equal(t, "TLS_AES_128_GCM_SHA256", bindings[0].TLSCipherSuites[0])
|
||||
require.Equal(t, 0, bindings[0].HideLoginURL)
|
||||
|
@ -794,6 +797,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
|
|||
require.False(t, bindings[1].EnableHTTPS)
|
||||
require.True(t, bindings[1].EnableWebAdmin)
|
||||
require.True(t, bindings[1].EnableWebClient)
|
||||
require.True(t, bindings[1].RenderOpenAPI)
|
||||
require.Nil(t, bindings[1].TLSCipherSuites)
|
||||
require.Equal(t, 1, bindings[1].HideLoginURL)
|
||||
|
||||
|
@ -802,6 +806,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
|
|||
require.True(t, bindings[2].EnableHTTPS)
|
||||
require.False(t, bindings[2].EnableWebAdmin)
|
||||
require.False(t, bindings[2].EnableWebClient)
|
||||
require.False(t, bindings[2].RenderOpenAPI)
|
||||
require.Equal(t, 1, bindings[2].ClientAuthType)
|
||||
require.Len(t, bindings[2].TLSCipherSuites, 2)
|
||||
require.Equal(t, "TLS_AES_256_GCM_SHA384", bindings[2].TLSCipherSuites[0])
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Account's configuration properties
|
||||
|
||||
Please take a look at the [OpenAPI schema](../httpd/schema/openapi.yaml) for the exact definitions of user, folder and admin fields.
|
||||
Please take a look at the [OpenAPI schema](../openapi/openapi.yaml) for the exact definitions of user, folder and admin fields.
|
||||
If you need an example you can export a dump using the Web Admin or by invoking the `dumpdata` endpoint directly, you need to obtain an access token first, for example:
|
||||
|
||||
```shell
|
||||
|
|
|
@ -103,7 +103,7 @@ If the `hook` defines an HTTP URL then this URL will be invoked as HTTP POST. Th
|
|||
|
||||
The HTTP hook will use the global configuration for HTTP clients and will respect the retry configurations.
|
||||
|
||||
The structure for SFTPGo objects can be found within the [OpenAPI schema](../httpd/schema/openapi.yaml).
|
||||
The structure for SFTPGo objects can be found within the [OpenAPI schema](../openapi/openapi.yaml).
|
||||
|
||||
## Pub/Sub services
|
||||
|
||||
|
|
|
@ -56,4 +56,4 @@ fi
|
|||
|
||||
Please note that this is a demo program and it might not work in all cases. For example, the username should be obtained by parsing the JSON serialized user and not by searching the username inside the JSON as shown here.
|
||||
|
||||
The structure for SFTPGo users can be found within the [OpenAPI schema](../httpd/schema/openapi.yaml).
|
||||
The structure for SFTPGo users can be found within the [OpenAPI schema](../openapi/openapi.yaml).
|
||||
|
|
|
@ -66,7 +66,7 @@ else
|
|||
fi
|
||||
```
|
||||
|
||||
The structure for SFTPGo users can be found within the [OpenAPI schema](../httpd/schema/openapi.yaml).
|
||||
The structure for SFTPGo users can be found within the [OpenAPI schema](../openapi/openapi.yaml).
|
||||
|
||||
You can disable the hook on a per-user basis so that you can mix external and internal users.
|
||||
|
||||
|
|
|
@ -222,9 +222,11 @@ The configuration file contains the following sections:
|
|||
- `tls_cipher_suites`, list of strings. List of supported cipher suites for TLS version 1.2. If empty, a default list of secure cipher suites is used, with a preference order based on hardware performance. Note that TLS 1.3 ciphersuites are not configurable. The supported ciphersuites names are defined [here](https://github.com/golang/go/blob/master/src/crypto/tls/cipher_suites.go#L52). Any invalid name will be silently ignored. The order matters, the ciphers listed first will be the preferred ones. Default: empty.
|
||||
- `proxy_allowed`, list of IP addresses and IP ranges allowed to set `X-Forwarded-For`, `X-Real-IP`, `X-Forwarded-Proto`, `CF-Connecting-IP`, `True-Client-IP` headers. Any of the indicated headers, if set on requests from a connection address not in this list, will be silently ignored. Default: empty.
|
||||
- `hide_login_url`, integer. If both web admin and web client are enabled each login page will show a link to the other one. This setting allows to hide this link. 0 means that the login links are displayed on both admin and client login page. This is the default. 1 means that the login link to the web client login page is hidden on admin login page. 2 means that the login link to the web admin login page is hidden on client login page. The flags can be combined, for example 3 will disable both login links.
|
||||
- `render_openapi`, boolean. Set to `false` to disable serving of the OpenAPI schema and renderer. Default `true`.
|
||||
- `templates_path`, string. Path to the HTML web templates. This can be an absolute path or a path relative to the config dir
|
||||
- `static_files_path`, string. Path to the static files for the web interface. This can be an absolute path or a path relative to the config dir. If both `templates_path` and `static_files_path` are empty the built-in web interface will be disabled
|
||||
- `backups_path`, string. Path to the backup directory. This can be an absolute path or a path relative to the config dir. We don't allow backups in arbitrary paths for security reasons
|
||||
- `openapi_path`, string. Path to the directory that contains the OpenAPI schema and the default renderer. This can be an absolute path or a path relative to the config dir. If empty the OpenAPI schema and the renderer will not be served regardless of the `render_openapi` directive
|
||||
- `web_root`, string. Defines a base URL for the web admin and client interfaces. If empty web admin and client resources will be available at the root ("/") URI. If defined it must be an absolute URI or it will be ignored
|
||||
- `certificate_file`, string. Certificate for HTTPS. This can be an absolute path or a path relative to the config dir.
|
||||
- `certificate_key_file`, string. Private key matching the above certificate. This can be an absolute path or a path relative to the config dir. If both the certificate and the private key are provided, the server will expect HTTPS connections. Certificate and key files can be reloaded on demand sending a `SIGHUP` signal on Unix based systems and a `paramchange` request to the running service on Windows.
|
||||
|
|
|
@ -69,17 +69,21 @@ Open the SFTPGo configuration file, search for the `httpd` section and change it
|
|||
"enable_https": true,
|
||||
"client_auth_type": 0,
|
||||
"tls_cipher_suites": [],
|
||||
"proxy_allowed": []
|
||||
"proxy_allowed": [],
|
||||
"hide_login_url": 0,
|
||||
"render_openapi": true
|
||||
}
|
||||
],
|
||||
"templates_path": "/usr/share/sftpgo/templates",
|
||||
"static_files_path": "/usr/share/sftpgo/static",
|
||||
"backups_path": "/srv/sftpgo/backups",
|
||||
"openapi_path": "/srv/sftpgo/openapi",
|
||||
"web_root": "",
|
||||
"certificate_file": "/etc/sftpgo/certs/sftpgo.com.crt",
|
||||
"certificate_key_file": "/etc/sftpgo/certs/sftpgo.com.key",
|
||||
"ca_certificates": [],
|
||||
"ca_revocation_lists": []
|
||||
"ca_revocation_lists": [],
|
||||
....
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ The program must finish within 20 seconds.
|
|||
If the hook is an HTTP URL then it will be invoked as HTTP POST. The login method, the used protocol, the ip address and the status of the user are added to the query string, for example `<http_url>?login_method=password&ip=1.2.3.4&protocol=SSH&status=1`.
|
||||
The request body will contain the user serialized as JSON.
|
||||
|
||||
The structure for SFTPGo users can be found within the [OpenAPI schema](../httpd/schema/openapi.yaml).
|
||||
The structure for SFTPGo users can be found within the [OpenAPI schema](../openapi/openapi.yaml).
|
||||
|
||||
The HTTP hook will use the global configuration for HTTP clients and will respect the retry configurations.
|
||||
|
||||
|
|
|
@ -94,8 +94,8 @@ You can find an example script that shows how to manage data retention [here](..
|
|||
|
||||
:warning: Deleting files is an irreversible action, please make sure you fully understand what you are doing before using this feature, you may have users with overlapping home directories or virtual folders shared between multiple users, it is relatively easy to inadvertently delete files you need.
|
||||
|
||||
The OpenAPI 3 schema for the exposed API can be found inside the source tree: [openapi.yaml](../httpd/schema/openapi.yaml "OpenAPI 3 specs"). If you want to render the schema without importing it manually, you can explore it on [Stoplight](https://sftpgo.stoplight.io/docs/sftpgo/openapi.yaml).
|
||||
The OpenAPI 3 schema for the exposed API can be found inside the source tree: [openapi.yaml](../openapi/openapi.yaml "OpenAPI 3 specs"). You can render the schema and try the API using the `/openapi` endpoint. SFTPGo uses by default [Swagger UI](https://github.com/swagger-api/swagger-ui), you can use another renderer just by copying it to the defined OpenAPI path.
|
||||
|
||||
You can generate your own REST client in your preferred programming language, or even bash scripts, using an OpenAPI generator such as [swagger-codegen](https://github.com/swagger-api/swagger-codegen) or [OpenAPI Generator](https://openapi-generator.tech/).
|
||||
You can also explore the schema on [Stoplight](https://sftpgo.stoplight.io/docs/sftpgo/openapi.yaml).
|
||||
|
||||
You can also use [Swagger UI](https://github.com/swagger-api/swagger-ui).
|
||||
You can generate your own REST API client in your preferred programming language, or even bash scripts, using an OpenAPI generator such as [swagger-codegen](https://github.com/swagger-api/swagger-codegen) or [OpenAPI Generator](https://openapi-generator.tech/).
|
||||
|
|
|
@ -38,6 +38,7 @@ sudo install -Dm644 sftpgo.json /etc/sftpgo/
|
|||
# override some configuration keys using environment variables
|
||||
sudo sh -c 'echo "SFTPGO_HTTPD__TEMPLATES_PATH=/usr/share/sftpgo/templates" > /etc/sftpgo/sftpgo.env'
|
||||
sudo sh -c 'echo "SFTPGO_HTTPD__STATIC_FILES_PATH=/usr/share/sftpgo/static" >> /etc/sftpgo/sftpgo.env'
|
||||
sudo sh -c 'echo "SFTPGO_HTTPD__OPENAPI_PATH=/usr/share/sftpgo/openapi" >> /etc/sftpgo/sftpgo.env'
|
||||
sudo sh -c 'echo "SFTPGO_HTTPD__BACKUPS_PATH=/var/lib/sftpgo/backups" >> /etc/sftpgo/sftpgo.env'
|
||||
sudo sh -c 'echo "SFTPGO_DATA_PROVIDER__CREDENTIALS_PATH=/var/lib/sftpgo/credentials" >> /etc/sftpgo/sftpgo.env'
|
||||
# if you use a file based data provider such as sqlite or bolt consider to set the database path too, for example:
|
||||
|
|
|
@ -26,7 +26,8 @@ Know issues:
|
|||
- removing a directory tree on Cloud Storage backends could generate a `not found` error when removing the last (virtual) directory. This happens if the client cycles the directories tree itself and removes files and directories one by one instead of issuing a single remove command
|
||||
- the used [WebDAV library](https://pkg.go.dev/golang.org/x/net/webdav?tab=doc) asks to open a file to execute a `stat` and sometimes reads some bytes to find the content type. Stat calls are executed before and after a download too, so to be able to properly list a directory you need to grant both `list` and `download` permissions and to be able to upload files you need to gran both `list` and `upload` permissions
|
||||
- the used [WebDAV library](https://pkg.go.dev/golang.org/x/net/webdav?tab=doc) not always returns a proper error code/message, most of the times it simply returns `Method not Allowed`. I'll try to improve the library error codes in the future
|
||||
- if a file or a directory cannot be accessed, for example due to OS permissions issues or because a mapped path for a virtual folder is a missing, it will be omitted from the directory listing. If there is a different error then the whole directory listing will fail. This behavior is different from SFTP/FTP where you will be able to see the problematic file/directory in the directory listing, you will only get an error if you try to access it.
|
||||
- if a file or a directory cannot be accessed, for example due to OS permissions issues or because a mapped path for a virtual folder is a missing, it will be omitted from the directory listing. If there is a different error then the whole directory listing will fail. This behavior is different from SFTP/FTP where you will be able to see the problematic file/directory in the directory listing, you will only get an error if you try to access it
|
||||
- if you use the native Windows client please check its usage and pay particular attention to the [registry settings](https://docs.microsoft.com/en-us/iis/publish/using-webdav/using-the-webdav-redirector#webdav-redirector-registry-settings). The default file size limit is 50MB and if you don't configure SFTPGo to use HTTPS you have to set `BasicAuthLevel` to `2`
|
||||
|
||||
We plan to add [Dead Properties](https://tools.ietf.org/html/rfc4918#section-3) support in future releases. We need a design decision here, probably the best solution is to store dead properties inside the data provider but this could increase a lot its size. Alternately we could store them on disk for local filesystem and add as metadata for Cloud Storage, this means that we need to do a separate `HEAD` request to retrieve dead properties for an S3 file. For big folders will do a lot of requests to the Cloud Provider, I don't like this solution. Another option is to expose a hook and allow you to implement `dead properties` outside SFTPGo.
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# REST API CLI client
|
||||
|
||||
:warning: This sample client is deprecated and it will work only with API V1 (SFTPGo <= 1.2.2). You can easily build your own client from the [OpenAPI](../../httpd/schema/openapi.yaml) schema or use [Swagger UI](https://github.com/swagger-api/swagger-ui).
|
||||
:warning: This sample client is deprecated and it will work only with API V1 (SFTPGo <= 1.2.2). You can easily build your own client from the [OpenAPI](../../openapi/openapi.yaml) schema or use [Swagger UI](https://github.com/swagger-api/swagger-ui).
|
||||
|
||||
`sftpgo_api_cli` is a very simple command line client for `SFTPGo` REST API written in python.
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Package httpd implements REST API and Web interface for SFTPGo.
|
||||
// The OpenAPI 3 schema for the exposed API can be found inside the source tree:
|
||||
// https://github.com/drakkan/sftpgo/blob/main/httpd/schema/openapi.yaml
|
||||
// https://github.com/drakkan/sftpgo/blob/main/openapi/openapi.yaml
|
||||
package httpd
|
||||
|
||||
import (
|
||||
|
@ -137,6 +137,7 @@ const (
|
|||
webClientForgotPwdPathDefault = "/web/client/forgot-password"
|
||||
webClientResetPwdPathDefault = "/web/client/reset-password"
|
||||
webStaticFilesPathDefault = "/static"
|
||||
webOpenAPIPathDefault = "/openapi"
|
||||
// MaxRestoreSize defines the max size for the loaddata input file
|
||||
MaxRestoreSize = 10485760 // 10 MB
|
||||
maxRequestSize = 1048576 // 1MB
|
||||
|
@ -210,6 +211,7 @@ var (
|
|||
webClientForgotPwdPath string
|
||||
webClientResetPwdPath string
|
||||
webStaticFilesPath string
|
||||
webOpenAPIPath string
|
||||
// max upload size for http clients, 1GB by default
|
||||
maxUploadFileSize = int64(1048576000)
|
||||
)
|
||||
|
@ -256,7 +258,9 @@ type Binding struct {
|
|||
// - 1 the login link to the web client login page is hidden on admin login page
|
||||
// - 2 the login link to the web admin login page is hidden on client login page
|
||||
// The flags can be combined, for example 3 will disable both login links.
|
||||
HideLoginURL int `json:"hide_login_url" mapstructure:"hide_login_url"`
|
||||
HideLoginURL int `json:"hide_login_url" mapstructure:"hide_login_url"`
|
||||
// Enable the built-in OpenAPI renderer
|
||||
RenderOpenAPI bool `json:"render_openapi" mapstructure:"render_openapi"`
|
||||
allowHeadersFrom []func(net.IP) bool
|
||||
}
|
||||
|
||||
|
@ -341,6 +345,9 @@ type Conf struct {
|
|||
StaticFilesPath string `json:"static_files_path" mapstructure:"static_files_path"`
|
||||
// Path to the backup directory. This can be an absolute path or a path relative to the config dir
|
||||
BackupsPath string `json:"backups_path" mapstructure:"backups_path"`
|
||||
// Path to the directory that contains the OpenAPI schema and the default renderer.
|
||||
// This can be an absolute path or a path relative to the config dir
|
||||
OpenAPIPath string `json:"openapi_path" mapstructure:"openapi_path"`
|
||||
// Defines a base URL for the web admin and client interfaces. If empty web admin and client resources will
|
||||
// be available at the root ("/") URI. If defined it must be an absolute URI or it will be ignored.
|
||||
WebRoot string `json:"web_root" mapstructure:"web_root"`
|
||||
|
@ -420,6 +427,7 @@ func (c *Conf) Initialize(configDir string) error {
|
|||
backupsPath = getConfigPath(c.BackupsPath, configDir)
|
||||
staticFilesPath := getConfigPath(c.StaticFilesPath, configDir)
|
||||
templatesPath := getConfigPath(c.TemplatesPath, configDir)
|
||||
openAPIPath := getConfigPath(c.OpenAPIPath, configDir)
|
||||
if backupsPath == "" {
|
||||
return fmt.Errorf("required directory is invalid, backup path %#v", backupsPath)
|
||||
}
|
||||
|
@ -469,7 +477,7 @@ func (c *Conf) Initialize(configDir string) error {
|
|||
}
|
||||
|
||||
go func(b Binding) {
|
||||
server := newHttpdServer(b, staticFilesPath, c.SigningPassphrase, c.Cors)
|
||||
server := newHttpdServer(b, staticFilesPath, c.SigningPassphrase, c.Cors, openAPIPath)
|
||||
|
||||
exitChannel <- server.listenAndServe()
|
||||
}(binding)
|
||||
|
@ -603,6 +611,7 @@ func updateWebAdminURLs(baseURL string) {
|
|||
webDefenderHostsPath = path.Join(baseURL, webDefenderHostsPathDefault)
|
||||
webDefenderPath = path.Join(baseURL, webDefenderPathDefault)
|
||||
webStaticFilesPath = path.Join(baseURL, webStaticFilesPathDefault)
|
||||
webOpenAPIPath = path.Join(baseURL, webOpenAPIPathDefault)
|
||||
}
|
||||
|
||||
// GetHTTPRouter returns an HTTP handler suitable to use for test cases
|
||||
|
@ -612,8 +621,9 @@ func GetHTTPRouter() http.Handler {
|
|||
Port: 8080,
|
||||
EnableWebAdmin: true,
|
||||
EnableWebClient: true,
|
||||
RenderOpenAPI: true,
|
||||
}
|
||||
server := newHttpdServer(b, "../static", "", CorsConfig{})
|
||||
server := newHttpdServer(b, "../static", "", CorsConfig{}, "../openapi")
|
||||
server.initializeRouter()
|
||||
return server.router
|
||||
}
|
||||
|
|
|
@ -14837,9 +14837,37 @@ func TestGetWebStatusMock(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestStaticFilesMock(t *testing.T) {
|
||||
req, _ := http.NewRequest(http.MethodGet, "/static/favicon.ico", nil)
|
||||
req, err := http.NewRequest(http.MethodGet, "/static/favicon.ico", nil)
|
||||
assert.NoError(t, err)
|
||||
rr := executeRequest(req)
|
||||
checkResponseCode(t, http.StatusOK, rr)
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, "/openapi/openapi.yaml", nil)
|
||||
assert.NoError(t, err)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusOK, rr)
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, "/static", nil)
|
||||
assert.NoError(t, err)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusMovedPermanently, rr)
|
||||
location := rr.Header().Get("Location")
|
||||
assert.Equal(t, "/static/", location)
|
||||
req, err = http.NewRequest(http.MethodGet, location, nil)
|
||||
assert.NoError(t, err)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusOK, rr)
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, "/openapi", nil)
|
||||
assert.NoError(t, err)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusMovedPermanently, rr)
|
||||
location = rr.Header().Get("Location")
|
||||
assert.Equal(t, "/openapi/", location)
|
||||
req, err = http.NewRequest(http.MethodGet, location, nil)
|
||||
assert.NoError(t, err)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusOK, rr)
|
||||
}
|
||||
|
||||
func waitForUsersQuotaScan(t *testing.T, token string) {
|
||||
|
|
|
@ -1406,7 +1406,7 @@ func TestProxyHeaders(t *testing.T) {
|
|||
}
|
||||
err = b.parseAllowedProxy()
|
||||
assert.NoError(t, err)
|
||||
server := newHttpdServer(b, "", "", CorsConfig{Enabled: true})
|
||||
server := newHttpdServer(b, "", "", CorsConfig{Enabled: true}, "")
|
||||
server.initializeRouter()
|
||||
testServer := httptest.NewServer(server.router)
|
||||
defer testServer.Close()
|
||||
|
@ -1492,7 +1492,7 @@ func TestRecoverer(t *testing.T) {
|
|||
EnableWebAdmin: true,
|
||||
EnableWebClient: false,
|
||||
}
|
||||
server := newHttpdServer(b, "../static", "", CorsConfig{})
|
||||
server := newHttpdServer(b, "../static", "", CorsConfig{}, "../openapi")
|
||||
server.initializeRouter()
|
||||
server.router.Get(recoveryPath, func(w http.ResponseWriter, r *http.Request) {
|
||||
panic("panic")
|
||||
|
@ -1607,7 +1607,7 @@ func TestWebAdminRedirect(t *testing.T) {
|
|||
EnableWebAdmin: true,
|
||||
EnableWebClient: false,
|
||||
}
|
||||
server := newHttpdServer(b, "../static", "", CorsConfig{})
|
||||
server := newHttpdServer(b, "../static", "", CorsConfig{}, "../openapi")
|
||||
server.initializeRouter()
|
||||
testServer := httptest.NewServer(server.router)
|
||||
defer testServer.Close()
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
@ -37,20 +38,29 @@ var (
|
|||
type httpdServer struct {
|
||||
binding Binding
|
||||
staticFilesPath string
|
||||
openAPIPath string
|
||||
enableWebAdmin bool
|
||||
enableWebClient bool
|
||||
renderOpenAPI bool
|
||||
router *chi.Mux
|
||||
tokenAuth *jwtauth.JWTAuth
|
||||
signingPassphrase string
|
||||
cors CorsConfig
|
||||
}
|
||||
|
||||
func newHttpdServer(b Binding, staticFilesPath, signingPassphrase string, cors CorsConfig) *httpdServer {
|
||||
func newHttpdServer(b Binding, staticFilesPath, signingPassphrase string, cors CorsConfig,
|
||||
openAPIPath string,
|
||||
) *httpdServer {
|
||||
if openAPIPath == "" {
|
||||
b.RenderOpenAPI = false
|
||||
}
|
||||
return &httpdServer{
|
||||
binding: b,
|
||||
staticFilesPath: staticFilesPath,
|
||||
openAPIPath: openAPIPath,
|
||||
enableWebAdmin: b.EnableWebAdmin,
|
||||
enableWebClient: b.EnableWebClient,
|
||||
renderOpenAPI: b.RenderOpenAPI,
|
||||
signingPassphrase: signingPassphrase,
|
||||
cors: cors,
|
||||
}
|
||||
|
@ -940,6 +950,17 @@ func (s *httpdServer) redirectToWebPath(w http.ResponseWriter, r *http.Request,
|
|||
}
|
||||
}
|
||||
|
||||
func (s *httpdServer) isStaticFileURL(r *http.Request) bool {
|
||||
var urlPath string
|
||||
rctx := chi.RouteContext(r.Context())
|
||||
if rctx != nil && rctx.RoutePath != "" {
|
||||
urlPath = rctx.RoutePath
|
||||
} else {
|
||||
urlPath = r.URL.Path
|
||||
}
|
||||
return !strings.HasPrefix(urlPath, webOpenAPIPath) && !strings.HasPrefix(urlPath, webStaticFilesPath)
|
||||
}
|
||||
|
||||
func (s *httpdServer) initializeRouter() {
|
||||
s.tokenAuth = jwtauth.New(jwa.HS256.String(), getSigningKey(s.signingPassphrase), nil)
|
||||
s.router = chi.NewRouter()
|
||||
|
@ -960,7 +981,8 @@ func (s *httpdServer) initializeRouter() {
|
|||
s.router.Use(c.Handler)
|
||||
}
|
||||
s.router.Use(middleware.GetHead)
|
||||
s.router.Use(middleware.StripSlashes)
|
||||
// StripSlashes causes infinite redirects at the root path if used with http.FileServer
|
||||
s.router.Use(middleware.Maybe(middleware.StripSlashes, s.isStaticFileURL))
|
||||
|
||||
s.router.NotFound(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
|
||||
|
@ -1135,6 +1157,13 @@ func (s *httpdServer) initializeRouter() {
|
|||
router.With(checkHTTPUserPerm(sdk.WebClientSharesDisabled)).Delete(userSharesPath+"/{id}", deleteShare)
|
||||
})
|
||||
|
||||
if s.renderOpenAPI {
|
||||
s.router.Group(func(router chi.Router) {
|
||||
router.Use(compressor.Handler)
|
||||
fileServer(router, webOpenAPIPath, http.Dir(s.openAPIPath))
|
||||
})
|
||||
}
|
||||
|
||||
if s.enableWebAdmin || s.enableWebClient {
|
||||
s.router.Group(func(router chi.Router) {
|
||||
router.Use(compressor.Handler)
|
||||
|
|
BIN
openapi/swagger-ui/favicon-16x16.png
Normal file
BIN
openapi/swagger-ui/favicon-16x16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 665 B |
BIN
openapi/swagger-ui/favicon-32x32.png
Normal file
BIN
openapi/swagger-ui/favicon-32x32.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 628 B |
60
openapi/swagger-ui/index.html
Normal file
60
openapi/swagger-ui/index.html
Normal file
|
@ -0,0 +1,60 @@
|
|||
<!-- HTML for static distribution bundle build -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Swagger UI</title>
|
||||
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" />
|
||||
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
|
||||
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
|
||||
<style>
|
||||
html
|
||||
{
|
||||
box-sizing: border-box;
|
||||
overflow: -moz-scrollbars-vertical;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after
|
||||
{
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body
|
||||
{
|
||||
margin:0;
|
||||
background: #fafafa;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="swagger-ui"></div>
|
||||
|
||||
<script src="./swagger-ui-bundle.js" charset="UTF-8"> </script>
|
||||
<script src="./swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
// Begin Swagger UI call region
|
||||
const ui = SwaggerUIBundle({
|
||||
url: "../openapi.yaml",
|
||||
dom_id: '#swagger-ui',
|
||||
deepLinking: true,
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis,
|
||||
SwaggerUIStandalonePreset
|
||||
],
|
||||
plugins: [
|
||||
SwaggerUIBundle.plugins.DownloadUrl
|
||||
],
|
||||
layout: "StandaloneLayout"
|
||||
});
|
||||
// End Swagger UI call region
|
||||
|
||||
window.ui = ui;
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
3
openapi/swagger-ui/swagger-ui-bundle.js
Normal file
3
openapi/swagger-ui/swagger-ui-bundle.js
Normal file
File diff suppressed because one or more lines are too long
3
openapi/swagger-ui/swagger-ui-standalone-preset.js
Normal file
3
openapi/swagger-ui/swagger-ui-standalone-preset.js
Normal file
File diff suppressed because one or more lines are too long
4
openapi/swagger-ui/swagger-ui.css
Normal file
4
openapi/swagger-ui/swagger-ui.css
Normal file
File diff suppressed because one or more lines are too long
|
@ -42,6 +42,7 @@ sed -i "s|\"users_base_dir\": \"\",|\"users_base_dir\": \"/srv/sftpgo/data\",|"
|
|||
sed -i "s|\"templates\"|\"/usr/share/sftpgo/templates\"|" sftpgo.json
|
||||
sed -i "s|\"static\"|\"/usr/share/sftpgo/static\"|" sftpgo.json
|
||||
sed -i "s|\"backups\"|\"/srv/sftpgo/backups\"|" sftpgo.json
|
||||
sed -i "s|\"openapi\"|\"/usr/share/sftpgo/openapi\"|" sftpgo.json
|
||||
sed -i "s|\"credentials\"|\"/var/lib/sftpgo/credentials\"|" sftpgo.json
|
||||
|
||||
cat >nfpm.yaml <<EOF
|
||||
|
@ -82,6 +83,9 @@ contents:
|
|||
- src: "${BASE_DIR}/static/*"
|
||||
dst: "/usr/share/sftpgo/static/"
|
||||
|
||||
- src: "${BASE_DIR}/openapi/*"
|
||||
dst: "/usr/share/sftpgo/openapi/"
|
||||
|
||||
- src: "./sftpgo.json"
|
||||
dst: "/etc/sftpgo/sftpgo.json"
|
||||
type: "config|noreplace"
|
||||
|
|
|
@ -209,12 +209,14 @@
|
|||
"client_auth_type": 0,
|
||||
"tls_cipher_suites": [],
|
||||
"proxy_allowed": [],
|
||||
"hide_login_url": 0
|
||||
"hide_login_url": 0,
|
||||
"render_openapi": true
|
||||
}
|
||||
],
|
||||
"templates_path": "templates",
|
||||
"static_files_path": "static",
|
||||
"backups_path": "backups",
|
||||
"openapi_path": "openapi",
|
||||
"web_root": "",
|
||||
"certificate_file": "",
|
||||
"certificate_key_file": "",
|
||||
|
|
|
@ -52,6 +52,7 @@ Source: "{#MyAppDir}\LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion
|
|||
Source: "{#MyAppDir}\sftpgo.json"; DestDir: "{commonappdata}\{#MyAppName}"; Flags: onlyifdoesntexist uninsneveruninstall
|
||||
Source: "{#MyAppDir}\templates\*"; DestDir: "{commonappdata}\{#MyAppName}\templates"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
Source: "{#MyAppDir}\static\*"; DestDir: "{commonappdata}\{#MyAppName}\static"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
Source: "{#MyAppDir}\openapi\*"; DestDir: "{commonappdata}\{#MyAppName}\openapi"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
|
||||
[Dirs]
|
||||
Name: "{commonappdata}\{#MyAppName}\logs"; Permissions: everyone-full
|
||||
|
@ -61,6 +62,7 @@ Name: "{commonappdata}\{#MyAppName}\credentials"; Permissions: everyone-full
|
|||
[Icons]
|
||||
Name: "{group}\Web Admin"; Filename: "http://127.0.0.1:8080/web/admin";
|
||||
Name: "{group}\Web Client"; Filename: "http://127.0.0.1:8080/web/client";
|
||||
Name: "{group}\OpenAPI"; Filename: "http://127.0.0.1:8080/openapi";
|
||||
Name: "{group}\Service Control"; WorkingDir: "{app}"; Filename: "powershell.exe"; Parameters: "-Command ""Start-Process cmd \""/k cd {app} & {#MyAppExeName} service --help\"" -Verb RunAs"; Comment: "Manage SFTPGo Service"
|
||||
Name: "{group}\Documentation"; Filename: "{#DocURL}";
|
||||
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"
|
||||
|
|
Loading…
Reference in a new issue