mirror of
https://github.com/Luzifer/ots.git
synced 2025-05-12 19:12:24 -04:00
Compare commits
No commits in common. "master" and "v1.12.0" have entirely different histories.
68 changed files with 5513 additions and 3916 deletions
.eslintrc.js.git_changerelease.yaml
.github/workflows
.gitignore.golangci.ymlDockerfileDockerfile.minimalHistory.mdMakefileREADME.mdTiltfileapi.goci
cmd/ots-cli
docker-compose.ymldocs
eslint.config.mjsfrontend
go.modgo.sumi18n.yamlmain.gopackage-lock.jsonpackage.jsonpkg
renovate.jsonsrc
app.vue
yarn.lockcomponents
clipboard-button.vuecreate.vuedisplay-url.vueexplanation.vuefileDisplay.vuegrowarea.vuenavbar.vueqr-button.vuesecret-display.vue
crypto.jsglobal.d.tshelpers.jsi18n.tslangs
lato.scsslatofont
OFL.txtlato-v20-latin-ext_latin-700.woff2lato-v20-latin-ext_latin-italic.woff2lato-v20-latin-ext_latin-regular.woff2
main.jsmain.tsrouter.jsstyle.scss
151
.eslintrc.js
Normal file
151
.eslintrc.js
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
/*
|
||||||
|
* Hack to automatically load globally installed eslint modules
|
||||||
|
* on Archlinux systems placed in /usr/lib/node_modules
|
||||||
|
*
|
||||||
|
* Source: https://github.com/eslint/eslint/issues/11914#issuecomment-569108633
|
||||||
|
*/
|
||||||
|
|
||||||
|
const Module = require('module')
|
||||||
|
|
||||||
|
const hacks = [
|
||||||
|
'babel-eslint',
|
||||||
|
'eslint-plugin-vue',
|
||||||
|
]
|
||||||
|
|
||||||
|
const ModuleFindPath = Module._findPath
|
||||||
|
Module._findPath = (request, paths, isMain) => {
|
||||||
|
const r = ModuleFindPath(request, paths, isMain)
|
||||||
|
if (!r && hacks.includes(request)) {
|
||||||
|
return require.resolve(`/usr/lib/node_modules/${request}`)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ESLint configuration derived as differences from eslint:recommended
|
||||||
|
* with changes I found useful to ensure code quality and equal formatting
|
||||||
|
* https://eslint.org/docs/user-guide/configuring
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
node: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
extends: [
|
||||||
|
'plugin:vue/recommended',
|
||||||
|
'eslint:recommended', // https://eslint.org/docs/rules/
|
||||||
|
],
|
||||||
|
|
||||||
|
globals: {
|
||||||
|
process: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
parser: '@babel/eslint-parser',
|
||||||
|
requireConfigFile: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
// required to lint *.vue files
|
||||||
|
'vue',
|
||||||
|
],
|
||||||
|
|
||||||
|
reportUnusedDisableDirectives: true,
|
||||||
|
|
||||||
|
root: true,
|
||||||
|
|
||||||
|
rules: {
|
||||||
|
'array-bracket-newline': ['error', { multiline: true }],
|
||||||
|
'array-bracket-spacing': ['error'],
|
||||||
|
'arrow-body-style': ['error', 'as-needed'],
|
||||||
|
'arrow-parens': ['error', 'as-needed'],
|
||||||
|
'arrow-spacing': ['error', { after: true, before: true }],
|
||||||
|
'block-spacing': ['error'],
|
||||||
|
'brace-style': ['error', '1tbs'],
|
||||||
|
'comma-dangle': ['error', 'always-multiline'],
|
||||||
|
'comma-spacing': ['error'],
|
||||||
|
'comma-style': ['error', 'last'],
|
||||||
|
'curly': ['error'],
|
||||||
|
'default-case-last': ['error'],
|
||||||
|
'default-param-last': ['error'],
|
||||||
|
'dot-location': ['error', 'property'],
|
||||||
|
'dot-notation': ['error'],
|
||||||
|
'eol-last': ['error', 'always'],
|
||||||
|
'eqeqeq': ['error', 'always', { null: 'ignore' }],
|
||||||
|
'func-call-spacing': ['error', 'never'],
|
||||||
|
'function-paren-newline': ['error', 'multiline'],
|
||||||
|
'generator-star-spacing': ['off'], // allow async-await
|
||||||
|
'implicit-arrow-linebreak': ['error'],
|
||||||
|
'indent': ['error', 2],
|
||||||
|
'key-spacing': ['error', { afterColon: true, beforeColon: false, mode: 'strict' }],
|
||||||
|
'keyword-spacing': ['error'],
|
||||||
|
'linebreak-style': ['error', 'unix'],
|
||||||
|
'lines-between-class-members': ['error'],
|
||||||
|
'multiline-comment-style': ['warn'],
|
||||||
|
'newline-per-chained-call': ['error'],
|
||||||
|
'no-alert': ['error'],
|
||||||
|
'no-console': ['off'],
|
||||||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', // allow debugger during development
|
||||||
|
'no-duplicate-imports': ['error'],
|
||||||
|
'no-else-return': ['error'],
|
||||||
|
'no-empty-function': ['error'],
|
||||||
|
'no-extra-parens': ['error'],
|
||||||
|
'no-implicit-coercion': ['error'],
|
||||||
|
'no-lonely-if': ['error'],
|
||||||
|
'no-multi-spaces': ['error'],
|
||||||
|
'no-multiple-empty-lines': ['warn', { max: 2, maxBOF: 0, maxEOF: 0 }],
|
||||||
|
'no-promise-executor-return': ['error'],
|
||||||
|
'no-return-assign': ['error'],
|
||||||
|
'no-script-url': ['error'],
|
||||||
|
'no-template-curly-in-string': ['error'],
|
||||||
|
'no-trailing-spaces': ['error'],
|
||||||
|
'no-unneeded-ternary': ['error'],
|
||||||
|
'no-unreachable-loop': ['error'],
|
||||||
|
'no-unsafe-optional-chaining': ['error'],
|
||||||
|
'no-useless-return': ['error'],
|
||||||
|
'no-var': ['error'],
|
||||||
|
'no-warning-comments': ['error'],
|
||||||
|
'no-whitespace-before-property': ['error'],
|
||||||
|
'object-curly-newline': ['error', { consistent: true }],
|
||||||
|
'object-curly-spacing': ['error', 'always'],
|
||||||
|
'object-shorthand': ['error'],
|
||||||
|
'padded-blocks': ['error', 'never'],
|
||||||
|
'prefer-arrow-callback': ['error'],
|
||||||
|
'prefer-const': ['error'],
|
||||||
|
'prefer-object-spread': ['error'],
|
||||||
|
'prefer-rest-params': ['error'],
|
||||||
|
'prefer-template': ['error'],
|
||||||
|
'quote-props': ['error', 'consistent-as-needed', { keywords: false }],
|
||||||
|
'quotes': ['error', 'single', { allowTemplateLiterals: true }],
|
||||||
|
'require-atomic-updates': ['error'],
|
||||||
|
'require-await': ['error'],
|
||||||
|
'semi': ['error', 'never'],
|
||||||
|
'sort-imports': ['error', { ignoreCase: true, ignoreDeclarationSort: false, ignoreMemberSort: false }],
|
||||||
|
'sort-keys': ['error', 'asc', { caseSensitive: true, natural: false }],
|
||||||
|
'space-before-blocks': ['error', 'always'],
|
||||||
|
'space-before-function-paren': ['error', 'never'],
|
||||||
|
'space-in-parens': ['error', 'never'],
|
||||||
|
'space-infix-ops': ['error'],
|
||||||
|
'space-unary-ops': ['error', { nonwords: false, words: true }],
|
||||||
|
'spaced-comment': ['warn', 'always'],
|
||||||
|
'switch-colon-spacing': ['error'],
|
||||||
|
'template-curly-spacing': ['error', 'never'],
|
||||||
|
'unicode-bom': ['error', 'never'],
|
||||||
|
'vue/new-line-between-multi-line-property': ['error'],
|
||||||
|
'vue/no-empty-component-block': ['error'],
|
||||||
|
'vue/no-reserved-component-names': ['error'],
|
||||||
|
'vue/no-template-target-blank': ['error'],
|
||||||
|
'vue/no-unused-properties': ['error'],
|
||||||
|
'vue/no-unused-refs': ['error'],
|
||||||
|
'vue/no-useless-mustaches': ['error'],
|
||||||
|
'vue/order-in-components': ['off'], // Collides with sort-keys
|
||||||
|
'vue/require-name-property': ['error'],
|
||||||
|
'vue/v-for-delimiter-style': ['error'],
|
||||||
|
'vue/v-on-function-call': ['error'],
|
||||||
|
'wrap-iife': ['error'],
|
||||||
|
'yoda': ['error'],
|
||||||
|
},
|
||||||
|
}
|
|
@ -16,7 +16,7 @@ release_commit_message: "Release: OTS {{.Version}}"
|
||||||
# bash inside the commands.
|
# bash inside the commands.
|
||||||
pre_commit_commands:
|
pre_commit_commands:
|
||||||
- |-
|
- |-
|
||||||
yq -iP "(select(.spec.template.spec | has(\"containers\")) | .spec.template.spec.containers[] | select(.name == \"ots\").image) = \"ghcr.io/luzifer/ots:v${TAG_VERSION}\"" docs/k8s_example.yml
|
yq -iP "(select(.spec.template.spec | has(\"containers\")) | .spec.template.spec.containers[] | select(.name == \"ots\").image) = \"luzifer/ots:v${TAG_VERSION}\"" docs/k8s_example.yml
|
||||||
git add docs/k8s_example.yml
|
git add docs/k8s_example.yml
|
||||||
- |-
|
- |-
|
||||||
yq -iP ".services.app.build.context = \"https://github.com/Luzifer/ots.git#v${TAG_VERSION}\"" docker-compose.yml
|
yq -iP ".services.app.build.context = \"https://github.com/Luzifer/ots.git#v${TAG_VERSION}\"" docker-compose.yml
|
||||||
|
|
45
.github/workflows/codeql.yml
vendored
45
.github/workflows/codeql.yml
vendored
|
@ -1,12 +1,13 @@
|
||||||
---
|
---
|
||||||
name: 'CodeQL'
|
|
||||||
|
name: "CodeQL"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ['master']
|
branches: [ "master" ]
|
||||||
pull_request:
|
pull_request:
|
||||||
# The branches below must be a subset of the branches above
|
# The branches below must be a subset of the branches above
|
||||||
branches: ['master']
|
branches: [ "master" ]
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '38 21 * * 3'
|
- cron: '38 21 * * 3'
|
||||||
|
|
||||||
|
@ -23,25 +24,29 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
language: ['go', 'javascript']
|
language: [ 'go', 'javascript' ]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
|
uses: github/codeql-action/init@v2
|
||||||
with:
|
with:
|
||||||
build-mode: ${{ matrix.language == 'go' && 'autobuild' || '' }}
|
languages: ${{ matrix.language }}
|
||||||
languages: ${{ matrix.language }}
|
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Autobuild
|
||||||
uses: github/codeql-action/analyze@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
|
uses: github/codeql-action/autobuild@v2
|
||||||
with:
|
|
||||||
category: '/language:${{matrix.language}}'
|
- name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@v2
|
||||||
|
with:
|
||||||
|
category: "/language:${{matrix.language}}"
|
||||||
|
|
||||||
|
...
|
||||||
|
|
48
.github/workflows/docker-publish.yml
vendored
48
.github/workflows/docker-publish.yml
vendored
|
@ -1,48 +0,0 @@
|
||||||
---
|
|
||||||
name: docker-publish
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: ['master']
|
|
||||||
tags: ['v*']
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
packages: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
docker-publish:
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
|
||||||
|
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
||||||
with:
|
|
||||||
lfs: true
|
|
||||||
show-progress: false
|
|
||||||
|
|
||||||
- name: Log into registry
|
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Docker Build & Publish
|
|
||||||
id: taggen
|
|
||||||
run: bash ci/docker-gen-tagnames.sh
|
|
||||||
|
|
||||||
- name: Build and push
|
|
||||||
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
push: true
|
|
||||||
tags: ${{ steps.taggen.outputs.docker_build_tags }}
|
|
41
.github/workflows/pr-regen-translations.yml
vendored
41
.github/workflows/pr-regen-translations.yml
vendored
|
@ -1,41 +0,0 @@
|
||||||
---
|
|
||||||
name: pull-request-ci
|
|
||||||
on:
|
|
||||||
pull_request_target:
|
|
||||||
paths: ['i18n.yaml']
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
generate-translations:
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
container:
|
|
||||||
image: ghcr.io/luzifer-docker/gh-arch-env@sha256:1c8236063720bbd07ee8d590fbcb25251de021da4c80a046ae77d310406e288f
|
|
||||||
env:
|
|
||||||
CGO_ENABLED: 0
|
|
||||||
GOPATH: /go
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
||||||
with:
|
|
||||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
|
||||||
ref: ${{ github.head_ref }}
|
|
||||||
|
|
||||||
- name: Marking workdir safe
|
|
||||||
run: git config --global --add safe.directory /__w/ots/ots
|
|
||||||
|
|
||||||
- name: Re-Generate embedded translations file
|
|
||||||
working-directory: ./ci/translate
|
|
||||||
run: go run .
|
|
||||||
|
|
||||||
- uses: stefanzweifel/git-auto-commit-action@b863ae1933cb653a53c021fe36dbb774e1fb9403 # v5.2.0
|
|
||||||
with:
|
|
||||||
commit_author: 'github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>'
|
|
||||||
commit_message: 'CI: Update embedded translations'
|
|
||||||
file_pattern: 'src/langs/langs.js'
|
|
34
.github/workflows/test-and-build.yml
vendored
34
.github/workflows/test-and-build.yml
vendored
|
@ -1,8 +1,9 @@
|
||||||
---
|
---
|
||||||
|
|
||||||
name: test-and-build
|
name: test-and-build
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ['**']
|
branches: ['*']
|
||||||
tags: ['v*']
|
tags: ['v*']
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
@ -16,7 +17,7 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
container:
|
container:
|
||||||
image: ghcr.io/luzifer-docker/gh-arch-env@sha256:1c8236063720bbd07ee8d590fbcb25251de021da4c80a046ae77d310406e288f
|
image: luzifer/archlinux
|
||||||
env:
|
env:
|
||||||
CGO_ENABLED: 0
|
CGO_ENABLED: 0
|
||||||
GOPATH: /go
|
GOPATH: /go
|
||||||
|
@ -24,7 +25,28 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
- name: Enable custom AUR package repo
|
||||||
|
run: echo -e "[luzifer]\nSigLevel = Never\nServer = https://archrepo.hub.luzifer.io/\$arch" >>/etc/pacman.conf
|
||||||
|
|
||||||
|
- name: Install required packages
|
||||||
|
run: |
|
||||||
|
pacman -Syy --noconfirm \
|
||||||
|
awk \
|
||||||
|
curl \
|
||||||
|
diffutils \
|
||||||
|
git \
|
||||||
|
go \
|
||||||
|
golangci-lint-bin \
|
||||||
|
make \
|
||||||
|
nodejs-lts-hydrogen \
|
||||||
|
npm \
|
||||||
|
tar \
|
||||||
|
trivy \
|
||||||
|
unzip \
|
||||||
|
which \
|
||||||
|
zip
|
||||||
|
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Marking workdir safe
|
- name: Marking workdir safe
|
||||||
run: git config --global --add safe.directory /__w/ots/ots
|
run: git config --global --add safe.directory /__w/ots/ots
|
||||||
|
@ -54,7 +76,7 @@ jobs:
|
||||||
grep -q "### Language" translate-issue.md || rm -f translate-issue.md
|
grep -q "### Language" translate-issue.md || rm -f translate-issue.md
|
||||||
|
|
||||||
- name: Update Translations Issue
|
- name: Update Translations Issue
|
||||||
uses: JasonEtco/create-an-issue@1b14a70e4d8dc185e5cc76d3bec9eab20257b2c5 # v2.9.2
|
uses: JasonEtco/create-an-issue@v2
|
||||||
if: github.ref == 'refs/heads/master' && hashFiles('translate-issue.md') != ''
|
if: github.ref == 'refs/heads/master' && hashFiles('translate-issue.md') != ''
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
@ -77,10 +99,12 @@ jobs:
|
||||||
run: 'awk "/^#/ && ++c==2{exit}; /^#/f" "History.md" | tail -n +2 >release_changelog.md'
|
run: 'awk "/^#/ && ++c==2{exit}; /^#/f" "History.md" | tail -n +2 >release_changelog.md'
|
||||||
|
|
||||||
- name: Release
|
- name: Release
|
||||||
uses: ncipollo/release-action@440c8c1cb0ed28b9f43e4d1d670870f059653174 # v1.16.0
|
uses: ncipollo/release-action@v1
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
with:
|
with:
|
||||||
artifacts: '.build/*'
|
artifacts: '.build/*'
|
||||||
bodyFile: release_changelog.md
|
bodyFile: release_changelog.md
|
||||||
draft: false
|
draft: false
|
||||||
generateReleaseNotes: false
|
generateReleaseNotes: false
|
||||||
|
|
||||||
|
...
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -8,7 +8,6 @@ frontend/css
|
||||||
frontend/js
|
frontend/js
|
||||||
frontend/locale/*.untranslated.json
|
frontend/locale/*.untranslated.json
|
||||||
frontend/webfonts
|
frontend/webfonts
|
||||||
frontend/*.ttf
|
|
||||||
frontend/*.woff2
|
frontend/*.woff2
|
||||||
node_modules
|
node_modules
|
||||||
ots
|
ots
|
||||||
|
|
299
.golangci.yml
299
.golangci.yml
|
@ -1,147 +1,174 @@
|
||||||
version: '2'
|
# Derived from https://github.com/golangci/golangci-lint/blob/master/.golangci.example.yml
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
run:
|
run:
|
||||||
|
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||||
|
timeout: 5m
|
||||||
|
# Force readonly modules usage for checking
|
||||||
modules-download-mode: readonly
|
modules-download-mode: readonly
|
||||||
relative-path-mode: wd
|
|
||||||
|
|
||||||
output:
|
output:
|
||||||
formats:
|
format: tab
|
||||||
tab:
|
|
||||||
path: stdout
|
|
||||||
colors: false
|
|
||||||
|
|
||||||
linters:
|
|
||||||
default: none
|
|
||||||
enable:
|
|
||||||
- asciicheck
|
|
||||||
- bidichk
|
|
||||||
- bodyclose
|
|
||||||
- containedctx
|
|
||||||
- contextcheck
|
|
||||||
- copyloopvar
|
|
||||||
- dogsled
|
|
||||||
- durationcheck
|
|
||||||
- errcheck
|
|
||||||
- errchkjson
|
|
||||||
- forbidigo
|
|
||||||
- funlen
|
|
||||||
- gocognit
|
|
||||||
- goconst
|
|
||||||
- gocritic
|
|
||||||
- gocyclo
|
|
||||||
- godox
|
|
||||||
- gosec
|
|
||||||
- govet
|
|
||||||
- ineffassign
|
|
||||||
- misspell
|
|
||||||
- mnd
|
|
||||||
- nakedret
|
|
||||||
- nilerr
|
|
||||||
- nilnil
|
|
||||||
- noctx
|
|
||||||
- nolintlint
|
|
||||||
- revive
|
|
||||||
- staticcheck
|
|
||||||
- unconvert
|
|
||||||
- unused
|
|
||||||
- wastedassign
|
|
||||||
- wrapcheck
|
|
||||||
|
|
||||||
settings:
|
|
||||||
funlen:
|
|
||||||
lines: 100
|
|
||||||
statements: 60
|
|
||||||
|
|
||||||
gocyclo:
|
|
||||||
min-complexity: 15
|
|
||||||
|
|
||||||
mnd:
|
|
||||||
ignored-functions:
|
|
||||||
- strconv.(?:Format|Parse)\B+
|
|
||||||
|
|
||||||
revive:
|
|
||||||
rules:
|
|
||||||
- name: atomic
|
|
||||||
- name: banned-characters
|
|
||||||
arguments:
|
|
||||||
- ;
|
|
||||||
- name: bare-return
|
|
||||||
- name: blank-imports
|
|
||||||
- name: bool-literal-in-expr
|
|
||||||
- name: call-to-gc
|
|
||||||
- name: confusing-naming
|
|
||||||
- name: confusing-results
|
|
||||||
- name: constant-logical-expr
|
|
||||||
- name: context-as-argument
|
|
||||||
- name: context-keys-type
|
|
||||||
- name: deep-exit
|
|
||||||
- name: defer
|
|
||||||
- name: dot-imports
|
|
||||||
- name: duplicated-imports
|
|
||||||
- name: early-return
|
|
||||||
- name: empty-block
|
|
||||||
- name: empty-lines
|
|
||||||
- name: errorf
|
|
||||||
- name: error-naming
|
|
||||||
- name: error-return
|
|
||||||
- name: error-strings
|
|
||||||
- name: exported
|
|
||||||
arguments:
|
|
||||||
- sayRepetitiveInsteadOfStutters
|
|
||||||
- name: flag-parameter
|
|
||||||
- name: get-return
|
|
||||||
- name: identical-branches
|
|
||||||
- name: if-return
|
|
||||||
- name: import-shadowing
|
|
||||||
- name: increment-decrement
|
|
||||||
- name: indent-error-flow
|
|
||||||
- name: modifies-parameter
|
|
||||||
- name: modifies-value-receiver
|
|
||||||
- name: optimize-operands-order
|
|
||||||
- name: range
|
|
||||||
- name: range-val-address
|
|
||||||
- name: range-val-in-closure
|
|
||||||
- name: receiver-naming
|
|
||||||
- name: redefines-builtin-id
|
|
||||||
- name: string-of-int
|
|
||||||
- name: struct-tag
|
|
||||||
- name: superfluous-else
|
|
||||||
- name: time-equal
|
|
||||||
- name: time-naming
|
|
||||||
- name: unconditional-recursion
|
|
||||||
- name: unexported-naming
|
|
||||||
- name: unexported-return
|
|
||||||
- name: unhandled-error
|
|
||||||
arguments:
|
|
||||||
- fmt.(Fp|P)rint(f|ln|)
|
|
||||||
- name: unnecessary-stmt
|
|
||||||
- name: unreachable-code
|
|
||||||
- name: unused-parameter
|
|
||||||
- name: unused-receiver
|
|
||||||
- name: useless-break
|
|
||||||
- name: var-declaration
|
|
||||||
- name: var-naming
|
|
||||||
- name: waitgroup-by-value
|
|
||||||
|
|
||||||
exclusions:
|
|
||||||
generated: lax
|
|
||||||
paths:
|
|
||||||
- third_party$
|
|
||||||
- builtin$
|
|
||||||
- examples$
|
|
||||||
|
|
||||||
issues:
|
issues:
|
||||||
|
# This disables the included exclude-list in golangci-lint as that
|
||||||
|
# list for example fully hides G304 gosec rule, errcheck, exported
|
||||||
|
# rule of revive and other errors one really wants to see.
|
||||||
|
# Smme detail: https://github.com/golangci/golangci-lint/issues/456
|
||||||
|
exclude-use-default: false
|
||||||
|
# Don't limit the number of shown issues: Report ALL of them
|
||||||
max-issues-per-linter: 0
|
max-issues-per-linter: 0
|
||||||
max-same-issues: 0
|
max-same-issues: 0
|
||||||
|
|
||||||
formatters:
|
linters:
|
||||||
|
disable-all: true
|
||||||
enable:
|
enable:
|
||||||
- gofmt
|
- asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers [fast: true, auto-fix: false]
|
||||||
- gofumpt
|
- bidichk # Checks for dangerous unicode character sequences [fast: true, auto-fix: false]
|
||||||
- goimports
|
- bodyclose # checks whether HTTP response body is closed successfully [fast: true, auto-fix: false]
|
||||||
exclusions:
|
- containedctx # containedctx is a linter that detects struct contained context.Context field [fast: true, auto-fix: false]
|
||||||
generated: lax
|
- contextcheck # check the function whether use a non-inherited context [fast: false, auto-fix: false]
|
||||||
paths:
|
- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) [fast: true, auto-fix: false]
|
||||||
- third_party$
|
- durationcheck # check for two durations multiplied together [fast: false, auto-fix: false]
|
||||||
- builtin$
|
- errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases [fast: false, auto-fix: false]
|
||||||
- examples$
|
- errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted. [fast: false, auto-fix: false]
|
||||||
|
- exportloopref # checks for pointers to enclosing loop variables [fast: true, auto-fix: false]
|
||||||
|
- forbidigo # Forbids identifiers [fast: true, auto-fix: false]
|
||||||
|
- funlen # Tool for detection of long functions [fast: true, auto-fix: false]
|
||||||
|
- gocognit # Computes and checks the cognitive complexity of functions [fast: true, auto-fix: false]
|
||||||
|
- goconst # Finds repeated strings that could be replaced by a constant [fast: true, auto-fix: false]
|
||||||
|
- gocritic # The most opinionated Go source code linter [fast: true, auto-fix: false]
|
||||||
|
- gocyclo # Computes and checks the cyclomatic complexity of functions [fast: true, auto-fix: false]
|
||||||
|
- godox # Tool for detection of FIXME, TODO and other comment keywords [fast: true, auto-fix: false]
|
||||||
|
- gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification [fast: true, auto-fix: true]
|
||||||
|
- gofumpt # Gofumpt checks whether code was gofumpt-ed. [fast: true, auto-fix: true]
|
||||||
|
- goimports # Goimports does everything that gofmt does. Additionally it checks unused imports [fast: true, auto-fix: true]
|
||||||
|
- gomnd # An analyzer to detect magic numbers. [fast: true, auto-fix: false]
|
||||||
|
- gosec # Inspects source code for security problems [fast: true, auto-fix: false]
|
||||||
|
- gosimple # Linter for Go source code that specializes in simplifying a code [fast: true, auto-fix: false]
|
||||||
|
- govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string [fast: true, auto-fix: false]
|
||||||
|
- ineffassign # Detects when assignments to existing variables are not used [fast: true, auto-fix: false]
|
||||||
|
- misspell # Finds commonly misspelled English words in comments [fast: true, auto-fix: true]
|
||||||
|
- nakedret # Finds naked returns in functions greater than a specified function length [fast: true, auto-fix: false]
|
||||||
|
- nilerr # Finds the code that returns nil even if it checks that the error is not nil. [fast: false, auto-fix: false]
|
||||||
|
- nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value. [fast: false, auto-fix: false]
|
||||||
|
- noctx # noctx finds sending http request without context.Context [fast: true, auto-fix: false]
|
||||||
|
- nolintlint # Reports ill-formed or insufficient nolint directives [fast: true, auto-fix: false]
|
||||||
|
- revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint. [fast: false, auto-fix: false]
|
||||||
|
- staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks [fast: true, auto-fix: false]
|
||||||
|
- stylecheck # Stylecheck is a replacement for golint [fast: true, auto-fix: false]
|
||||||
|
- tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 [fast: false, auto-fix: false]
|
||||||
|
- typecheck # Like the front-end of a Go compiler, parses and type-checks Go code [fast: true, auto-fix: false]
|
||||||
|
- unconvert # Remove unnecessary type conversions [fast: true, auto-fix: false]
|
||||||
|
- unused # Checks Go code for unused constants, variables, functions and types [fast: false, auto-fix: false]
|
||||||
|
- wastedassign # wastedassign finds wasted assignment statements. [fast: false, auto-fix: false]
|
||||||
|
- wrapcheck # Checks that errors returned from external packages are wrapped [fast: false, auto-fix: false]
|
||||||
|
|
||||||
|
linters-settings:
|
||||||
|
funlen:
|
||||||
|
lines: 100
|
||||||
|
statements: 60
|
||||||
|
|
||||||
|
gocyclo:
|
||||||
|
# minimal code complexity to report, 30 by default (but we recommend 10-20)
|
||||||
|
min-complexity: 15
|
||||||
|
|
||||||
|
gomnd:
|
||||||
|
settings:
|
||||||
|
mnd:
|
||||||
|
ignored-functions: 'strconv.(?:Format|Parse)\B+'
|
||||||
|
|
||||||
|
revive:
|
||||||
|
rules:
|
||||||
|
#- name: add-constant # Suggests using constant for magic numbers and string literals
|
||||||
|
# Opinion: Makes sense for strings, not for numbers but checks numbers
|
||||||
|
#- name: argument-limit # Specifies the maximum number of arguments a function can receive | Opinion: Don't need this
|
||||||
|
- name: atomic # Check for common mistaken usages of the `sync/atomic` package
|
||||||
|
- name: banned-characters # Checks banned characters in identifiers
|
||||||
|
arguments:
|
||||||
|
- ';' # Greek question mark
|
||||||
|
- name: bare-return # Warns on bare returns
|
||||||
|
- name: blank-imports # Disallows blank imports
|
||||||
|
- name: bool-literal-in-expr # Suggests removing Boolean literals from logic expressions
|
||||||
|
- name: call-to-gc # Warns on explicit call to the garbage collector
|
||||||
|
#- name: cognitive-complexity # Sets restriction for maximum Cognitive complexity.
|
||||||
|
# There is a dedicated linter for this
|
||||||
|
- name: confusing-naming # Warns on methods with names that differ only by capitalization
|
||||||
|
- name: confusing-results # Suggests to name potentially confusing function results
|
||||||
|
- name: constant-logical-expr # Warns on constant logical expressions
|
||||||
|
- name: context-as-argument # `context.Context` should be the first argument of a function.
|
||||||
|
- name: context-keys-type # Disallows the usage of basic types in `context.WithValue`.
|
||||||
|
#- name: cyclomatic # Sets restriction for maximum Cyclomatic complexity.
|
||||||
|
# There is a dedicated linter for this
|
||||||
|
#- name: datarace # Spots potential dataraces
|
||||||
|
# Is not (yet) available?
|
||||||
|
- name: deep-exit # Looks for program exits in funcs other than `main()` or `init()`
|
||||||
|
- name: defer # Warns on some [defer gotchas](https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-iii-36a1ab3d6ef1)
|
||||||
|
- name: dot-imports # Forbids `.` imports.
|
||||||
|
- name: duplicated-imports # Looks for packages that are imported two or more times
|
||||||
|
- name: early-return # Spots if-then-else statements that can be refactored to simplify code reading
|
||||||
|
- name: empty-block # Warns on empty code blocks
|
||||||
|
- name: empty-lines # Warns when there are heading or trailing newlines in a block
|
||||||
|
- name: errorf # Should replace `errors.New(fmt.Sprintf())` with `fmt.Errorf()`
|
||||||
|
- name: error-naming # Naming of error variables.
|
||||||
|
- name: error-return # The error return parameter should be last.
|
||||||
|
- name: error-strings # Conventions around error strings.
|
||||||
|
- name: exported # Naming and commenting conventions on exported symbols.
|
||||||
|
arguments: ['sayRepetitiveInsteadOfStutters']
|
||||||
|
#- name: file-header # Header which each file should have.
|
||||||
|
# Useless without config, have no config for it
|
||||||
|
- name: flag-parameter # Warns on boolean parameters that create a control coupling
|
||||||
|
#- name: function-length # Warns on functions exceeding the statements or lines max
|
||||||
|
# There is a dedicated linter for this
|
||||||
|
#- name: function-result-limit # Specifies the maximum number of results a function can return
|
||||||
|
# Opinion: Don't need this
|
||||||
|
- name: get-return # Warns on getters that do not yield any result
|
||||||
|
- name: identical-branches # Spots if-then-else statements with identical `then` and `else` branches
|
||||||
|
- name: if-return # Redundant if when returning an error.
|
||||||
|
#- name: imports-blacklist # Disallows importing the specified packages
|
||||||
|
# Useless without config, have no config for it
|
||||||
|
- name: import-shadowing # Spots identifiers that shadow an import
|
||||||
|
- name: increment-decrement # Use `i++` and `i--` instead of `i += 1` and `i -= 1`.
|
||||||
|
- name: indent-error-flow # Prevents redundant else statements.
|
||||||
|
#- name: line-length-limit # Specifies the maximum number of characters in a lined
|
||||||
|
# There is a dedicated linter for this
|
||||||
|
#- name: max-public-structs # The maximum number of public structs in a file.
|
||||||
|
# Opinion: Don't need this
|
||||||
|
- name: modifies-parameter # Warns on assignments to function parameters
|
||||||
|
- name: modifies-value-receiver # Warns on assignments to value-passed method receivers
|
||||||
|
#- name: nested-structs # Warns on structs within structs
|
||||||
|
# Opinion: Don't need this
|
||||||
|
- name: optimize-operands-order # Checks inefficient conditional expressions
|
||||||
|
#- name: package-comments # Package commenting conventions.
|
||||||
|
# Opinion: Don't need this
|
||||||
|
- name: range # Prevents redundant variables when iterating over a collection.
|
||||||
|
- name: range-val-address # Warns if address of range value is used dangerously
|
||||||
|
- name: range-val-in-closure # Warns if range value is used in a closure dispatched as goroutine
|
||||||
|
- name: receiver-naming # Conventions around the naming of receivers.
|
||||||
|
- name: redefines-builtin-id # Warns on redefinitions of builtin identifiers
|
||||||
|
#- name: string-format # Warns on specific string literals that fail one or more user-configured regular expressions
|
||||||
|
# Useless without config, have no config for it
|
||||||
|
- name: string-of-int # Warns on suspicious casts from int to string
|
||||||
|
- name: struct-tag # Checks common struct tags like `json`,`xml`,`yaml`
|
||||||
|
- name: superfluous-else # Prevents redundant else statements (extends indent-error-flow)
|
||||||
|
- name: time-equal # Suggests to use `time.Time.Equal` instead of `==` and `!=` for equality check time.
|
||||||
|
- name: time-naming # Conventions around the naming of time variables.
|
||||||
|
- name: unconditional-recursion # Warns on function calls that will lead to (direct) infinite recursion
|
||||||
|
- name: unexported-naming # Warns on wrongly named un-exported symbols
|
||||||
|
- name: unexported-return # Warns when a public return is from unexported type.
|
||||||
|
- name: unhandled-error # Warns on unhandled errors returned by funcion calls
|
||||||
|
arguments:
|
||||||
|
- "fmt.(Fp|P)rint(f|ln|)"
|
||||||
|
- name: unnecessary-stmt # Suggests removing or simplifying unnecessary statements
|
||||||
|
- name: unreachable-code # Warns on unreachable code
|
||||||
|
- name: unused-parameter # Suggests to rename or remove unused function parameters
|
||||||
|
- name: unused-receiver # Suggests to rename or remove unused method receivers
|
||||||
|
#- name: use-any # Proposes to replace `interface{}` with its alias `any`
|
||||||
|
# Is not (yet) available?
|
||||||
|
- name: useless-break # Warns on useless `break` statements in case clauses
|
||||||
|
- name: var-declaration # Reduces redundancies around variable declaration.
|
||||||
|
- name: var-naming # Naming rules.
|
||||||
|
- name: waitgroup-by-value # Warns on functions taking sync.WaitGroup as a by-value parameter
|
||||||
|
|
||||||
|
...
|
||||||
|
|
15
Dockerfile
15
Dockerfile
|
@ -1,4 +1,4 @@
|
||||||
FROM golang:1.24.3-alpine@sha256:ef18ee7117463ac1055f5a370ed18b8750f01589f13ea0b48642f5792b234044 AS builder
|
FROM luzifer/archlinux as builder
|
||||||
|
|
||||||
ENV CGO_ENABLED=0 \
|
ENV CGO_ENABLED=0 \
|
||||||
GOPATH=/go \
|
GOPATH=/go \
|
||||||
|
@ -8,25 +8,26 @@ COPY . /go/src/github.com/Luzifer/ots
|
||||||
WORKDIR /go/src/github.com/Luzifer/ots
|
WORKDIR /go/src/github.com/Luzifer/ots
|
||||||
|
|
||||||
RUN set -ex \
|
RUN set -ex \
|
||||||
&& apk --no-cache add \
|
&& pacman --noconfirm -Syy \
|
||||||
curl \
|
curl \
|
||||||
git \
|
git \
|
||||||
|
go \
|
||||||
make \
|
make \
|
||||||
nodejs-current \
|
nodejs-lts-hydrogen \
|
||||||
npm \
|
npm \
|
||||||
tar \
|
tar \
|
||||||
unzip \
|
unzip \
|
||||||
&& make frontend_prod generate-apidocs \
|
&& make download_libs generate-inner generate-apidocs \
|
||||||
&& go install \
|
&& go install \
|
||||||
-ldflags "-X main.version=$(git describe --tags --always || echo dev)" \
|
-ldflags "-X main.version=$(git describe --tags --always || echo dev)" \
|
||||||
-mod=readonly
|
-mod=readonly
|
||||||
|
|
||||||
|
|
||||||
FROM alpine:3.21@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c
|
FROM alpine:latest
|
||||||
|
|
||||||
LABEL org.opencontainers.image.authors='Knut Ahlers <knut@ahlers.me>' \
|
LABEL org.opencontainers.image.authors='Knut Ahlers <knut@ahlers.me>' \
|
||||||
org.opencontainers.image.version='1.17.0' \
|
org.opencontainers.image.version='1.12.0' \
|
||||||
org.opencontainers.image.url='https://github.com/Luzifer/ots/pkgs/container/ots' \
|
org.opencontainers.image.url='https://hub.docker.com/r/luzifer/ots/' \
|
||||||
org.opencontainers.image.documentation='https://github.com/Luzifer/ots/wiki' \
|
org.opencontainers.image.documentation='https://github.com/Luzifer/ots/wiki' \
|
||||||
org.opencontainers.image.source='https://github.com/Luzifer/ots' \
|
org.opencontainers.image.source='https://github.com/Luzifer/ots' \
|
||||||
org.opencontainers.image.licenses='Apache-2.0'
|
org.opencontainers.image.licenses='Apache-2.0'
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM golang:1.24.3-alpine@sha256:ef18ee7117463ac1055f5a370ed18b8750f01589f13ea0b48642f5792b234044 AS builder
|
FROM luzifer/archlinux as builder
|
||||||
|
|
||||||
ENV CGO_ENABLED=0 \
|
ENV CGO_ENABLED=0 \
|
||||||
GOPATH=/go \
|
GOPATH=/go \
|
||||||
|
@ -8,15 +8,16 @@ COPY . /go/src/github.com/Luzifer/ots
|
||||||
WORKDIR /go/src/github.com/Luzifer/ots
|
WORKDIR /go/src/github.com/Luzifer/ots
|
||||||
|
|
||||||
RUN set -ex \
|
RUN set -ex \
|
||||||
&& apk --no-cache add \
|
&& pacman --noconfirm -Syy \
|
||||||
curl \
|
curl \
|
||||||
git \
|
git \
|
||||||
|
go \
|
||||||
make \
|
make \
|
||||||
nodejs-current \
|
nodejs-lts-hydrogen \
|
||||||
npm \
|
npm \
|
||||||
tar \
|
tar \
|
||||||
unzip \
|
unzip \
|
||||||
&& make frontend_prod generate-apidocs \
|
&& make download_libs generate-inner generate-apidocs \
|
||||||
&& go install \
|
&& go install \
|
||||||
-ldflags "-X main.version=$(git describe --tags --always || echo dev)" \
|
-ldflags "-X main.version=$(git describe --tags --always || echo dev)" \
|
||||||
-mod=readonly
|
-mod=readonly
|
||||||
|
@ -25,8 +26,8 @@ RUN set -ex \
|
||||||
FROM scratch
|
FROM scratch
|
||||||
|
|
||||||
LABEL org.opencontainers.image.authors='Knut Ahlers <knut@ahlers.me>' \
|
LABEL org.opencontainers.image.authors='Knut Ahlers <knut@ahlers.me>' \
|
||||||
org.opencontainers.image.version='1.17.0' \
|
org.opencontainers.image.version='1.12.0' \
|
||||||
org.opencontainers.image.url='https://github.com/Luzifer/ots/pkgs/container/ots' \
|
org.opencontainers.image.url='https://hub.docker.com/r/luzifer/ots/' \
|
||||||
org.opencontainers.image.documentation='https://github.com/Luzifer/ots/wiki' \
|
org.opencontainers.image.documentation='https://github.com/Luzifer/ots/wiki' \
|
||||||
org.opencontainers.image.source='https://github.com/Luzifer/ots' \
|
org.opencontainers.image.source='https://github.com/Luzifer/ots' \
|
||||||
org.opencontainers.image.licenses='Apache-2.0'
|
org.opencontainers.image.licenses='Apache-2.0'
|
||||||
|
|
66
History.md
66
History.md
|
@ -1,69 +1,3 @@
|
||||||
# 1.17.0 / 2025-05-12
|
|
||||||
|
|
||||||
* Improvements
|
|
||||||
* Port to Vue3 and TypeScript
|
|
||||||
|
|
||||||
* Bugfixes
|
|
||||||
* Update Go dependencies
|
|
||||||
* Update Node dependencies
|
|
||||||
|
|
||||||
* Translations
|
|
||||||
* Update Polish translation (#213) (Thanks @Icikowski)
|
|
||||||
|
|
||||||
# 1.16.0 / 2025-05-01
|
|
||||||
|
|
||||||
* New Features
|
|
||||||
* feat: support auto theme mode (#212) (Thanks @Ma-ve)
|
|
||||||
|
|
||||||
* Bugfixes
|
|
||||||
* Cleanup test workflow
|
|
||||||
* Lint: Update linter config for golangci-lint v2
|
|
||||||
* Update Go dependencies
|
|
||||||
* Update Node dependencies
|
|
||||||
|
|
||||||
# 1.15.1 / 2024-12-12
|
|
||||||
|
|
||||||
* Bugfixes
|
|
||||||
* Update Node dependencies
|
|
||||||
* Update Go dependencies
|
|
||||||
|
|
||||||
# 1.15.0 / 2024-12-05
|
|
||||||
|
|
||||||
* Improvements
|
|
||||||
* Add alternative `appIcon` for dark-mode (#204)
|
|
||||||
|
|
||||||
# 1.14.0 / 2024-11-21
|
|
||||||
|
|
||||||
* Improvements
|
|
||||||
* Add ability to paste files into textarea
|
|
||||||
* Add button to burn secrets immediately (#193)
|
|
||||||
* Add customization to add footer-links (#192)
|
|
||||||
* Add error message when subtle crypto is unavailable
|
|
||||||
* Add 'log-requests' option to disable request logging (#199) (Thanks @jimmypw)
|
|
||||||
* Add multi-platform image build
|
|
||||||
* Add periodic in-memory store pruner (#200) (Thanks @jimmypw)
|
|
||||||
* Add TLS configuration for server (#190) (Thanks @hixichen)
|
|
||||||
|
|
||||||
* Bugfixes
|
|
||||||
* Fix: Use no-cache to satisfy Trivy
|
|
||||||
|
|
||||||
* Translations
|
|
||||||
* Update Polish translation (#194, #201) (Thanks @Icikowski)
|
|
||||||
|
|
||||||
# 1.13.0 / 2024-08-27
|
|
||||||
|
|
||||||
* Bugfixes
|
|
||||||
* Update Node dependencies
|
|
||||||
* Update Go dependencies
|
|
||||||
* Lint: Resolve unused-parameter error
|
|
||||||
|
|
||||||
* Translations
|
|
||||||
* Add Italian translation (#173) (Thanks @ste93cry)
|
|
||||||
* Update Dutch translation (#168) (Thanks @mboeren & @sorcix)
|
|
||||||
* Restore old `nl` translation as `nl-BE`
|
|
||||||
* Update French translation (#167) (Thanks @toindev)
|
|
||||||
* Update Swedish translation (#171) (Thank @artingu)
|
|
||||||
|
|
||||||
# 1.12.0 / 2024-01-24
|
# 1.12.0 / 2024-01-24
|
||||||
|
|
||||||
* Improvements
|
* Improvements
|
||||||
|
|
44
Makefile
44
Makefile
|
@ -1,35 +1,49 @@
|
||||||
default: build-local
|
VER_FONTAWESOME:=6.4.0
|
||||||
|
|
||||||
build-local: frontend generate-apidocs
|
|
||||||
|
default: generate download_libs
|
||||||
|
|
||||||
|
build-local: download_libs generate-inner generate-apidocs
|
||||||
go build \
|
go build \
|
||||||
-buildmode=pie \
|
-buildmode=pie \
|
||||||
-ldflags "-s -w -X main.version=$(shell git describe --tags --always || echo dev)" \
|
-ldflags "-s -w -X main.version=$(shell git describe --tags --always || echo dev)" \
|
||||||
-mod=readonly \
|
-mod=readonly \
|
||||||
-trimpath
|
-trimpath
|
||||||
|
|
||||||
|
generate:
|
||||||
|
docker run --rm -i -v $(CURDIR):$(CURDIR) -w $(CURDIR) node:18-alpine \
|
||||||
|
sh -exc "apk add make && make generate-inner generate-apidocs && chown -R $(shell id -u) frontend node_modules"
|
||||||
|
|
||||||
generate-apidocs:
|
generate-apidocs:
|
||||||
npx --yes @redocly/cli build-docs docs/openapi.yaml --disableGoogleFont true -o /tmp/api.html
|
npx --yes @redocly/cli build-docs docs/openapi.yaml --disableGoogleFont true -o /tmp/api.html
|
||||||
mv /tmp/api.html frontend/
|
mv /tmp/api.html frontend/
|
||||||
|
|
||||||
frontend_prod: export NODE_ENV=production
|
generate-inner:
|
||||||
frontend_prod: frontend
|
npm ci --include=dev
|
||||||
|
node ./ci/build.mjs
|
||||||
frontend: node_modules
|
|
||||||
corepack yarn@1 node ci/build.mjs
|
|
||||||
|
|
||||||
frontend_lint: node_modules
|
|
||||||
corepack yarn@1 eslint --fix src
|
|
||||||
|
|
||||||
node_modules:
|
|
||||||
corepack yarn@1 install --production=false --frozen-lockfile
|
|
||||||
|
|
||||||
publish: export NODE_ENV=production
|
publish: export NODE_ENV=production
|
||||||
publish: frontend_prod generate-apidocs
|
publish: download_libs generate-inner generate-apidocs
|
||||||
bash ./ci/build.sh
|
bash ./ci/build.sh
|
||||||
|
|
||||||
translate:
|
translate:
|
||||||
cd ci/translate && go run . --write-issue-file
|
cd ci/translate && go run . --write-issue-file
|
||||||
|
|
||||||
|
# -- Download / refresh external libraries --
|
||||||
|
|
||||||
|
clean_libs:
|
||||||
|
rm -rf \
|
||||||
|
frontend/css \
|
||||||
|
frontend/js \
|
||||||
|
frontend/webfonts
|
||||||
|
|
||||||
|
download_libs: clean_libs
|
||||||
|
download_libs: fontawesome
|
||||||
|
|
||||||
|
fontawesome:
|
||||||
|
curl -sSfL https://github.com/FortAwesome/Font-Awesome/archive/$(VER_FONTAWESOME).tar.gz | \
|
||||||
|
tar -vC frontend -xz --strip-components=1 --wildcards --exclude='*/js-packages' '*/css' '*/webfonts'
|
||||||
|
|
||||||
# -- Vulnerability scanning --
|
# -- Vulnerability scanning --
|
||||||
|
|
||||||
trivy:
|
trivy:
|
||||||
|
@ -42,5 +56,3 @@ trivy:
|
||||||
--scanners config,license,secret,vuln \
|
--scanners config,license,secret,vuln \
|
||||||
--severity HIGH,CRITICAL \
|
--severity HIGH,CRITICAL \
|
||||||
--skip-dirs docs
|
--skip-dirs docs
|
||||||
|
|
||||||
.PHONY: node_modules
|
|
||||||
|
|
20
README.md
20
README.md
|
@ -1,6 +1,7 @@
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
[](https://goreportcard.com/report/github.com/Luzifer/ots)
|
||||||
|
|
||||||
# Luzifer / OTS
|
# Luzifer / OTS
|
||||||
|
|
||||||
|
@ -90,19 +91,6 @@ You will now need to supply the web application with the password in addition to
|
||||||
|
|
||||||
In this case due to how browsers are handling hashes in URLs (the part after the `#`) the only URL the server gets to know is `https://ots.fyi/` which loads the frontend. Afterwards the Javascript executed in the browser fetches the encrypted secret at the given ID and decrypts it with the given password (in this case `mypass`). I will not be able to tell the content of your secret and just see the AES 256bit encrypted content.
|
In this case due to how browsers are handling hashes in URLs (the part after the `#`) the only URL the server gets to know is `https://ots.fyi/` which loads the frontend. Afterwards the Javascript executed in the browser fetches the encrypted secret at the given ID and decrypts it with the given password (in this case `mypass`). I will not be able to tell the content of your secret and just see the AES 256bit encrypted content.
|
||||||
|
|
||||||
## Local development
|
|
||||||
|
|
||||||
This repo contains a `Tilefile` to be used with [tilt v0.33+](https://tilt.dev/) to build and start the server for development.
|
|
||||||
|
|
||||||
Requirements:
|
|
||||||
- Go v1.23+
|
|
||||||
- Node v22+
|
|
||||||
- Tilt v0.33+
|
|
||||||
|
|
||||||
Run `tilt up`, and see `http://localhost:10350/` for the Tilt dashboard.
|
|
||||||
|
|
||||||
Front-end application is available at `http://localhost:15641/`.
|
|
||||||
|
|
||||||
## Localize to your own language
|
## Localize to your own language
|
||||||
|
|
||||||
If you want to help translating the application to your own language please see the [`i18n.yaml`](https://github.com/Luzifer/ots/blob/master/i18n.yaml) file from this repository and translate the English strings inside. Afterwards please [open an issue](https://github.com/Luzifer/ots/issues/new) and attach your translation including the information which language you translated the strings into.
|
If you want to help translating the application to your own language please see the [`i18n.yaml`](https://github.com/Luzifer/ots/blob/master/i18n.yaml) file from this repository and translate the English strings inside. Afterwards please [open an issue](https://github.com/Luzifer/ots/issues/new) and attach your translation including the information which language you translated the strings into.
|
||||||
|
|
51
Tiltfile
51
Tiltfile
|
@ -1,51 +0,0 @@
|
||||||
# Install Node deps on change of package.json
|
|
||||||
local_resource(
|
|
||||||
'yarn',
|
|
||||||
cmd='corepack yarn@1 install', # Not using the make target to edit the lockfile
|
|
||||||
deps=['package.json'],
|
|
||||||
)
|
|
||||||
|
|
||||||
# Rebuild frontend if source files change
|
|
||||||
local_resource(
|
|
||||||
'frontend',
|
|
||||||
cmd='make frontend',
|
|
||||||
deps=['src'],
|
|
||||||
resource_deps=['yarn'],
|
|
||||||
)
|
|
||||||
|
|
||||||
# Generate translation files on source change
|
|
||||||
local_resource(
|
|
||||||
'translations',
|
|
||||||
cmd='make translate',
|
|
||||||
deps=['i18n.yaml'],
|
|
||||||
)
|
|
||||||
|
|
||||||
# Rebuild and run Go webserver on code changes
|
|
||||||
local_resource(
|
|
||||||
'server',
|
|
||||||
deps=[
|
|
||||||
'api.go',
|
|
||||||
'frontend',
|
|
||||||
'helpers.go',
|
|
||||||
'main.go',
|
|
||||||
'pkg',
|
|
||||||
'storage.go',
|
|
||||||
'tplFuncs.go',
|
|
||||||
'go.mod', 'go.sum',
|
|
||||||
],
|
|
||||||
ignore=[
|
|
||||||
'src'
|
|
||||||
],
|
|
||||||
serve_cmd='go run . --listen=:15641',
|
|
||||||
serve_env={
|
|
||||||
'CUSTOMIZE': 'customize.yaml',
|
|
||||||
},
|
|
||||||
readiness_probe=probe(
|
|
||||||
http_get=http_get_action(15641, path='/api/healthz'),
|
|
||||||
initial_delay_secs=1,
|
|
||||||
),
|
|
||||||
resource_deps=[
|
|
||||||
'frontend',
|
|
||||||
'translations',
|
|
||||||
],
|
|
||||||
)
|
|
3
api.go
3
api.go
|
@ -52,7 +52,6 @@ func (a apiServer) Register(r *mux.Router) {
|
||||||
r.HandleFunc("/get/{id}", a.handleRead)
|
r.HandleFunc("/get/{id}", a.handleRead)
|
||||||
r.HandleFunc("/isWritable", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusNoContent) })
|
r.HandleFunc("/isWritable", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusNoContent) })
|
||||||
r.HandleFunc("/settings", a.handleSettings).Methods(http.MethodGet)
|
r.HandleFunc("/settings", a.handleSettings).Methods(http.MethodGet)
|
||||||
r.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a apiServer) handleCreate(res http.ResponseWriter, r *http.Request) {
|
func (a apiServer) handleCreate(res http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -60,7 +59,7 @@ func (a apiServer) handleCreate(res http.ResponseWriter, r *http.Request) {
|
||||||
// As a safeguard against HUGE payloads behind a misconfigured
|
// As a safeguard against HUGE payloads behind a misconfigured
|
||||||
// proxy we take double the maximum secret size after which we
|
// proxy we take double the maximum secret size after which we
|
||||||
// just close the read and cut the connection to the sender.
|
// just close the read and cut the connection to the sender.
|
||||||
r.Body = http.MaxBytesReader(res, r.Body, cust.MaxSecretSize*2) //nolint:mnd
|
r.Body = http.MaxBytesReader(res, r.Body, cust.MaxSecretSize*2) //nolint:gomnd
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
15
ci/build.mjs
15
ci/build.mjs
|
@ -1,6 +1,6 @@
|
||||||
import esbuild from 'esbuild'
|
|
||||||
import { sassPlugin } from 'esbuild-sass-plugin'
|
import { sassPlugin } from 'esbuild-sass-plugin'
|
||||||
import vuePlugin from 'esbuild-plugin-vue3'
|
import vuePlugin from 'esbuild-vue'
|
||||||
|
import esbuild from 'esbuild'
|
||||||
|
|
||||||
esbuild.build({
|
esbuild.build({
|
||||||
assetNames: '[name]',
|
assetNames: '[name]',
|
||||||
|
@ -8,10 +8,9 @@ esbuild.build({
|
||||||
define: {
|
define: {
|
||||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'dev'),
|
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'dev'),
|
||||||
},
|
},
|
||||||
entryPoints: ['src/main.ts'],
|
entryPoints: ['src/main.js'],
|
||||||
legalComments: 'none',
|
legalComments: 'none',
|
||||||
loader: {
|
loader: {
|
||||||
'.ttf': 'empty', // Drop files, all targets below support woff2
|
|
||||||
'.woff2': 'file',
|
'.woff2': 'file',
|
||||||
},
|
},
|
||||||
minify: true,
|
minify: true,
|
||||||
|
@ -21,10 +20,10 @@ esbuild.build({
|
||||||
vuePlugin(),
|
vuePlugin(),
|
||||||
],
|
],
|
||||||
target: [
|
target: [
|
||||||
'chrome109',
|
'chrome87',
|
||||||
'edge132',
|
'edge87',
|
||||||
'es2020',
|
'es2020',
|
||||||
'firefox115',
|
'firefox84',
|
||||||
'safari16',
|
'safari14',
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
function log() {
|
|
||||||
echo "[$(date +%H:%M:%S)] $@" >&2
|
|
||||||
}
|
|
||||||
|
|
||||||
[[ -n ${GITHUB_REF_NAME:-} ]] || {
|
|
||||||
log "ERR: This script is intended to run on a Github Action only."
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
repo="ghcr.io/${GITHUB_REPOSITORY,,}"
|
|
||||||
tags=()
|
|
||||||
|
|
||||||
case "${GITHUB_REF_TYPE}" in
|
|
||||||
branch)
|
|
||||||
# Generic build to develop: Workflow has to limit branches to master
|
|
||||||
tags+=("${repo}:develop")
|
|
||||||
;;
|
|
||||||
tag)
|
|
||||||
# Build to latest & tag: Older tags are not intended to rebuild
|
|
||||||
tags+=("${repo}:latest" "${repo}:${GITHUB_REF_NAME}")
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
log "ERR: The ref type ${GITHUB_REF_TYPE} is not handled."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
export IFS=,
|
|
||||||
echo "docker_build_tags=${tags[*]}" >>${GITHUB_OUTPUT}
|
|
|
@ -1,31 +1,29 @@
|
||||||
module translate
|
module translate
|
||||||
|
|
||||||
go 1.23.0
|
go 1.21
|
||||||
|
|
||||||
toolchain go1.24.3
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Luzifer/go_helpers/v2 v2.25.0
|
github.com/Luzifer/go_helpers/v2 v2.22.0
|
||||||
github.com/Luzifer/rconfig/v2 v2.6.0
|
github.com/Luzifer/rconfig/v2 v2.4.0
|
||||||
github.com/Masterminds/sprig/v3 v3.3.0
|
github.com/Masterminds/sprig/v3 v3.2.3
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
dario.cat/mergo v1.0.1 // indirect
|
|
||||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||||
github.com/Masterminds/semver/v3 v3.3.1 // indirect
|
github.com/Masterminds/semver/v3 v3.2.1 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.5.0 // indirect
|
||||||
github.com/huandu/xstrings v1.5.0 // indirect
|
github.com/huandu/xstrings v1.4.0 // indirect
|
||||||
|
github.com/imdario/mergo v0.3.16 // indirect
|
||||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||||
github.com/shopspring/decimal v1.4.0 // indirect
|
github.com/shopspring/decimal v1.3.1 // indirect
|
||||||
github.com/spf13/cast v1.7.1 // indirect
|
github.com/spf13/cast v1.6.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.6 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
golang.org/x/crypto v0.37.0 // indirect
|
golang.org/x/crypto v0.17.0 // indirect
|
||||||
golang.org/x/sys v0.32.0 // indirect
|
golang.org/x/sys v0.15.0 // indirect
|
||||||
gopkg.in/validator.v2 v2.0.1 // indirect
|
gopkg.in/validator.v2 v2.0.1 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,34 +1,38 @@
|
||||||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
github.com/Luzifer/go_helpers/v2 v2.22.0 h1:rJrZkJDzAiq4J0RUbwPI7kQ5rUy7BYQ/GUpo3fSM0y0=
|
||||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
github.com/Luzifer/go_helpers/v2 v2.22.0/go.mod h1:cIIqMPu3NT8/6kHke+03hVznNDLLKVGA74Lz47CWJyA=
|
||||||
github.com/Luzifer/go_helpers/v2 v2.25.0 h1:k1J4gd1+BfuokTDoWgcgib9P5mdadjzKEgbtKSVe46k=
|
github.com/Luzifer/rconfig/v2 v2.4.0 h1:MAdymTlExAZ8mx5VG8xOFAtFQSpWBipKYQHPOmYTn9o=
|
||||||
github.com/Luzifer/go_helpers/v2 v2.25.0/go.mod h1:KSVUdAJAav5cWGyB5oKGxmC27HrKULVTOxwPS/Kr+pc=
|
github.com/Luzifer/rconfig/v2 v2.4.0/go.mod h1:hWF3ZVSusbYlg5bEvCwalEyUSY+0JPJWUiIu7rBmav8=
|
||||||
github.com/Luzifer/rconfig/v2 v2.6.0 h1:ZKgsO2Wt/XZXawuAZCDkW7xszxZ8hQDTV1Wm63Jvnqk=
|
|
||||||
github.com/Luzifer/rconfig/v2 v2.6.0/go.mod h1:CxISRwCV2WjO5gnUnaRGDq17u1M3TvmFgzJLr87ejtc=
|
|
||||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||||
github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4=
|
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||||
github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
||||||
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
|
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||||
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
|
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
|
||||||
|
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
||||||
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
|
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||||
|
github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU=
|
||||||
|
github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||||
|
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||||
|
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||||
|
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
@ -37,28 +41,64 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
||||||
|
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
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.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||||
|
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||||
|
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||||
|
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/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||||
|
golang.org/x/sys v0.15.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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/validator.v2 v2.0.1 h1:xF0KWyGWXm/LM2G1TrEjqOu4pa6coO9AlWSf3msVfDY=
|
gopkg.in/validator.v2 v2.0.1 h1:xF0KWyGWXm/LM2G1TrEjqOu4pa6coO9AlWSf3msVfDY=
|
||||||
gopkg.in/validator.v2 v2.0.1/go.mod h1:lIUZBlB3Im4s/eYp39Ry/wkR02yOPhZ9IwIRBjuPuG8=
|
gopkg.in/validator.v2 v2.0.1/go.mod h1:lIUZBlB3Im4s/eYp39Ry/wkR02yOPhZ9IwIRBjuPuG8=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
|
@ -11,7 +11,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Masterminds/sprig/v3"
|
"github.com/Masterminds/sprig/v3"
|
||||||
"github.com/mitchellh/hashstructure/v2"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
@ -72,11 +71,6 @@ func main() {
|
||||||
logrus.WithError(err).Fatal("loading translation file")
|
logrus.WithError(err).Fatal("loading translation file")
|
||||||
}
|
}
|
||||||
|
|
||||||
tfHash, err := hashstructure.Hash(tf, hashstructure.FormatV2, nil)
|
|
||||||
if err != nil {
|
|
||||||
logrus.WithError(err).Fatal("hashing source translations")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.AutoTranslate {
|
if cfg.AutoTranslate {
|
||||||
logrus.Info("auto-translating new strings...")
|
logrus.Info("auto-translating new strings...")
|
||||||
|
|
||||||
|
@ -93,16 +87,10 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tfHashNew, err := hashstructure.Hash(tf, hashstructure.FormatV2, nil)
|
logrus.Info("saving translation file...")
|
||||||
if err != nil {
|
|
||||||
logrus.WithError(err).Fatal("hashing processed translations")
|
|
||||||
}
|
|
||||||
|
|
||||||
if tfHash != tfHashNew {
|
if err = saveTranslationFile(tf); err != nil {
|
||||||
logrus.Info("saving translation file...")
|
logrus.WithError(err).Fatal("saving translation file")
|
||||||
if err = saveTranslationFile(tf); err != nil {
|
|
||||||
logrus.WithError(err).Fatal("saving translation file")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Info("updating JS embedded translations...")
|
logrus.Info("updating JS embedded translations...")
|
||||||
|
@ -288,7 +276,7 @@ func saveTranslationFile(tf translationFile) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
encoder := yaml.NewEncoder(f)
|
encoder := yaml.NewEncoder(f)
|
||||||
encoder.SetIndent(2) //nolint:mnd
|
encoder.SetIndent(2) //nolint:gomnd
|
||||||
|
|
||||||
if err = encoder.Encode(tf); err != nil {
|
if err = encoder.Encode(tf); err != nil {
|
||||||
f.Close() //nolint:errcheck,gosec,revive // Short-lived fd-leak
|
f.Close() //nolint:errcheck,gosec,revive // Short-lived fd-leak
|
||||||
|
|
|
@ -11,6 +11,8 @@ import (
|
||||||
var langKeyFormat = regexp.MustCompile(`^[a-z]{2}(-[A-Z]{2})?$`)
|
var langKeyFormat = regexp.MustCompile(`^[a-z]{2}(-[A-Z]{2})?$`)
|
||||||
|
|
||||||
func verify(tf translationFile) error {
|
func verify(tf translationFile) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
if !langKeyFormat.MatchString(tf.Reference.LanguageKey) {
|
if !langKeyFormat.MatchString(tf.Reference.LanguageKey) {
|
||||||
return errors.New("reference contains invalid languageKey")
|
return errors.New("reference contains invalid languageKey")
|
||||||
}
|
}
|
||||||
|
@ -27,7 +29,7 @@ func verify(tf translationFile) error {
|
||||||
tf.Reference.FormalTranslations,
|
tf.Reference.FormalTranslations,
|
||||||
tf.Reference.Translations,
|
tf.Reference.Translations,
|
||||||
false,
|
false,
|
||||||
) {
|
); err != nil {
|
||||||
return errors.New("reference contains error in formalTranslations")
|
return errors.New("reference contains error in formalTranslations")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ func fetchRunE(cmd *cobra.Command, args []string) error {
|
||||||
func storeAttachment(dir string, f client.SecretAttachment) error {
|
func storeAttachment(dir string, f client.SecretAttachment) error {
|
||||||
// First lets find a free file name to save the file as
|
// First lets find a free file name to save the file as
|
||||||
var (
|
var (
|
||||||
fileNameFragments = strings.SplitN(f.Name, ".", 2) //nolint:mnd
|
fileNameFragments = strings.SplitN(f.Name, ".", 2) //nolint:gomnd
|
||||||
i int
|
i int
|
||||||
storeName = path.Join(dir, f.Name)
|
storeName = path.Join(dir, f.Name)
|
||||||
storeNameTpl string
|
storeNameTpl string
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
var versionCmd = &cobra.Command{
|
var versionCmd = &cobra.Command{
|
||||||
Use: "version",
|
Use: "version",
|
||||||
Short: "Displays the tool version",
|
Short: "Displays the tool version",
|
||||||
Run: func(*cobra.Command, []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
fmt.Printf("ots-cli %s\n", version) //nolint:forbidigo
|
fmt.Printf("ots-cli %s\n", version) //nolint:forbidigo
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
module github.com/Luzifer/ots/cmd/ots-cli
|
module github.com/Luzifer/ots/cmd/ots-cli
|
||||||
|
|
||||||
go 1.23.0
|
go 1.21.1
|
||||||
|
|
||||||
toolchain go1.24.3
|
|
||||||
|
|
||||||
replace (
|
replace (
|
||||||
github.com/Luzifer/ots/pkg/client => ../../pkg/client
|
github.com/Luzifer/ots/pkg/client => ../../pkg/client
|
||||||
|
@ -10,19 +8,19 @@ replace (
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Luzifer/ots/pkg/client v0.0.0-20250501151834-283ffa548fa8
|
github.com/Luzifer/ots/pkg/client v0.0.0-20231219121337-e8c57765e0dd
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/spf13/cobra v1.9.1
|
github.com/spf13/cobra v1.8.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Luzifer/go-openssl/v4 v4.2.4 // indirect
|
github.com/Luzifer/go-openssl/v4 v4.2.1 // indirect
|
||||||
github.com/Luzifer/ots/pkg/customization v0.0.0-20250501151834-283ffa548fa8 // indirect
|
github.com/Luzifer/ots/pkg/customization v0.0.0-20231219121337-e8c57765e0dd // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/ryanuber/go-glob v1.0.0 // indirect
|
github.com/ryanuber/go-glob v1.0.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.6 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
golang.org/x/crypto v0.37.0 // indirect
|
golang.org/x/crypto v0.17.0 // indirect
|
||||||
golang.org/x/sys v0.32.0 // indirect
|
golang.org/x/sys v0.15.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
github.com/Luzifer/go-openssl/v4 v4.2.4 h1:3Eu3gSeZpr8Ha+IofVnSWttCL1xejRr/lda4l4TZRWk=
|
github.com/Luzifer/go-openssl/v4 v4.2.1 h1:0+/gaQ5TcBhGmVqGrfyA21eujlbbaNwj0VlOA3nh4ts=
|
||||||
github.com/Luzifer/go-openssl/v4 v4.2.4/go.mod h1:ykquxaR0R1Vor83/FAtGBJZZO5zswuSQTVx1FQc1bJY=
|
github.com/Luzifer/go-openssl/v4 v4.2.1/go.mod h1:CZZZWY0buCtkxrkqDPQYigC4Kn55UuO97TEoV+hwz2s=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
@ -15,21 +15,23 @@ github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkB
|
||||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
|
@ -2,7 +2,7 @@ version: "3.8"
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
build:
|
build:
|
||||||
context: https://github.com/Luzifer/ots.git#v1.17.0
|
context: https://github.com/Luzifer/ots.git#v1.12.0
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
# Optional, see "Customization" in README
|
# Optional, see "Customization" in README
|
||||||
|
@ -18,7 +18,7 @@ services:
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
redis:
|
redis:
|
||||||
image: redis:alpine@sha256:0779069b3c24a47a2f681855c1c01d046793e7c5f7d2b079c2aa0652c42eaf0e
|
image: redis:alpine
|
||||||
restart: always
|
restart: always
|
||||||
volumes:
|
volumes:
|
||||||
- ./data:/data
|
- ./data:/data
|
||||||
|
|
|
@ -107,7 +107,7 @@ spec:
|
||||||
name: ots-cutomize
|
name: ots-cutomize
|
||||||
containers:
|
containers:
|
||||||
- name: ots
|
- name: ots
|
||||||
image: ghcr.io/luzifer/ots:v1.17.0
|
image: luzifer/ots:v1.12.0
|
||||||
args:
|
args:
|
||||||
- --storage-type
|
- --storage-type
|
||||||
- redis
|
- redis
|
||||||
|
|
|
@ -1,126 +0,0 @@
|
||||||
import globals from 'globals'
|
|
||||||
import js from '@eslint/js'
|
|
||||||
import typescriptEslint from '@typescript-eslint/eslint-plugin'
|
|
||||||
import vue from 'eslint-plugin-vue'
|
|
||||||
|
|
||||||
export default [
|
|
||||||
js.configs.recommended,
|
|
||||||
...vue.configs['flat/recommended'],
|
|
||||||
{
|
|
||||||
languageOptions: {
|
|
||||||
ecmaVersion: 'latest',
|
|
||||||
globals: {
|
|
||||||
...globals.browser,
|
|
||||||
...globals.node,
|
|
||||||
process: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
parserOptions: {
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
requireConfigFile: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
linterOptions: {
|
|
||||||
reportUnusedDisableDirectives: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
plugins: {
|
|
||||||
'@typescript-eslint': typescriptEslint,
|
|
||||||
vue,
|
|
||||||
},
|
|
||||||
|
|
||||||
rules: {
|
|
||||||
'array-bracket-newline': ['error', { multiline: true }],
|
|
||||||
'array-bracket-spacing': ['error'],
|
|
||||||
'arrow-body-style': ['error', 'as-needed'],
|
|
||||||
'arrow-parens': ['error', 'as-needed'],
|
|
||||||
'arrow-spacing': ['error', { after: true, before: true }],
|
|
||||||
'block-spacing': ['error'],
|
|
||||||
'brace-style': ['error', '1tbs'],
|
|
||||||
'camelcase': ['warn'],
|
|
||||||
'comma-dangle': ['error', 'always-multiline'],
|
|
||||||
'comma-spacing': ['error'],
|
|
||||||
'comma-style': ['error', 'last'],
|
|
||||||
'curly': ['error'],
|
|
||||||
'default-case-last': ['error'],
|
|
||||||
'default-param-last': ['error'],
|
|
||||||
'dot-location': ['error', 'property'],
|
|
||||||
'dot-notation': ['error'],
|
|
||||||
'eol-last': ['error', 'always'],
|
|
||||||
'eqeqeq': ['error', 'always', { null: 'ignore' }],
|
|
||||||
'func-call-spacing': ['error', 'never'],
|
|
||||||
'function-paren-newline': ['error', 'multiline'],
|
|
||||||
'generator-star-spacing': ['off'],
|
|
||||||
'implicit-arrow-linebreak': ['error'],
|
|
||||||
'indent': ['error', 2],
|
|
||||||
'key-spacing': ['error', { afterColon: true, beforeColon: false, mode: 'strict' }],
|
|
||||||
'keyword-spacing': ['error'],
|
|
||||||
'linebreak-style': ['error', 'unix'],
|
|
||||||
'lines-between-class-members': ['error'],
|
|
||||||
'multiline-comment-style': ['off'],
|
|
||||||
'newline-per-chained-call': ['error'],
|
|
||||||
'no-alert': ['error'],
|
|
||||||
'no-console': ['off'],
|
|
||||||
'no-debugger': 'off',
|
|
||||||
'no-duplicate-imports': ['error'],
|
|
||||||
'no-else-return': ['error'],
|
|
||||||
'no-empty-function': ['error'],
|
|
||||||
'no-extra-parens': ['error'],
|
|
||||||
'no-implicit-coercion': ['error'],
|
|
||||||
'no-lonely-if': ['error'],
|
|
||||||
'no-multi-spaces': ['error'],
|
|
||||||
'no-multiple-empty-lines': ['warn', { max: 2, maxBOF: 0, maxEOF: 0 }],
|
|
||||||
'no-promise-executor-return': ['error'],
|
|
||||||
'no-return-assign': ['error'],
|
|
||||||
'no-script-url': ['error'],
|
|
||||||
'no-template-curly-in-string': ['error'],
|
|
||||||
'no-trailing-spaces': ['error'],
|
|
||||||
'no-unneeded-ternary': ['error'],
|
|
||||||
'no-unreachable-loop': ['error'],
|
|
||||||
'no-unsafe-optional-chaining': ['error'],
|
|
||||||
'no-useless-return': ['error'],
|
|
||||||
'no-var': ['error'],
|
|
||||||
'no-warning-comments': ['error'],
|
|
||||||
'no-whitespace-before-property': ['error'],
|
|
||||||
'object-curly-newline': ['error', { consistent: true }],
|
|
||||||
'object-curly-spacing': ['error', 'always'],
|
|
||||||
'object-shorthand': ['error'],
|
|
||||||
'padded-blocks': ['error', 'never'],
|
|
||||||
'prefer-arrow-callback': ['error'],
|
|
||||||
'prefer-const': ['error'],
|
|
||||||
'prefer-object-spread': ['error'],
|
|
||||||
'prefer-rest-params': ['error'],
|
|
||||||
'prefer-template': ['error'],
|
|
||||||
'quote-props': ['error', 'consistent-as-needed', { keywords: false }],
|
|
||||||
'quotes': ['error', 'single', { allowTemplateLiterals: true }],
|
|
||||||
'require-atomic-updates': ['error'],
|
|
||||||
'require-await': ['error'],
|
|
||||||
'semi': ['error', 'never'],
|
|
||||||
'sort-imports': ['error', { ignoreCase: true, ignoreDeclarationSort: false, ignoreMemberSort: false }],
|
|
||||||
'sort-keys': ['error', 'asc', { caseSensitive: true, natural: false }],
|
|
||||||
'space-before-blocks': ['error', 'always'],
|
|
||||||
'space-before-function-paren': ['error', 'never'],
|
|
||||||
'space-in-parens': ['error', 'never'],
|
|
||||||
'space-infix-ops': ['error'],
|
|
||||||
'space-unary-ops': ['error', { nonwords: false, words: true }],
|
|
||||||
'spaced-comment': ['warn', 'always'],
|
|
||||||
'switch-colon-spacing': ['error'],
|
|
||||||
'template-curly-spacing': ['error', 'never'],
|
|
||||||
'unicode-bom': ['error', 'never'],
|
|
||||||
'vue/comment-directive': 'off',
|
|
||||||
'vue/new-line-between-multi-line-property': ['error'],
|
|
||||||
'vue/no-empty-component-block': ['error'],
|
|
||||||
'vue/no-reserved-component-names': ['error'],
|
|
||||||
'vue/no-template-target-blank': ['error'],
|
|
||||||
'vue/no-unused-properties': ['error'],
|
|
||||||
'vue/no-unused-refs': ['error'],
|
|
||||||
'vue/no-useless-mustaches': ['error'],
|
|
||||||
'vue/order-in-components': ['off'],
|
|
||||||
'vue/require-name-property': ['error'],
|
|
||||||
'vue/v-for-delimiter-style': ['error'],
|
|
||||||
'wrap-iife': ['error'],
|
|
||||||
'yoda': ['error'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
|
@ -4,7 +4,7 @@
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
|
||||||
{{ range (list "fa-solid-900.woff2" "fa-brands-400.woff2") }}
|
{{ range (list "webfonts/fa-solid-900.woff2" "webfonts/fa-brands-400.woff2" "lato-v20-latin-ext_latin-regular.woff2" "lato-v20-latin-ext_latin-700.woff2") }}
|
||||||
<link
|
<link
|
||||||
as="font"
|
as="font"
|
||||||
crossorigin="anonymous"
|
crossorigin="anonymous"
|
||||||
|
@ -21,20 +21,17 @@
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
>
|
>
|
||||||
|
|
||||||
|
<link
|
||||||
|
crossorigin="anonymous"
|
||||||
|
href="css/all.min.css"
|
||||||
|
integrity="{{ assetSRI `css/all.min.css` }}"
|
||||||
|
rel="stylesheet"
|
||||||
|
>
|
||||||
|
|
||||||
<title>{{ .Customize.AppTitle }}</title>
|
<title>{{ .Customize.AppTitle }}</title>
|
||||||
|
|
||||||
<script nonce="{{ .InlineContentNonce }}">
|
<script nonce="{{ .InlineContentNonce }}">
|
||||||
window.getThemeFromStorage = () => localStorage.getItem('set-color-scheme') || 'auto'
|
window.getTheme = () => localStorage.getItem('set-color-scheme') || (window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark')
|
||||||
|
|
||||||
window.getTheme = () => {
|
|
||||||
const colorScheme = window.getThemeFromStorage()
|
|
||||||
|
|
||||||
if (!colorScheme || colorScheme === 'auto') {
|
|
||||||
return window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark'
|
|
||||||
}
|
|
||||||
|
|
||||||
return colorScheme
|
|
||||||
}
|
|
||||||
|
|
||||||
window.refreshTheme = () => {
|
window.refreshTheme = () => {
|
||||||
document.querySelector('html').setAttribute('data-bs-theme', window.getTheme())
|
document.querySelector('html').setAttribute('data-bs-theme', window.getTheme())
|
||||||
|
@ -49,8 +46,8 @@
|
||||||
document.addEventListener('DOMContentLoaded', () => window.refreshTheme())
|
document.addEventListener('DOMContentLoaded', () => window.refreshTheme())
|
||||||
|
|
||||||
// Template variable from Golang process
|
// Template variable from Golang process
|
||||||
window.maxSecretExpire = {{ .MaxSecretExpiry }}
|
const maxSecretExpire = {{ .MaxSecretExpiry }}
|
||||||
window.version = "{{ .Version }}"
|
const version = "{{ .Version }}"
|
||||||
window.OTSCustomize = JSON.parse('{{ .Customize.ToJSON }}')
|
window.OTSCustomize = JSON.parse('{{ .Customize.ToJSON }}')
|
||||||
window.useFormalLanguage = {{ .Customize.UseFormalLanguage | mustToJson }}
|
window.useFormalLanguage = {{ .Customize.UseFormalLanguage | mustToJson }}
|
||||||
</script>
|
</script>
|
||||||
|
|
48
go.mod
48
go.mod
|
@ -1,45 +1,45 @@
|
||||||
module github.com/Luzifer/ots
|
module github.com/Luzifer/ots
|
||||||
|
|
||||||
go 1.23.0
|
go 1.21.1
|
||||||
|
|
||||||
toolchain go1.24.3
|
toolchain go1.21.2
|
||||||
|
|
||||||
replace github.com/Luzifer/ots/pkg/customization => ./pkg/customization
|
replace github.com/Luzifer/ots/pkg/customization => ./pkg/customization
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Luzifer/go_helpers/v2 v2.25.0
|
github.com/Luzifer/go_helpers/v2 v2.22.0
|
||||||
github.com/Luzifer/ots/pkg/customization v0.0.0-20250501151834-283ffa548fa8
|
github.com/Luzifer/ots/pkg/customization v0.0.0-20231215213647-d9fe7df6cbb6
|
||||||
github.com/Luzifer/rconfig/v2 v2.6.0
|
github.com/Luzifer/rconfig/v2 v2.4.0
|
||||||
github.com/Masterminds/sprig/v3 v3.3.0
|
github.com/Masterminds/sprig/v3 v3.2.3
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible
|
github.com/gofrs/uuid v4.4.0+incompatible
|
||||||
github.com/gorilla/mux v1.8.1
|
github.com/gorilla/mux v1.8.1
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/prometheus/client_golang v1.22.0
|
github.com/prometheus/client_golang v1.17.0
|
||||||
github.com/redis/go-redis/v9 v9.8.0
|
github.com/redis/go-redis/v9 v9.3.1
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
dario.cat/mergo v1.0.1 // indirect
|
|
||||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||||
github.com/Masterminds/semver/v3 v3.3.1 // indirect
|
github.com/Masterminds/semver/v3 v3.2.1 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.5.0 // indirect
|
||||||
github.com/huandu/xstrings v1.5.0 // indirect
|
github.com/huandu/xstrings v1.4.0 // indirect
|
||||||
|
github.com/imdario/mergo v0.3.16 // indirect
|
||||||
|
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
|
||||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/prometheus/client_model v0.5.0 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/common v0.45.0 // indirect
|
||||||
github.com/prometheus/common v0.63.0 // indirect
|
github.com/prometheus/procfs v0.12.0 // indirect
|
||||||
github.com/prometheus/procfs v0.16.1 // indirect
|
github.com/shopspring/decimal v1.3.1 // indirect
|
||||||
github.com/shopspring/decimal v1.4.0 // indirect
|
github.com/spf13/cast v1.6.0 // indirect
|
||||||
github.com/spf13/cast v1.7.1 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/spf13/pflag v1.0.6 // indirect
|
golang.org/x/crypto v0.17.0 // indirect
|
||||||
golang.org/x/crypto v0.37.0 // indirect
|
golang.org/x/sys v0.15.0 // indirect
|
||||||
golang.org/x/sys v0.32.0 // indirect
|
google.golang.org/protobuf v1.31.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.6 // indirect
|
|
||||||
gopkg.in/validator.v2 v2.0.1 // indirect
|
gopkg.in/validator.v2 v2.0.1 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
138
go.sum
138
go.sum
|
@ -1,23 +1,22 @@
|
||||||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
github.com/Luzifer/go_helpers/v2 v2.22.0 h1:rJrZkJDzAiq4J0RUbwPI7kQ5rUy7BYQ/GUpo3fSM0y0=
|
||||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
github.com/Luzifer/go_helpers/v2 v2.22.0/go.mod h1:cIIqMPu3NT8/6kHke+03hVznNDLLKVGA74Lz47CWJyA=
|
||||||
github.com/Luzifer/go_helpers/v2 v2.25.0 h1:k1J4gd1+BfuokTDoWgcgib9P5mdadjzKEgbtKSVe46k=
|
github.com/Luzifer/rconfig/v2 v2.4.0 h1:MAdymTlExAZ8mx5VG8xOFAtFQSpWBipKYQHPOmYTn9o=
|
||||||
github.com/Luzifer/go_helpers/v2 v2.25.0/go.mod h1:KSVUdAJAav5cWGyB5oKGxmC27HrKULVTOxwPS/Kr+pc=
|
github.com/Luzifer/rconfig/v2 v2.4.0/go.mod h1:hWF3ZVSusbYlg5bEvCwalEyUSY+0JPJWUiIu7rBmav8=
|
||||||
github.com/Luzifer/rconfig/v2 v2.6.0 h1:ZKgsO2Wt/XZXawuAZCDkW7xszxZ8hQDTV1Wm63Jvnqk=
|
|
||||||
github.com/Luzifer/rconfig/v2 v2.6.0/go.mod h1:CxISRwCV2WjO5gnUnaRGDq17u1M3TvmFgzJLr87ejtc=
|
|
||||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||||
github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4=
|
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||||
github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
||||||
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
|
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||||
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
|
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
|
||||||
|
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
@ -27,68 +26,111 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
|
||||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
|
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
||||||
|
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||||
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
|
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||||
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU=
|
||||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||||
|
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||||
|
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
|
||||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
|
||||||
|
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||||
|
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
|
||||||
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
|
||||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||||
github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k=
|
github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM=
|
||||||
github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18=
|
github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
|
||||||
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||||
github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI=
|
github.com/redis/go-redis/v9 v9.3.1 h1:KqdY8U+3X6z+iACvumCNxnoluToB+9Me+TvyFa21Mds=
|
||||||
github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
|
github.com/redis/go-redis/v9 v9.3.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
||||||
|
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
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.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||||
|
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||||
|
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||||
|
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/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
golang.org/x/sys v0.15.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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||||
|
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/validator.v2 v2.0.1 h1:xF0KWyGWXm/LM2G1TrEjqOu4pa6coO9AlWSf3msVfDY=
|
gopkg.in/validator.v2 v2.0.1 h1:xF0KWyGWXm/LM2G1TrEjqOu4pa6coO9AlWSf3msVfDY=
|
||||||
gopkg.in/validator.v2 v2.0.1/go.mod h1:lIUZBlB3Im4s/eYp39Ry/wkR02yOPhZ9IwIRBjuPuG8=
|
gopkg.in/validator.v2 v2.0.1/go.mod h1:lIUZBlB3Im4s/eYp39Ry/wkR02yOPhZ9IwIRBjuPuG8=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
124
i18n.yaml
124
i18n.yaml
|
@ -4,7 +4,6 @@ reference:
|
||||||
translators:
|
translators:
|
||||||
- Luzifer
|
- Luzifer
|
||||||
translations:
|
translations:
|
||||||
alert-insecure-environment: You are accessing this instance using an insecure connection. You will not be able to create or read secrets.
|
|
||||||
alert-secret-not-found: This is not the secret you are looking for… - If you expected the secret to be here it might be compromised as someone else might have opened the link already.
|
alert-secret-not-found: This is not the secret you are looking for… - If you expected the secret to be here it might be compromised as someone else might have opened the link already.
|
||||||
alert-something-went-wrong: Something went wrong. I'm very sorry about this…
|
alert-something-went-wrong: Something went wrong. I'm very sorry about this…
|
||||||
btn-create-secret: Create the secret!
|
btn-create-secret: Create the secret!
|
||||||
|
@ -13,7 +12,6 @@ reference:
|
||||||
btn-reveal-secret: Show me the secret!
|
btn-reveal-secret: Show me the secret!
|
||||||
btn-reveal-secret-processing: Secret is being decrypted…
|
btn-reveal-secret-processing: Secret is being decrypted…
|
||||||
btn-show-explanation: How does this work?
|
btn-show-explanation: How does this work?
|
||||||
btn-theme-switcher-auto: Auto
|
|
||||||
expire-default: Default Expiry
|
expire-default: Default Expiry
|
||||||
expire-n-days: '{n} day | {n} days'
|
expire-n-days: '{n} day | {n} days'
|
||||||
expire-n-hours: '{n} hour | {n} hours'
|
expire-n-hours: '{n} hour | {n} hours'
|
||||||
|
@ -40,14 +38,12 @@ reference:
|
||||||
text-powered-by: Powered by
|
text-powered-by: Powered by
|
||||||
text-pre-reveal-hint: To reveal the secret click this button but be aware doing so will destroy the secret. You can only view it once!
|
text-pre-reveal-hint: To reveal the secret click this button but be aware doing so will destroy the secret. You can only view it once!
|
||||||
text-pre-url: 'Your secret was created and stored using this URL:'
|
text-pre-url: 'Your secret was created and stored using this URL:'
|
||||||
text-secret-burned: The secret was successfully destroyed.
|
|
||||||
text-secret-create-disabled: The creation of new secrets is disabled in this instance.
|
text-secret-create-disabled: The creation of new secrets is disabled in this instance.
|
||||||
title-explanation: This is how it works…
|
title-explanation: This is how it works…
|
||||||
title-new-secret: Create a new secret
|
title-new-secret: Create a new secret
|
||||||
title-reading-secret: Reading your secret…
|
title-reading-secret: Reading your secret…
|
||||||
title-secret-create-disabled: Secret creation disabled…
|
title-secret-create-disabled: Secret creation disabled…
|
||||||
title-secret-created: Secret created!
|
title-secret-created: Secret created!
|
||||||
tooltip-burn-secret: Burn Secret now!
|
|
||||||
tooltip-copy-to-clipboard: Copy to Clipboard
|
tooltip-copy-to-clipboard: Copy to Clipboard
|
||||||
tooltip-download-as-file: Download as File
|
tooltip-download-as-file: Download as File
|
||||||
translations:
|
translations:
|
||||||
|
@ -99,7 +95,6 @@ translations:
|
||||||
translators:
|
translators:
|
||||||
- Luzifer
|
- Luzifer
|
||||||
translations:
|
translations:
|
||||||
alert-insecure-environment: Du besuchst diese Instanz über eine unsichere Verbindung. Du kannst deswegen keine Secrets erstellen oder lesen.
|
|
||||||
alert-secret-not-found: Das ist nicht das Secret, was du suchst… - Falls du diesen Link noch nicht selbst geöffnet hast, könnte das Secret kompromittiert sein, da jemand anderes den Link geöffnet haben könnte.
|
alert-secret-not-found: Das ist nicht das Secret, was du suchst… - Falls du diesen Link noch nicht selbst geöffnet hast, könnte das Secret kompromittiert sein, da jemand anderes den Link geöffnet haben könnte.
|
||||||
alert-something-went-wrong: Irgendwas ging schief. Entschuldigung…
|
alert-something-went-wrong: Irgendwas ging schief. Entschuldigung…
|
||||||
btn-create-secret: Secret erstellen!
|
btn-create-secret: Secret erstellen!
|
||||||
|
@ -108,7 +103,6 @@ translations:
|
||||||
btn-reveal-secret: Zeig mir das Secret!
|
btn-reveal-secret: Zeig mir das Secret!
|
||||||
btn-reveal-secret-processing: Secret wird entschlüsselt…
|
btn-reveal-secret-processing: Secret wird entschlüsselt…
|
||||||
btn-show-explanation: Wie funktioniert das?
|
btn-show-explanation: Wie funktioniert das?
|
||||||
btn-theme-switcher-auto: Auto
|
|
||||||
expire-default: Server-Standard
|
expire-default: Server-Standard
|
||||||
expire-n-days: '{n} Tag | {n} Tage'
|
expire-n-days: '{n} Tag | {n} Tage'
|
||||||
expire-n-hours: '{n} Stunde | {n} Stunden'
|
expire-n-hours: '{n} Stunde | {n} Stunden'
|
||||||
|
@ -135,18 +129,15 @@ translations:
|
||||||
text-powered-by: Läuft mit
|
text-powered-by: Läuft mit
|
||||||
text-pre-reveal-hint: Um das Secret anzuzeigen klicke diesen Button aber denk dran, dass das Secret nur einmal angezeigt und dabei gelöscht wird.
|
text-pre-reveal-hint: Um das Secret anzuzeigen klicke diesen Button aber denk dran, dass das Secret nur einmal angezeigt und dabei gelöscht wird.
|
||||||
text-pre-url: 'Dein Secret wurde angelegt und unter folgender URL gespeichert:'
|
text-pre-url: 'Dein Secret wurde angelegt und unter folgender URL gespeichert:'
|
||||||
text-secret-burned: Das Secret wurde zerstört.
|
|
||||||
text-secret-create-disabled: Auf dieser Instanz wurde das Erstellen neuer Secrets deaktiviert.
|
text-secret-create-disabled: Auf dieser Instanz wurde das Erstellen neuer Secrets deaktiviert.
|
||||||
title-explanation: So funktioniert es…
|
title-explanation: So funktioniert es…
|
||||||
title-new-secret: Erstelle ein neues Secret
|
title-new-secret: Erstelle ein neues Secret
|
||||||
title-reading-secret: Secret auslesen…
|
title-reading-secret: Secret auslesen…
|
||||||
title-secret-create-disabled: Erstellen von Secrets deaktiviert…
|
title-secret-create-disabled: Erstellen von Secrets deaktiviert…
|
||||||
title-secret-created: Secret erstellt!
|
title-secret-created: Secret erstellt!
|
||||||
tooltip-burn-secret: Secret jetzt zerstören!
|
|
||||||
tooltip-copy-to-clipboard: In die Zwischenablage kopieren
|
tooltip-copy-to-clipboard: In die Zwischenablage kopieren
|
||||||
tooltip-download-as-file: Als Datei herunterladen
|
tooltip-download-as-file: Als Datei herunterladen
|
||||||
formalTranslations:
|
formalTranslations:
|
||||||
alert-insecure-environment: Sie besuchen diese Instanz über eine unsichere Verbindung. Sie können deswegen keine Secrets erstellen oder lesen.
|
|
||||||
alert-secret-not-found: Dieses Secret existiert nicht. - Falls Sie diesen Link noch nicht selbst geöffnet haben, könnte der Inhalt kompromittiert sein, da jemand anderes den Link geöffnet haben könnte.
|
alert-secret-not-found: Dieses Secret existiert nicht. - Falls Sie diesen Link noch nicht selbst geöffnet haben, könnte der Inhalt kompromittiert sein, da jemand anderes den Link geöffnet haben könnte.
|
||||||
btn-reveal-secret: Secret anzeigen
|
btn-reveal-secret: Secret anzeigen
|
||||||
items-explanation:
|
items-explanation:
|
||||||
|
@ -214,12 +205,9 @@ translations:
|
||||||
alert-secret-not-found: Ce secret n'est pas celui que vous cherchez… - Si vous comptiez trouvez ce secret ici, il a pu être compromis car quelqu'un a probablement déjà ouvert le lien.
|
alert-secret-not-found: Ce secret n'est pas celui que vous cherchez… - Si vous comptiez trouvez ce secret ici, il a pu être compromis car quelqu'un a probablement déjà ouvert le lien.
|
||||||
alert-something-went-wrong: Un problème est survenu. Nous en sommes désolés…
|
alert-something-went-wrong: Un problème est survenu. Nous en sommes désolés…
|
||||||
btn-create-secret: Créer le secret!
|
btn-create-secret: Créer le secret!
|
||||||
btn-create-secret-processing: Secret en cours de création ...
|
|
||||||
btn-new-secret: Nouveau secret
|
btn-new-secret: Nouveau secret
|
||||||
btn-reveal-secret: Voir le secret!
|
btn-reveal-secret: Voir le secret!
|
||||||
btn-reveal-secret-processing: Secret en cours de déchiffrement ...
|
|
||||||
btn-show-explanation: Comment ça fonctionne?
|
btn-show-explanation: Comment ça fonctionne?
|
||||||
expire-default: Expiration par défaut
|
|
||||||
expire-n-days: '{n} jour | {n} jours'
|
expire-n-days: '{n} jour | {n} jours'
|
||||||
expire-n-hours: '{n} heure | {n} heures'
|
expire-n-hours: '{n} heure | {n} heures'
|
||||||
expire-n-minutes: '{n} minute | {n} minutes'
|
expire-n-minutes: '{n} minute | {n} minutes'
|
||||||
|
@ -230,18 +218,11 @@ translations:
|
||||||
- Seul le secret chiffré est envoyé au serveur (ni le secret en clair, ni le mot de passe ne sont envoyés!)
|
- Seul le secret chiffré est envoyé au serveur (ni le secret en clair, ni le mot de passe ne sont envoyés!)
|
||||||
- Le serveur stocke le secret chiffré pendant un certain temps
|
- Le serveur stocke le secret chiffré pendant un certain temps
|
||||||
- Vous fournissez l'URL affichée contenant l'identifiant et le mot de passe de déchiffrage au destinataire
|
- Vous fournissez l'URL affichée contenant l'identifiant et le mot de passe de déchiffrage au destinataire
|
||||||
- 'Le destinataire ne peut voir le secret qu''une fois: si cela ne fonctionne pas, c''est que le secret a été consulté par quelqu''un d''autre!'
|
- 'Le destintaire ne peut voir le secret qu''une fois: si cela ne fonctionne pas, c''est que le secret a été consulté par quelqu''un d''autre!'
|
||||||
- Dès que le secret chiffré a été récupéré, il est supprimé du serveur
|
- Dès que le secret chiffré a été récupéré, il est supprimé du serveur
|
||||||
label-expiry: 'Expiration dans:'
|
|
||||||
label-secret-data: 'Données secrètes:'
|
label-secret-data: 'Données secrètes:'
|
||||||
label-secret-files: 'Attacher des fichiers:'
|
|
||||||
text-attached-files: L'émetteur a attaché des fichiers au secret. Assurez-vous d'avoir confiance en l'émetteur, les fichiers n'ont pas été vérifiés !
|
|
||||||
text-burn-hint: Attention de ne pas ouvrir cette URL vous-même, cela détruirait le secret. Fournissez-la à quelqu'un d'autre!
|
text-burn-hint: Attention de ne pas ouvrir cette URL vous-même, cela détruirait le secret. Fournissez-la à quelqu'un d'autre!
|
||||||
text-burn-time: 'S''il n''a pas été vu auparavant, ce secret sera automatiquement supprimé :'
|
|
||||||
text-hint-burned: <strong>Attention:</strong> Vous ne pouvez consulter ce contenu qu'une fois. Le secret sera détruit dès que vous rechargez la page, donc copiez le maintenant…
|
text-hint-burned: <strong>Attention:</strong> Vous ne pouvez consulter ce contenu qu'une fois. Le secret sera détruit dès que vous rechargez la page, donc copiez le maintenant…
|
||||||
text-invalid-files-selected: Au moins l'un des fichiers sélectionnés n'est pas autorisé comme pièce-jointe.
|
|
||||||
text-max-filesize: 'Taille maximum: {maxSize}'
|
|
||||||
text-max-filesize-exceeded: 'Le(s) fichier(s) que vous avez choisis sont trop volumineux pour être attachés : {curSize} / {maxSize}'
|
|
||||||
text-powered-by: Propulsé par
|
text-powered-by: Propulsé par
|
||||||
text-pre-reveal-hint: Pour afficher le secret, cliquez sur ce bouton, mais soyez conscient que cela le détruira. Vous ne pouvez l'afficher qu'une fois!
|
text-pre-reveal-hint: Pour afficher le secret, cliquez sur ce bouton, mais soyez conscient que cela le détruira. Vous ne pouvez l'afficher qu'une fois!
|
||||||
text-pre-url: 'Votre secret a été créé et stocké à cette URL:'
|
text-pre-url: 'Votre secret a été créé et stocké à cette URL:'
|
||||||
|
@ -251,54 +232,6 @@ translations:
|
||||||
title-reading-secret: Lecture du secret…
|
title-reading-secret: Lecture du secret…
|
||||||
title-secret-create-disabled: Création secrète désactivée...
|
title-secret-create-disabled: Création secrète désactivée...
|
||||||
title-secret-created: Secret créé!
|
title-secret-created: Secret créé!
|
||||||
tooltip-copy-to-clipboard: Copier dans le presse-papiers
|
|
||||||
tooltip-download-as-file: Télécharger en tant que fichier
|
|
||||||
it:
|
|
||||||
deeplLanguage: it
|
|
||||||
translators: []
|
|
||||||
translations:
|
|
||||||
alert-secret-not-found: Questo non è il secret che stai cercando… - Se ti aspettavi di vedere il secret allora potrebbe essere stato compromesso poichè qualcun altro potrebbe aver già aperto il link.
|
|
||||||
alert-something-went-wrong: Qualcosa non ha funzionato. Mi dispiace davvero…
|
|
||||||
btn-create-secret: Crea il secret!
|
|
||||||
btn-create-secret-processing: Creazione del secret in corso…
|
|
||||||
btn-new-secret: Nuovo secret
|
|
||||||
btn-reveal-secret: Mostrami il secret!
|
|
||||||
btn-reveal-secret-processing: Decrittazione del secret in corso…
|
|
||||||
btn-show-explanation: Come funziona?
|
|
||||||
expire-default: Scadenza predefinita
|
|
||||||
expire-n-days: '{n} giorno | {n} giorni'
|
|
||||||
expire-n-hours: '{n} ora | {n} ore'
|
|
||||||
expire-n-minutes: '{n} minuto | {n} minuti'
|
|
||||||
expire-n-seconds: '{n} secondo | {n} secondi'
|
|
||||||
items-explanation:
|
|
||||||
- Inserisci un secret nel campo di testo di questa pagina
|
|
||||||
- Il tuo browser critta il secret usando una password generata in modo casuale
|
|
||||||
- Solo il secret crittato viene inviato al server (nè il secret decrittato nè la password vengono mai inviati!)
|
|
||||||
- Il server conserva il secret crittato per un certo periodo di tempo
|
|
||||||
- Passi l'URL visualizzato contenente l'ID e la password di decrittazione al destinatario
|
|
||||||
- 'Il destinatario può vedere il secret esattamente una sola volta: se non può, il secret potrebbe essere stato visto da qualcun altro!'
|
|
||||||
- Dopo che il secret crittato è stato visualizzato la prima volta, viene cancellato dal server
|
|
||||||
label-expiry: 'Scade in:'
|
|
||||||
label-secret-data: 'Dati del secret:'
|
|
||||||
label-secret-files: 'Allega files:'
|
|
||||||
text-attached-files: Il mittente ha allegato alcuni files al secret. Assicurati di fidarti del mittente perchè i files non sono stati controllati!
|
|
||||||
text-burn-hint: Per favore ricorda di non visitare questo URL perchè il secret verrebbe cancellato. Passalo semplicemente a qualcun altro!
|
|
||||||
text-burn-time: 'Se non viene visualizzato prima, questo secret verrà cancellato automaticamente:'
|
|
||||||
text-hint-burned: <strong>Attenzione:</strong> Vedrai il secret solo questa volta. Non appena ricaricherai la pagina verrà cancellato, quindi magari copialo ora…
|
|
||||||
text-invalid-files-selected: Almeno uno dei files selezionati non è consentito come allegato.
|
|
||||||
text-max-filesize: 'Dimensione massima: {maxSize}'
|
|
||||||
text-max-filesize-exceeded: 'Il/I file(s) che hai scelto ha/hanno una dimensione troppo grande per essere allegato/allegati: {curSize} / {maxSize}'
|
|
||||||
text-powered-by: Realizzato con
|
|
||||||
text-pre-reveal-hint: Per rivelare il secret clicca su questo pulsante, ma attenzione perchè farlo lo cancellerà. Lo puoi vedere solo una volta!
|
|
||||||
text-pre-url: 'Il tuo secret è stato creato e salvato usando questo URL:'
|
|
||||||
text-secret-create-disabled: La creazione di nuovi secrets è disabilitata su questa istanza.
|
|
||||||
title-explanation: Questo è come funziona…
|
|
||||||
title-new-secret: Crea un nuovo secret
|
|
||||||
title-reading-secret: Leggi il tuo secret…
|
|
||||||
title-secret-create-disabled: Creazione secret disabilitata…
|
|
||||||
title-secret-created: Secret creato!
|
|
||||||
tooltip-copy-to-clipboard: Copia nella clipboard
|
|
||||||
tooltip-download-as-file: Scarica come file
|
|
||||||
lv:
|
lv:
|
||||||
deeplLanguage: lv
|
deeplLanguage: lv
|
||||||
translators: []
|
translators: []
|
||||||
|
@ -336,50 +269,6 @@ translations:
|
||||||
nl:
|
nl:
|
||||||
deeplLanguage: nl
|
deeplLanguage: nl
|
||||||
translators: []
|
translators: []
|
||||||
translations:
|
|
||||||
alert-secret-not-found: De gegevens die je zocht bestaan niet (meer)… - Als je hier informatie verwachtte dan is de link mogelijk al door iemand anders bekeken!
|
|
||||||
alert-something-went-wrong: Er ging iets verkeerd, sorry…
|
|
||||||
btn-create-secret: Geheim aanmaken!
|
|
||||||
btn-create-secret-processing: Geheim wordt aangemaakt...
|
|
||||||
btn-new-secret: Nieuw
|
|
||||||
btn-reveal-secret: Toon mij het geheim!
|
|
||||||
btn-reveal-secret-processing: Geheim wordt ontsleuteld...
|
|
||||||
btn-show-explanation: Hoe werkt dit?
|
|
||||||
btn-theme-switcher-auto: Auto
|
|
||||||
expire-default: Standaard vervaltermijn
|
|
||||||
expire-n-days: '{n} dag | {n} dagen'
|
|
||||||
expire-n-hours: '{n} uur | {n} uur'
|
|
||||||
expire-n-minutes: '{n} minuut | {n} minuten'
|
|
||||||
expire-n-seconds: '{n} seconde | {n} seconden'
|
|
||||||
items-explanation:
|
|
||||||
- Je vult vertrouwelijke informatie in op deze pagina.
|
|
||||||
- Je browser versleutelt de ingevulde tekst via een automatisch gegenereerd wachtwoord.
|
|
||||||
- Alleen de versleutelde data wordt naar de server gestuurd (de onversleutelde data of het wachtwoord worden nooit verstuurd!)
|
|
||||||
- De server slaat de versleutelde data voor een bepaalde periode op
|
|
||||||
- Je geeft de URL met ID en het gegenereerde wachtwoord aan de ontvanger.
|
|
||||||
- 'De ontvanger kan de vertrouwelijke informatie exact eenmaal bekijken: indien het niet lukt heeft mogelijk iemand anders de info gezien!'
|
|
||||||
- Nadat het versleutelde geheim eenmalig bekeken is, wordt deze van de server verwijderd
|
|
||||||
label-expiry: 'Verwijder na:'
|
|
||||||
label-secret-data: 'Vertrouwelijke info:'
|
|
||||||
label-secret-files: 'Bestanden toevoegen:'
|
|
||||||
text-attached-files: De afzender heeft bestanden toegevoegd. Deze zijn niet gecontroleerd, gebruik deze enkel als je de afzender vertrouwt!
|
|
||||||
text-burn-hint: Open de URL niet zelf, deze is slechts eenmalig te gebruiken. Geef de URL aan de ontvanger.
|
|
||||||
text-burn-time: 'Deze vertrouwelijke informatie wordt automatisch gewist indien niet bekeken voor:'
|
|
||||||
text-hint-burned: <strong>Opgelet:</strong> Je ziet deze informatie alleen nu. Je kan het niet meer opnieuw opvragen als je de pagina verlaat.
|
|
||||||
text-max-filesize: 'Maximum grootte: {maxSize}'
|
|
||||||
text-max-filesize-exceeded: 'De bestanden die je toevoegde zijn te groot: {curSize} / {maxSize}'
|
|
||||||
text-powered-by: Mogelijk gemaakt door
|
|
||||||
text-pre-reveal-hint: 'Gebruik deze knop om het geheim weer te geven. Let op: Je kan dit slechts eenmaal doen!'
|
|
||||||
text-pre-url: 'Het geheim kan opgevraagd worden via deze URL:'
|
|
||||||
text-secret-create-disabled: Het aanmaken van nieuwe geheimen is in deze omgeving uitgeschakeld.
|
|
||||||
title-explanation: Dit is hoe het werkt…
|
|
||||||
title-new-secret: Nieuw geheim aanmaken
|
|
||||||
title-reading-secret: Geheim wordt gelezen…
|
|
||||||
title-secret-create-disabled: Aanmaken geheimen uitgeschakeld...
|
|
||||||
title-secret-created: Geheim aangemaakt!
|
|
||||||
nl-BE:
|
|
||||||
deeplLanguage: nl-BE
|
|
||||||
translators: []
|
|
||||||
translations:
|
translations:
|
||||||
alert-secret-not-found: De gegevens die je zocht bestaan niet (meer)… - Als je hier informatie verwachtte dan is de link mogelijk al door iemand anders bekeken!
|
alert-secret-not-found: De gegevens die je zocht bestaan niet (meer)… - Als je hier informatie verwachtte dan is de link mogelijk al door iemand anders bekeken!
|
||||||
alert-something-went-wrong: Er ging iets verkeerd, sorry…
|
alert-something-went-wrong: Er ging iets verkeerd, sorry…
|
||||||
|
@ -425,7 +314,6 @@ translations:
|
||||||
translators:
|
translators:
|
||||||
- Icikowski
|
- Icikowski
|
||||||
translations:
|
translations:
|
||||||
alert-insecure-environment: Odwiedzasz tę instancję przez niezabezpieczone połączenie. Nie będziesz mógł tworzyć ani odczytywać sekretów.
|
|
||||||
alert-secret-not-found: To nie jest sekret, którego szukasz… - Jeśli spodziewałeś się tu sekretu, to może być on zagrożony, ponieważ ktoś inny mógł już otworzyć ten link.
|
alert-secret-not-found: To nie jest sekret, którego szukasz… - Jeśli spodziewałeś się tu sekretu, to może być on zagrożony, ponieważ ktoś inny mógł już otworzyć ten link.
|
||||||
alert-something-went-wrong: Coś poszło nie tak. Bardzo mi przykro…
|
alert-something-went-wrong: Coś poszło nie tak. Bardzo mi przykro…
|
||||||
btn-create-secret: Stwórz sekret!
|
btn-create-secret: Stwórz sekret!
|
||||||
|
@ -434,7 +322,6 @@ translations:
|
||||||
btn-reveal-secret: Pokaż mi sekret!
|
btn-reveal-secret: Pokaż mi sekret!
|
||||||
btn-reveal-secret-processing: Sekret jest odszyfrowywany...
|
btn-reveal-secret-processing: Sekret jest odszyfrowywany...
|
||||||
btn-show-explanation: Jak to działa?
|
btn-show-explanation: Jak to działa?
|
||||||
btn-theme-switcher-auto: Auto
|
|
||||||
expire-default: Domyślne wygasanie
|
expire-default: Domyślne wygasanie
|
||||||
expire-n-days: '{n} dzień | {n} dni'
|
expire-n-days: '{n} dzień | {n} dni'
|
||||||
expire-n-hours: '{n} godzina | {n} godzin(y)'
|
expire-n-hours: '{n} godzina | {n} godzin(y)'
|
||||||
|
@ -461,14 +348,12 @@ translations:
|
||||||
text-powered-by: Obsługiwane przez
|
text-powered-by: Obsługiwane przez
|
||||||
text-pre-reveal-hint: Aby odsłonić sekret, naciśnij ten przycisk, jednak wiedz, że to zniszczy sekret. Możesz go zobaczyć tylko raz!
|
text-pre-reveal-hint: Aby odsłonić sekret, naciśnij ten przycisk, jednak wiedz, że to zniszczy sekret. Możesz go zobaczyć tylko raz!
|
||||||
text-pre-url: 'Twój sekret został stworzony i zachowany pod tym adresem URL:'
|
text-pre-url: 'Twój sekret został stworzony i zachowany pod tym adresem URL:'
|
||||||
text-secret-burned: Sekret został pomyślnie zniszczony.
|
|
||||||
text-secret-create-disabled: Tworzenie nowych sekretów jest wyłączone na tej instancji.
|
text-secret-create-disabled: Tworzenie nowych sekretów jest wyłączone na tej instancji.
|
||||||
title-explanation: Oto, jak to działa…
|
title-explanation: Oto, jak to działa…
|
||||||
title-new-secret: Stwórz nowy sekret
|
title-new-secret: Stwórz nowy sekret
|
||||||
title-reading-secret: Odczytywanie Twojego sekretu…
|
title-reading-secret: Odczytywanie Twojego sekretu…
|
||||||
title-secret-create-disabled: Tworzenie sekretów wyłączone…
|
title-secret-create-disabled: Tworzenie sekretów wyłączone…
|
||||||
title-secret-created: Sekret utworzony!
|
title-secret-created: Sekret utworzony!
|
||||||
tooltip-burn-secret: Zniszcz sekret teraz!
|
|
||||||
tooltip-copy-to-clipboard: Skopiuj do schowka
|
tooltip-copy-to-clipboard: Skopiuj do schowka
|
||||||
tooltip-download-as-file: Pobierz jako plik
|
tooltip-download-as-file: Pobierz jako plik
|
||||||
pt-BR:
|
pt-BR:
|
||||||
|
@ -551,12 +436,11 @@ translations:
|
||||||
title-secret-created: Секрет создан!
|
title-secret-created: Секрет создан!
|
||||||
sv:
|
sv:
|
||||||
deeplLanguage: sv
|
deeplLanguage: sv
|
||||||
translators:
|
translators: []
|
||||||
- artingu
|
|
||||||
translations:
|
translations:
|
||||||
alert-secret-not-found: Hemlighet hittades inte… - Om du förväntade dig att hemligheten skulle finnas här kan den vara röjd då någon annan kan ha öppnat denna länk tidigare.
|
alert-secret-not-found: Hemlighet hittades inte… - Om du förväntade dig att hemligheten skulle finnas här kan den vara röjd då någon annan kan ha öppnat denna länk tidigare.
|
||||||
alert-something-went-wrong: Något gick fel. Jag ber om ursäkt för detta!…
|
alert-something-went-wrong: Något gick fel. Jag ber om ursäkt för detta!…
|
||||||
btn-create-secret: Skapa hemlighet!
|
btn-create-secret: Skapa hemliget!
|
||||||
btn-create-secret-processing: Hemlighet håller på att skapas..
|
btn-create-secret-processing: Hemlighet håller på att skapas..
|
||||||
btn-new-secret: Ny hemlighet.
|
btn-new-secret: Ny hemlighet.
|
||||||
btn-reveal-secret: Visa mig hemligheten!
|
btn-reveal-secret: Visa mig hemligheten!
|
||||||
|
@ -591,7 +475,7 @@ translations:
|
||||||
title-explanation: Såhär fungerar det…
|
title-explanation: Såhär fungerar det…
|
||||||
title-new-secret: Skapa ny hemlighet
|
title-new-secret: Skapa ny hemlighet
|
||||||
title-reading-secret: Läs din hemlighet…
|
title-reading-secret: Läs din hemlighet…
|
||||||
title-secret-create-disabled: Skapande av hemlighet avaktiverat...
|
title-secret-create-disabled: Hemlig skapelse avaktiverad...
|
||||||
title-secret-created: Hemlighet skapad!
|
title-secret-created: Hemlighet skapad!
|
||||||
tr:
|
tr:
|
||||||
deeplLanguage: tr
|
deeplLanguage: tr
|
||||||
|
|
25
main.go
25
main.go
|
@ -29,14 +29,10 @@ var (
|
||||||
cfg struct {
|
cfg struct {
|
||||||
Customize string `flag:"customize" default:"" description:"Customize-File to load"`
|
Customize string `flag:"customize" default:"" description:"Customize-File to load"`
|
||||||
Listen string `flag:"listen" default:":3000" description:"IP/Port to listen on"`
|
Listen string `flag:"listen" default:":3000" description:"IP/Port to listen on"`
|
||||||
LogRequests bool `flag:"log-requests" default:"true" description:"Enable request logging"`
|
|
||||||
LogLevel string `flag:"log-level" default:"info" description:"Set log level (debug, info, warning, error)"`
|
LogLevel string `flag:"log-level" default:"info" description:"Set log level (debug, info, warning, error)"`
|
||||||
SecretExpiry int64 `flag:"secret-expiry" default:"0" description:"Maximum expiry of the stored secrets in seconds"`
|
SecretExpiry int64 `flag:"secret-expiry" default:"0" description:"Maximum expiry of the stored secrets in seconds"`
|
||||||
StorageType string `flag:"storage-type" default:"mem" description:"Storage to use for putting secrets to" validate:"nonzero"` //revive:disable-line:struct-tag // Matches wrong validation library
|
StorageType string `flag:"storage-type" default:"mem" description:"Storage to use for putting secrets to" validate:"nonzero"`
|
||||||
VersionAndExit bool `flag:"version" default:"false" description:"Print version information and exit"`
|
VersionAndExit bool `flag:"version" default:"false" description:"Print version information and exit"`
|
||||||
EnableTLS bool `flag:"enable-tls" default:"false" description:"Enable HTTPS/TLS"`
|
|
||||||
CertFile string `flag:"cert-file" default:"" description:"Path to the TLS certificate file"`
|
|
||||||
KeyFile string `flag:"key-file" default:"" description:"Path to the TLS private key file"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assets file_helpers.FSStack
|
assets file_helpers.FSStack
|
||||||
|
@ -139,9 +135,7 @@ func main() {
|
||||||
|
|
||||||
var hdl http.Handler = r
|
var hdl http.Handler = r
|
||||||
hdl = http_helpers.GzipHandler(hdl)
|
hdl = http_helpers.GzipHandler(hdl)
|
||||||
if cfg.LogRequests {
|
hdl = http_helpers.NewHTTPLogHandlerWithLogger(hdl, logrus.StandardLogger())
|
||||||
hdl = http_helpers.NewHTTPLogHandlerWithLogger(hdl, logrus.StandardLogger())
|
|
||||||
}
|
|
||||||
|
|
||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
Addr: cfg.Listen,
|
Addr: cfg.Listen,
|
||||||
|
@ -164,19 +158,8 @@ func main() {
|
||||||
"version": version,
|
"version": version,
|
||||||
}).Info("ots started")
|
}).Info("ots started")
|
||||||
|
|
||||||
if cfg.EnableTLS {
|
if err = server.ListenAndServe(); err != nil {
|
||||||
if cfg.CertFile == "" || cfg.KeyFile == "" {
|
logrus.WithError(err).Fatal("HTTP server quit unexpectedly")
|
||||||
logrus.Fatal("TLS is enabled but cert-file or key-file is not provided")
|
|
||||||
}
|
|
||||||
logrus.Infof("Starting HTTPS server on %s", cfg.Listen)
|
|
||||||
if err := server.ListenAndServeTLS(cfg.CertFile, cfg.KeyFile); err != nil {
|
|
||||||
logrus.WithError(err).Fatal("HTTPS server quit unexpectedly")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logrus.Infof("Starting HTTP server on %s", cfg.Listen)
|
|
||||||
if err := server.ListenAndServe(); err != nil {
|
|
||||||
logrus.WithError(err).Fatal("HTTP server quit unexpectedly")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
4408
package-lock.json
generated
Normal file
4408
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
35
package.json
35
package.json
|
@ -1,28 +1,21 @@
|
||||||
{
|
{
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/eslint-parser": "7.27.1",
|
"@babel/eslint-parser": "^7.22.5",
|
||||||
"@types/bootstrap": "5.2.10",
|
"esbuild": "^0.17.17",
|
||||||
"@typescript-eslint/eslint-plugin": "8.32.1",
|
"esbuild-sass-plugin": "^2.9.0",
|
||||||
"@typescript-eslint/parser": "8.32.1",
|
"esbuild-vue": "^1.2.2",
|
||||||
"@vue/tsconfig": "0.7.0",
|
"eslint": "^8.42.0",
|
||||||
"esbuild": "0.25.4",
|
"eslint-plugin-vue": "^9.14.1",
|
||||||
"esbuild-plugin-vue3": "0.4.2",
|
"vue-template-compiler": "^2.7.14"
|
||||||
"esbuild-sass-plugin": "3.3.1",
|
|
||||||
"eslint": "9.26.0",
|
|
||||||
"eslint-plugin-vue": "10.1.0",
|
|
||||||
"typescript": "5.8.3",
|
|
||||||
"vue-eslint-parser": "10.1.3"
|
|
||||||
},
|
},
|
||||||
"name": "ots",
|
"name": "ots",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free": "6.7.2",
|
"base64-js": "^1.5.1",
|
||||||
"base64-js": "1.5.1",
|
"bootstrap": "^5.3.2",
|
||||||
"bootstrap": "5.3.6",
|
"qrcode": "^1.5.3",
|
||||||
"qrcode": "1.5.4",
|
"vue": "^2.7.14",
|
||||||
"vue": "3.5.13",
|
"vue-i18n": "^8.28.2",
|
||||||
"vue-i18n": "11.1.3",
|
"vue-router": "^3.6.5"
|
||||||
"vue-router": "4.5.1"
|
}
|
||||||
},
|
|
||||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
|
||||||
}
|
}
|
|
@ -40,7 +40,7 @@ var HTTPClient HTTPClientIntf = http.DefaultClient
|
||||||
//
|
//
|
||||||
// The corresponding settings are found in `/src/crypto.js` in the OTS
|
// The corresponding settings are found in `/src/crypto.js` in the OTS
|
||||||
// source code.
|
// source code.
|
||||||
var KeyDerivationFunc = openssl.NewPBKDF2Generator(sha512.New, 300000) //nolint:mnd // that's the definition
|
var KeyDerivationFunc = openssl.NewPBKDF2Generator(sha512.New, 300000) //nolint:gomnd // that's the definition
|
||||||
|
|
||||||
// Logger can be set to enable logging from the library. By default
|
// Logger can be set to enable logging from the library. By default
|
||||||
// all log-messages will be discarded.
|
// all log-messages will be discarded.
|
||||||
|
@ -158,7 +158,7 @@ func Fetch(secretURL string) (s Secret, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return s, fmt.Errorf("unescaping fragment: %w", err)
|
return s, fmt.Errorf("unescaping fragment: %w", err)
|
||||||
}
|
}
|
||||||
fragmentParts := strings.SplitN(fragment, "|", 2) //nolint:mnd
|
fragmentParts := strings.SplitN(fragment, "|", 2) //nolint:gomnd
|
||||||
|
|
||||||
fetchURL := u.JoinPath(strings.Join([]string{".", "api", "get", fragmentParts[0]}, "/")).String()
|
fetchURL := u.JoinPath(strings.Join([]string{".", "api", "get", fragmentParts[0]}, "/")).String()
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), RequestTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), RequestTimeout)
|
||||||
|
|
|
@ -1,24 +1,23 @@
|
||||||
module github.com/Luzifer/ots/pkg/client
|
module github.com/Luzifer/ots/pkg/client
|
||||||
|
|
||||||
go 1.23.0
|
go 1.21.1
|
||||||
|
|
||||||
toolchain go1.24.3
|
|
||||||
|
|
||||||
replace github.com/Luzifer/ots/pkg/customization => ../customization
|
replace github.com/Luzifer/ots/pkg/customization => ../customization
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Luzifer/go-openssl/v4 v4.2.4
|
github.com/Luzifer/go-openssl/v4 v4.2.1
|
||||||
github.com/Luzifer/ots/pkg/customization v0.0.0-20250501151834-283ffa548fa8
|
github.com/Luzifer/ots/pkg/customization v0.0.0-20231215213647-d9fe7df6cbb6
|
||||||
github.com/ryanuber/go-glob v1.0.0
|
github.com/ryanuber/go-glob v1.0.0
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.8.4
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
golang.org/x/crypto v0.37.0 // indirect
|
golang.org/x/crypto v0.17.0 // indirect
|
||||||
golang.org/x/sys v0.32.0 // indirect
|
golang.org/x/sys v0.15.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
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
github.com/Luzifer/go-openssl/v4 v4.2.4 h1:3Eu3gSeZpr8Ha+IofVnSWttCL1xejRr/lda4l4TZRWk=
|
github.com/Luzifer/go-openssl/v4 v4.2.1 h1:0+/gaQ5TcBhGmVqGrfyA21eujlbbaNwj0VlOA3nh4ts=
|
||||||
github.com/Luzifer/go-openssl/v4 v4.2.4/go.mod h1:ykquxaR0R1Vor83/FAtGBJZZO5zswuSQTVx1FQc1bJY=
|
github.com/Luzifer/go-openssl/v4 v4.2.1/go.mod h1:CZZZWY0buCtkxrkqDPQYigC4Kn55UuO97TEoV+hwz2s=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
@ -13,15 +13,17 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Frontend has a max attachment size of 64MiB as the base64 encoding
|
// Frontend has a max attachment size of 64MiB as the base64 encoding
|
||||||
|
@ -23,7 +23,6 @@ type (
|
||||||
// Customize holds the structure of the customization file
|
// Customize holds the structure of the customization file
|
||||||
Customize struct {
|
Customize struct {
|
||||||
AppIcon string `json:"appIcon,omitempty" yaml:"appIcon"`
|
AppIcon string `json:"appIcon,omitempty" yaml:"appIcon"`
|
||||||
AppIconDark string `json:"appIconDark,omitempty" yaml:"appIconDark"`
|
|
||||||
AppTitle string `json:"appTitle,omitempty" yaml:"appTitle"`
|
AppTitle string `json:"appTitle,omitempty" yaml:"appTitle"`
|
||||||
DisableAppTitle bool `json:"disableAppTitle,omitempty" yaml:"disableAppTitle"`
|
DisableAppTitle bool `json:"disableAppTitle,omitempty" yaml:"disableAppTitle"`
|
||||||
DisablePoweredBy bool `json:"disablePoweredBy,omitempty" yaml:"disablePoweredBy"`
|
DisablePoweredBy bool `json:"disablePoweredBy,omitempty" yaml:"disablePoweredBy"`
|
||||||
|
@ -41,13 +40,6 @@ type (
|
||||||
MetricsAllowedSubnets []string `json:"-" yaml:"metricsAllowedSubnets"`
|
MetricsAllowedSubnets []string `json:"-" yaml:"metricsAllowedSubnets"`
|
||||||
OverlayFSPath string `json:"-" yaml:"overlayFSPath"`
|
OverlayFSPath string `json:"-" yaml:"overlayFSPath"`
|
||||||
UseFormalLanguage bool `json:"-" yaml:"useFormalLanguage"`
|
UseFormalLanguage bool `json:"-" yaml:"useFormalLanguage"`
|
||||||
|
|
||||||
FooterLinks []FooterLink `json:"footerLinks,omitempty" yaml:"footerLinks"`
|
|
||||||
}
|
|
||||||
|
|
||||||
FooterLink struct {
|
|
||||||
Name string `json:"name" yaml:"name"`
|
|
||||||
URL string `json:"url" yaml:"url"`
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
module github.com/Luzifer/ots/pkg/customization
|
module github.com/Luzifer/ots/pkg/customization
|
||||||
|
|
||||||
go 1.23.0
|
go 1.21.1
|
||||||
|
|
||||||
toolchain go1.24.3
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require golang.org/x/sys v0.32.0 // indirect
|
require golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
|
||||||
|
|
|
@ -10,11 +10,11 @@ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
|
||||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
|
|
|
@ -18,37 +18,14 @@ type (
|
||||||
|
|
||||||
storageMem struct {
|
storageMem struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
store map[string]memStorageSecret
|
store map[string]memStorageSecret
|
||||||
storePruneTimer *time.Ticker
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// New creates a new In-Mem storage
|
// New creates a new In-Mem storage
|
||||||
func New() storage.Storage {
|
func New() storage.Storage {
|
||||||
store := &storageMem{
|
return &storageMem{
|
||||||
store: make(map[string]memStorageSecret),
|
store: make(map[string]memStorageSecret),
|
||||||
storePruneTimer: time.NewTicker(time.Minute),
|
|
||||||
}
|
|
||||||
|
|
||||||
go store.storePruner()
|
|
||||||
|
|
||||||
return store
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *storageMem) storePruner() {
|
|
||||||
for range s.storePruneTimer.C {
|
|
||||||
s.pruneStore()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *storageMem) pruneStore() {
|
|
||||||
s.Lock()
|
|
||||||
defer s.Unlock()
|
|
||||||
|
|
||||||
for k, v := range s.store {
|
|
||||||
if v.hasExpired() {
|
|
||||||
delete(s.store, k)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,16 +68,9 @@ func (s *storageMem) ReadAndDestroy(id string) (string, error) {
|
||||||
|
|
||||||
defer delete(s.store, id)
|
defer delete(s.store, id)
|
||||||
|
|
||||||
// Still check to see if the secret has expired in order to prevent a
|
if !secret.Expiry.IsZero() && secret.Expiry.Before(time.Now()) {
|
||||||
// race condition where a secret has expired but the the store pruner has
|
|
||||||
// not yet been invoked.
|
|
||||||
if secret.hasExpired() {
|
|
||||||
return "", storage.ErrSecretNotFound
|
return "", storage.ErrSecretNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return secret.Secret, nil
|
return secret.Secret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memStorageSecret) hasExpired() bool {
|
|
||||||
return !m.Expiry.IsZero() && m.Expiry.Before(time.Now())
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
|
||||||
"extends": [
|
|
||||||
"local>luzifer/renovate-config"
|
|
||||||
],
|
|
||||||
"packageRules": [
|
|
||||||
{
|
|
||||||
"enabled": false,
|
|
||||||
"matchPackageNames": [
|
|
||||||
"github.com/Luzifer/ots/pkg/**"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
95
src/app.vue
95
src/app.vue
|
@ -1,16 +1,14 @@
|
||||||
|
<!-- eslint-disable vue/no-v-html -->
|
||||||
<template>
|
<template>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<app-navbar
|
<app-navbar />
|
||||||
v-model:theme="theme"
|
|
||||||
@navigate="navigate"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="container mt-4">
|
<div class="container mt-4">
|
||||||
<div
|
<div
|
||||||
v-if="error"
|
v-if="error"
|
||||||
class="row justify-content-center"
|
class="row justify-content-center"
|
||||||
>
|
>
|
||||||
<div class="col-12 col-md-8">
|
<div class="col-8">
|
||||||
<div
|
<div
|
||||||
class="alert alert-danger"
|
class="alert alert-danger"
|
||||||
role="alert"
|
role="alert"
|
||||||
|
@ -21,71 +19,44 @@
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<router-view
|
<router-view @error="displayError" />
|
||||||
@error="displayError"
|
|
||||||
@navigate="navigate"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
v-if="!$root.customize.disablePoweredBy"
|
||||||
class="row mt-4"
|
class="row mt-4"
|
||||||
>
|
>
|
||||||
<div class="col form-text text-center">
|
<div class="col form-text text-center">
|
||||||
<span
|
{{ $t('text-powered-by') }}
|
||||||
v-if="!customize.disablePoweredBy"
|
<a href="https://github.com/Luzifer/ots"><i class="fab fa-github" /> OTS</a>
|
||||||
class="mx-2"
|
{{ $root.version }}
|
||||||
>
|
|
||||||
{{ $t('text-powered-by') }}
|
|
||||||
<a href="https://github.com/Luzifer/ots"><i class="fab fa-github" /> OTS</a>
|
|
||||||
{{ version }}
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
v-for="link in customize.footerLinks"
|
|
||||||
:key="link.url"
|
|
||||||
class="mx-2"
|
|
||||||
>
|
|
||||||
<a :href="link.url">{{ link.name }}</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script>
|
||||||
import { isNavigationFailure, NavigationFailureType } from 'vue-router'
|
|
||||||
|
|
||||||
import AppNavbar from './components/navbar.vue'
|
import AppNavbar from './components/navbar.vue'
|
||||||
import { defineComponent } from 'vue'
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default {
|
||||||
components: { AppNavbar },
|
components: {
|
||||||
|
AppNavbar,
|
||||||
computed: {
|
|
||||||
isSecureEnvironment(): boolean {
|
|
||||||
return Boolean(window.crypto.subtle)
|
|
||||||
},
|
|
||||||
|
|
||||||
version(): string {
|
|
||||||
return window.version
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
this.navigate('/')
|
this.$root.navigate('/')
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
customize: {} as any,
|
error: '',
|
||||||
error: '' as string | null,
|
|
||||||
theme: 'auto',
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
displayError(error: string | null) {
|
displayError(error) {
|
||||||
this.error = error
|
this.error = error
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -98,13 +69,13 @@ export default defineComponent({
|
||||||
|
|
||||||
const parts = hash.substring(1).split('|')
|
const parts = hash.substring(1).split('|')
|
||||||
const secretId = parts[0]
|
const secretId = parts[0]
|
||||||
let securePassword = null as string | null
|
let securePassword = null
|
||||||
|
|
||||||
if (parts.length === 2) {
|
if (parts.length === 2) {
|
||||||
securePassword = parts[1]
|
securePassword = parts[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
this.navigate({
|
this.$root.navigate({
|
||||||
path: '/secret',
|
path: '/secret',
|
||||||
query: {
|
query: {
|
||||||
secretId,
|
secretId,
|
||||||
|
@ -112,44 +83,14 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
navigate(to: string | any): void {
|
|
||||||
this.error = ''
|
|
||||||
this.$router.replace(to)
|
|
||||||
.catch(err => {
|
|
||||||
if (isNavigationFailure(err, NavigationFailureType.duplicated)) {
|
|
||||||
// Hide duplicate nav errors
|
|
||||||
return
|
|
||||||
}
|
|
||||||
throw err
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Trigger initialization functions
|
// Trigger initialization functions
|
||||||
mounted() {
|
mounted() {
|
||||||
this.customize = window.OTSCustomize
|
|
||||||
|
|
||||||
window.onhashchange = this.hashLoad
|
window.onhashchange = this.hashLoad
|
||||||
this.hashLoad()
|
this.hashLoad()
|
||||||
|
|
||||||
if (!this.isSecureEnvironment) {
|
|
||||||
this.error = this.$t('alert-insecure-environment')
|
|
||||||
}
|
|
||||||
|
|
||||||
this.theme = window.getThemeFromStorage()
|
|
||||||
window.matchMedia('(prefers-color-scheme: light)')
|
|
||||||
.addEventListener('change', () => {
|
|
||||||
window.refreshTheme()
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
|
|
||||||
name: 'App',
|
name: 'App',
|
||||||
|
}
|
||||||
watch: {
|
|
||||||
theme(to): void {
|
|
||||||
window.setTheme(to)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -8,13 +8,10 @@
|
||||||
<i :class="{'fas fa-fw fa-clipboard': !copyToClipboardSuccess, 'fas fa-fw fa-circle-check': copyToClipboardSuccess}" />
|
<i :class="{'fas fa-fw fa-clipboard': !copyToClipboardSuccess, 'fas fa-fw fa-circle-check': copyToClipboardSuccess}" />
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
<script>
|
||||||
<script lang="ts">
|
export default {
|
||||||
import { defineComponent } from 'vue'
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
computed: {
|
computed: {
|
||||||
hasClipboard(): boolean {
|
hasClipboard() {
|
||||||
return Boolean(navigator.clipboard && navigator.clipboard.writeText)
|
return Boolean(navigator.clipboard && navigator.clipboard.writeText)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -26,7 +23,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
copy(): void {
|
copy() {
|
||||||
navigator.clipboard.writeText(this.content)
|
navigator.clipboard.writeText(this.content)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.copyToClipboardSuccess = true
|
this.copyToClipboardSuccess = true
|
||||||
|
@ -46,5 +43,5 @@ export default defineComponent({
|
||||||
type: String,
|
type: String,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
<!-- eslint-disable vue/no-v-html -->
|
||||||
<template>
|
<template>
|
||||||
<!-- Creation disabled -->
|
<!-- Creation disabled -->
|
||||||
<div
|
<div
|
||||||
v-if="!showCreateForm"
|
v-if="!canWrite"
|
||||||
class="card border-info-subtle mb-3"
|
class="card border-info-subtle mb-3"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -35,11 +36,10 @@
|
||||||
v-model="secret"
|
v-model="secret"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
:rows="2"
|
:rows="2"
|
||||||
@paste-file="handlePasteFile"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="!customize.disableFileAttachment"
|
v-if="!$root.customize.disableFileAttachment"
|
||||||
class="col-12 mb-3"
|
class="col-12 mb-3"
|
||||||
>
|
>
|
||||||
<label for="createSecretFiles">{{ $t('label-secret-files') }}</label>
|
<label for="createSecretFiles">{{ $t('label-secret-files') }}</label>
|
||||||
|
@ -49,8 +49,8 @@
|
||||||
class="form-control"
|
class="form-control"
|
||||||
type="file"
|
type="file"
|
||||||
multiple
|
multiple
|
||||||
:accept="customize.acceptedFileTypes"
|
:accept="$root.customize.acceptedFileTypes"
|
||||||
@change="handleSelectFiles"
|
@change="updateFileMeta"
|
||||||
>
|
>
|
||||||
<div class="form-text">
|
<div class="form-text">
|
||||||
{{ $t('text-max-filesize', { maxSize: bytesToHuman(maxFileSize) }) }}
|
{{ $t('text-max-filesize', { maxSize: bytesToHuman(maxFileSize) }) }}
|
||||||
|
@ -67,14 +67,6 @@
|
||||||
>
|
>
|
||||||
{{ $t('text-max-filesize-exceeded', { curSize: bytesToHuman(fileSize), maxSize: bytesToHuman(maxFileSize) }) }}
|
{{ $t('text-max-filesize-exceeded', { curSize: bytesToHuman(fileSize), maxSize: bytesToHuman(maxFileSize) }) }}
|
||||||
</div>
|
</div>
|
||||||
<FilesDisplay
|
|
||||||
v-if="attachedFiles.length > 0"
|
|
||||||
class="mt-3"
|
|
||||||
:can-delete="true"
|
|
||||||
:track-download="false"
|
|
||||||
:files="attachedFiles"
|
|
||||||
@file-clicked="deleteFile"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 col-12 order-2 order-md-1">
|
<div class="col-md-6 col-12 order-2 order-md-1">
|
||||||
<button
|
<button
|
||||||
|
@ -92,7 +84,7 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="!customize.disableExpiryOverride"
|
v-if="!$root.customize.disableExpiryOverride"
|
||||||
class="col-md-6 col-12 order-1 order-md-2"
|
class="col-md-6 col-12 order-1 order-md-2"
|
||||||
>
|
>
|
||||||
<div class="row mb-3 justify-content-end">
|
<div class="row mb-3 justify-content-end">
|
||||||
|
@ -107,7 +99,7 @@
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
v-for="opt in expiryChoices"
|
v-for="opt in expiryChoices"
|
||||||
:key="opt.value || 'null'"
|
:key="opt.value"
|
||||||
:value="opt.value"
|
:value="opt.value"
|
||||||
>
|
>
|
||||||
{{ opt.text }}
|
{{ opt.text }}
|
||||||
|
@ -120,12 +112,11 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<script>
|
||||||
|
/* global maxSecretExpire */
|
||||||
|
|
||||||
<script lang="ts">
|
import appCrypto from '../crypto.js'
|
||||||
import appCrypto from '../crypto.ts'
|
|
||||||
import { bytesToHuman } from '../helpers'
|
import { bytesToHuman } from '../helpers'
|
||||||
import { defineComponent } from 'vue'
|
|
||||||
import FilesDisplay from './fileDisplay.vue'
|
|
||||||
import GrowArea from './growarea.vue'
|
import GrowArea from './growarea.vue'
|
||||||
import OTSMeta from '../ots-meta'
|
import OTSMeta from '../ots-meta'
|
||||||
|
|
||||||
|
@ -156,35 +147,30 @@ const internalMaxFileSize = 64 * 1024 * 1024 // 64 MiB
|
||||||
const passwordCharset = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
const passwordCharset = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||||
const passwordLength = 20
|
const passwordLength = 20
|
||||||
|
|
||||||
export default defineComponent({
|
export default {
|
||||||
components: { FilesDisplay, GrowArea },
|
components: { GrowArea },
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
canCreate(): boolean {
|
canCreate() {
|
||||||
return (this.secret.trim().length > 0 || this.selectedFileMeta.length > 0) && !this.maxFileSizeExceeded && !this.invalidFilesSelected
|
return (this.secret.trim().length > 0 || this.selectedFileMeta.length > 0) && !this.maxFileSizeExceeded && !this.invalidFilesSelected
|
||||||
},
|
},
|
||||||
|
|
||||||
customize(): any {
|
expiryChoices() {
|
||||||
return window.OTSCustomize || {}
|
const choices = [{ text: this.$t('expire-default'), value: null }]
|
||||||
},
|
for (const choice of this.$root.customize.expiryChoices || defaultExpiryChoices) {
|
||||||
|
if (maxSecretExpire > 0 && choice > maxSecretExpire) {
|
||||||
expiryChoices(): Record<string, string | null>[] {
|
|
||||||
const choices = [{ text: this.$t('expire-default'), value: null as string | null }]
|
|
||||||
|
|
||||||
for (const choice of this.customize.expiryChoices || defaultExpiryChoices) {
|
|
||||||
if (window.maxSecretExpire > 0 && choice > window.maxSecretExpire) {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
const option = { text: '', value: choice }
|
const option = { value: choice }
|
||||||
if (choice >= 86400) {
|
if (choice >= 86400) {
|
||||||
option.text = this.$t('expire-n-days', Math.round(choice / 86400))
|
option.text = this.$tc('expire-n-days', Math.round(choice / 86400))
|
||||||
} else if (choice >= 3600) {
|
} else if (choice >= 3600) {
|
||||||
option.text = this.$t('expire-n-hours', Math.round(choice / 3600))
|
option.text = this.$tc('expire-n-hours', Math.round(choice / 3600))
|
||||||
} else if (choice >= 60) {
|
} else if (choice >= 60) {
|
||||||
option.text = this.$t('expire-n-minutes', Math.round(choice / 60))
|
option.text = this.$tc('expire-n-minutes', Math.round(choice / 60))
|
||||||
} else {
|
} else {
|
||||||
option.text = this.$t('expire-n-seconds', choice)
|
option.text = this.$tc('expire-n-seconds', choice)
|
||||||
}
|
}
|
||||||
|
|
||||||
choices.push(option)
|
choices.push(option)
|
||||||
|
@ -193,13 +179,13 @@ export default defineComponent({
|
||||||
return choices
|
return choices
|
||||||
},
|
},
|
||||||
|
|
||||||
invalidFilesSelected(): boolean {
|
invalidFilesSelected() {
|
||||||
if (this.customize.acceptedFileTypes === '') {
|
if (this.$root.customize.acceptedFileTypes === '') {
|
||||||
// No limitation configured, no need to check
|
// No limitation configured, no need to check
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const accepted = this.customize.acceptedFileTypes.split(',')
|
const accepted = this.$root.customize.acceptedFileTypes.split(',')
|
||||||
for (const fm of this.selectedFileMeta) {
|
for (const fm of this.selectedFileMeta) {
|
||||||
let isAccepted = false
|
let isAccepted = false
|
||||||
|
|
||||||
|
@ -217,30 +203,21 @@ export default defineComponent({
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
|
|
||||||
isSecureEnvironment(): boolean {
|
maxFileSize() {
|
||||||
return Boolean(window.crypto.subtle)
|
return this.$root.customize.maxAttachmentSizeTotal === 0 ? internalMaxFileSize : Math.min(internalMaxFileSize, this.$root.customize.maxAttachmentSizeTotal)
|
||||||
},
|
},
|
||||||
|
|
||||||
maxFileSize(): number {
|
maxFileSizeExceeded() {
|
||||||
return this.customize.maxAttachmentSizeTotal === 0 ? internalMaxFileSize : Math.min(internalMaxFileSize, this.customize.maxAttachmentSizeTotal)
|
|
||||||
},
|
|
||||||
|
|
||||||
maxFileSizeExceeded(): boolean {
|
|
||||||
return this.fileSize > this.maxFileSize
|
return this.fileSize > this.maxFileSize
|
||||||
},
|
},
|
||||||
|
|
||||||
showCreateForm(): boolean {
|
|
||||||
return this.canWrite && this.isSecureEnvironment
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
created(): void {
|
created() {
|
||||||
this.checkWriteAccess()
|
this.checkWriteAccess()
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
attachedFiles: [],
|
|
||||||
canWrite: null,
|
canWrite: null,
|
||||||
createRunning: false,
|
createRunning: false,
|
||||||
fileSize: 0,
|
fileSize: 0,
|
||||||
|
@ -251,13 +228,11 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
emits: ['error', 'navigate'],
|
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
bytesToHuman,
|
bytesToHuman,
|
||||||
|
|
||||||
checkWriteAccess(): Promise<void> {
|
checkWriteAccess() {
|
||||||
return fetch('api/isWritable', {
|
fetch('api/isWritable', {
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
redirect: 'error',
|
redirect: 'error',
|
||||||
|
@ -274,9 +249,9 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
// createSecret executes the secret creation after encrypting the secret
|
// createSecret executes the secret creation after encrypting the secret
|
||||||
createSecret(): void {
|
createSecret() {
|
||||||
if (!this.canCreate) {
|
if (!this.canCreate) {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encoding large files takes a while, prevent duplicate click on "create"
|
// Encoding large files takes a while, prevent duplicate click on "create"
|
||||||
|
@ -289,9 +264,9 @@ export default defineComponent({
|
||||||
const meta = new OTSMeta()
|
const meta = new OTSMeta()
|
||||||
meta.secret = this.secret
|
meta.secret = this.secret
|
||||||
|
|
||||||
if (this.attachedFiles.length > 0) {
|
if (this.$refs.createSecretFiles) {
|
||||||
for (const f of this.attachedFiles) {
|
for (const f of [...this.$refs.createSecretFiles.files]) {
|
||||||
meta.files.push(f.fileObj)
|
meta.files.push(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,7 +294,7 @@ export default defineComponent({
|
||||||
|
|
||||||
resp.json()
|
resp.json()
|
||||||
.then(data => {
|
.then(data => {
|
||||||
this.$emit('navigate', {
|
this.$root.navigate({
|
||||||
path: '/display-secret-url',
|
path: '/display-secret-url',
|
||||||
query: {
|
query: {
|
||||||
expiresAt: data.expires_at,
|
expiresAt: data.expires_at,
|
||||||
|
@ -334,40 +309,11 @@ export default defineComponent({
|
||||||
this.$emit('error', this.$t('alert-something-went-wrong'))
|
this.$emit('error', this.$t('alert-something-went-wrong'))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return false
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteFile(fileId: string): void {
|
isAcceptedBy(fileMeta, accept) {
|
||||||
this.attachedFiles = [...this.attachedFiles].filter(file => file.id !== fileId)
|
|
||||||
this.updateFileMeta()
|
|
||||||
},
|
|
||||||
|
|
||||||
handlePasteFile(file: File): void {
|
|
||||||
this.attachedFiles.push({
|
|
||||||
fileObj: file,
|
|
||||||
id: window.crypto.randomUUID(),
|
|
||||||
name: file.name,
|
|
||||||
size: file.size,
|
|
||||||
type: file.type,
|
|
||||||
})
|
|
||||||
this.updateFileMeta()
|
|
||||||
},
|
|
||||||
|
|
||||||
handleSelectFiles(): void {
|
|
||||||
for (const file of this.$refs.createSecretFiles.files) {
|
|
||||||
this.attachedFiles.push({
|
|
||||||
fileObj: file,
|
|
||||||
id: window.crypto.randomUUID(),
|
|
||||||
name: file.name,
|
|
||||||
size: file.size,
|
|
||||||
type: file.type,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
this.updateFileMeta()
|
|
||||||
|
|
||||||
this.$refs.createSecretFiles.value = ''
|
|
||||||
},
|
|
||||||
|
|
||||||
isAcceptedBy(fileMeta: any, accept: string): boolean {
|
|
||||||
if (/^(?:[a-z]+|\*)\/(?:[a-zA-Z0-9.+_-]+|\*)$/.test(accept)) {
|
if (/^(?:[a-z]+|\*)\/(?:[a-zA-Z0-9.+_-]+|\*)$/.test(accept)) {
|
||||||
// That's likely supposed to be a mime-type
|
// That's likely supposed to be a mime-type
|
||||||
return RegExp(`^${accept.replaceAll('*', '.*')}$`).test(fileMeta.type)
|
return RegExp(`^${accept.replaceAll('*', '.*')}$`).test(fileMeta.type)
|
||||||
|
@ -380,14 +326,14 @@ export default defineComponent({
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
|
|
||||||
updateFileMeta(): void {
|
updateFileMeta() {
|
||||||
let cumSize = 0
|
let cumSize = 0
|
||||||
for (const f of this.attachedFiles) {
|
for (const f of [...this.$refs.createSecretFiles.files]) {
|
||||||
cumSize += f.size
|
cumSize += f.size
|
||||||
}
|
}
|
||||||
|
|
||||||
this.fileSize = cumSize
|
this.fileSize = cumSize
|
||||||
this.selectedFileMeta = this.attachedFiles.map(file => ({
|
this.selectedFileMeta = [...this.$refs.createSecretFiles.files].map(file => ({
|
||||||
name: file.name,
|
name: file.name,
|
||||||
type: file.type,
|
type: file.type,
|
||||||
}))
|
}))
|
||||||
|
@ -395,5 +341,5 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
name: 'AppCreate',
|
name: 'AppCreate',
|
||||||
})
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -5,10 +5,7 @@
|
||||||
class="card-header bg-success-subtle"
|
class="card-header bg-success-subtle"
|
||||||
v-html="$t('title-secret-created')"
|
v-html="$t('title-secret-created')"
|
||||||
/>
|
/>
|
||||||
<div
|
<div class="card-body">
|
||||||
v-if="!burned"
|
|
||||||
class="card-body"
|
|
||||||
>
|
|
||||||
<p v-html="$t('text-pre-url')" />
|
<p v-html="$t('text-pre-url')" />
|
||||||
<div class="input-group mb-3">
|
<div class="input-group mb-3">
|
||||||
<input
|
<input
|
||||||
|
@ -17,20 +14,13 @@
|
||||||
type="text"
|
type="text"
|
||||||
readonly
|
readonly
|
||||||
:value="secretUrl"
|
:value="secretUrl"
|
||||||
@focus="selectURL"
|
@focus="$refs.secretUrl.select()"
|
||||||
>
|
>
|
||||||
<app-clipboard-button
|
<app-clipboard-button
|
||||||
:content="secretUrl"
|
:content="secretUrl"
|
||||||
:title="$t('tooltip-copy-to-clipboard')"
|
:title="$t('tooltip-copy-to-clipboard')"
|
||||||
/>
|
/>
|
||||||
<app-qr-button :qr-content="secretUrl" />
|
<app-qr-button :qr-content="secretUrl" />
|
||||||
<button
|
|
||||||
class="btn btn-danger"
|
|
||||||
:title="$t('tooltip-burn-secret')"
|
|
||||||
@click="burnSecret"
|
|
||||||
>
|
|
||||||
<i class="fas fa-fire fa-fw" />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<p v-html="$t('text-burn-hint')" />
|
<p v-html="$t('text-burn-hint')" />
|
||||||
<p v-if="expiresAt">
|
<p v-if="expiresAt">
|
||||||
|
@ -38,25 +28,16 @@
|
||||||
<strong>{{ expiresAt.toLocaleString() }}</strong>
|
<strong>{{ expiresAt.toLocaleString() }}</strong>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
v-else
|
|
||||||
class="card-body"
|
|
||||||
>
|
|
||||||
{{ $t('text-secret-burned') }}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<script>
|
||||||
<script lang="ts">
|
|
||||||
import appClipboardButton from './clipboard-button.vue'
|
import appClipboardButton from './clipboard-button.vue'
|
||||||
import appQrButton from './qr-button.vue'
|
import appQrButton from './qr-button.vue'
|
||||||
import { defineComponent } from 'vue'
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default {
|
||||||
components: { appClipboardButton, appQrButton },
|
components: { appClipboardButton, appQrButton },
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
secretUrl(): string {
|
secretUrl() {
|
||||||
return [
|
return [
|
||||||
window.location.href.split('#')[0],
|
window.location.href.split('#')[0],
|
||||||
encodeURIComponent([
|
encodeURIComponent([
|
||||||
|
@ -69,31 +50,16 @@ export default defineComponent({
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
burned: false,
|
|
||||||
popover: null,
|
popover: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
mounted() {
|
||||||
burnSecret(): Promise<void> {
|
|
||||||
return fetch(`api/get/${this.secretId}`)
|
|
||||||
.then(() => {
|
|
||||||
this.burned = true
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
selectURL(): void {
|
|
||||||
this.$refs.secretUrl.select()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted(): void {
|
|
||||||
// Give the interface a moment to transistion and focus
|
// Give the interface a moment to transistion and focus
|
||||||
window.setTimeout(() => this.$refs.secretUrl.focus(), 100)
|
window.setTimeout(() => this.$refs.secretUrl.focus(), 100)
|
||||||
},
|
},
|
||||||
|
|
||||||
name: 'AppDisplayURL',
|
name: 'AppDisplayURL',
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
expiresAt: {
|
expiresAt: {
|
||||||
default: null,
|
default: null,
|
||||||
|
@ -111,5 +77,5 @@ export default defineComponent({
|
||||||
type: String,
|
type: String,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
<!-- eslint-disable vue/no-v-html -->
|
||||||
<template>
|
<template>
|
||||||
<div class="card border-primary-subtle mb-3">
|
<div class="card border-primary-subtle mb-3">
|
||||||
<div
|
<div
|
||||||
|
@ -7,7 +8,7 @@
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<ul>
|
<ul>
|
||||||
<li
|
<li
|
||||||
v-for="(explanation, idx) in $tm('items-explanation')"
|
v-for="(explanation, idx) in $t('items-explanation')"
|
||||||
:key="`idx${idx}`"
|
:key="`idx${idx}`"
|
||||||
>
|
>
|
||||||
{{ explanation }}
|
{{ explanation }}
|
||||||
|
@ -16,11 +17,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<script>
|
||||||
<script lang="ts">
|
export default { name: 'AppExplanation' }
|
||||||
import { defineComponent } from 'vue'
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'AppExplanation',
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,93 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="list-group mb-3">
|
|
||||||
<a
|
|
||||||
v-for="file in files"
|
|
||||||
:key="file.id"
|
|
||||||
class="cursor-pointer list-group-item list-group-item-action font-monospace d-flex align-items-center"
|
|
||||||
:href="file.url"
|
|
||||||
:download="file.name"
|
|
||||||
@click="handleClick(file)"
|
|
||||||
>
|
|
||||||
<i :class="fasFileType(file.type)" />
|
|
||||||
<span>{{ file.name }}</span>
|
|
||||||
<span class="ms-auto">{{ bytesToHuman(file.size) }}</span>
|
|
||||||
<template v-if="trackDownload">
|
|
||||||
<i
|
|
||||||
v-if="!hasDownloaded[file.id]"
|
|
||||||
class="fas fa-fw fa-download ms-2 text-warning"
|
|
||||||
/>
|
|
||||||
<i
|
|
||||||
v-else
|
|
||||||
class="fas fa-fw fa-circle-check ms-2 text-success"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template v-if="canDelete">
|
|
||||||
<i
|
|
||||||
class="fas fa-fw fa-trash ms-2 text-danger"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { bytesToHuman } from '../helpers'
|
|
||||||
import { defineComponent } from 'vue'
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
hasDownloaded: {},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
emits: ['fileClicked'],
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
bytesToHuman,
|
|
||||||
|
|
||||||
fasFileType(type: string): string {
|
|
||||||
return [
|
|
||||||
'fas',
|
|
||||||
'fa-fw',
|
|
||||||
'me-2',
|
|
||||||
...[
|
|
||||||
{ icon: ['fa-file-pdf'], match: /application\/pdf/ },
|
|
||||||
{ icon: ['fa-file-audio'], match: /^audio\// },
|
|
||||||
{ icon: ['fa-file-image'], match: /^image\// },
|
|
||||||
{ icon: ['fa-file-lines'], match: /^text\// },
|
|
||||||
{ icon: ['fa-file-video'], match: /^video\// },
|
|
||||||
{ icon: ['fa-file-zipper'], match: /^application\/(gzip|x-tar|zip)$/ },
|
|
||||||
{ icon: ['fa-file-circle-question'], match: /.*/ },
|
|
||||||
].filter(el => el.match.test(type))[0].icon,
|
|
||||||
].join(' ')
|
|
||||||
},
|
|
||||||
|
|
||||||
handleClick(file: any): void {
|
|
||||||
this.hasDownloaded[file.id] = true
|
|
||||||
this.$emit('fileClicked', file.id)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
name: 'AppFileDisplay',
|
|
||||||
|
|
||||||
props: {
|
|
||||||
canDelete: {
|
|
||||||
default: false,
|
|
||||||
required: false,
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
|
|
||||||
files: {
|
|
||||||
required: true,
|
|
||||||
type: Array<any>,
|
|
||||||
},
|
|
||||||
|
|
||||||
trackDownload: {
|
|
||||||
default: true,
|
|
||||||
required: false,
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -3,14 +3,11 @@
|
||||||
ref="area"
|
ref="area"
|
||||||
v-model="data"
|
v-model="data"
|
||||||
style="resize: none;"
|
style="resize: none;"
|
||||||
@paste="handlePaste"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script>
|
||||||
import { defineComponent } from 'vue'
|
export default {
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
created() {
|
created() {
|
||||||
this.data = this.value
|
this.data = this.value
|
||||||
},
|
},
|
||||||
|
@ -21,14 +18,8 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
emits: ['input', 'pasteFile'],
|
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
changeSize(): void {
|
changeSize() {
|
||||||
if (!this.$refs.area) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const verticalBorderSize = this.getStyle('borderTopWidth') + this.getStyle('borderBottomWidth') || 0
|
const verticalBorderSize = this.getStyle('borderTopWidth') + this.getStyle('borderBottomWidth') || 0
|
||||||
const verticalPaddingSize = this.getStyle('paddingTop') + this.getStyle('paddingBottom') || 0
|
const verticalPaddingSize = this.getStyle('paddingTop') + this.getStyle('paddingBottom') || 0
|
||||||
|
|
||||||
|
@ -39,35 +30,12 @@ export default defineComponent({
|
||||||
this.$refs.area.style.height = `${newHeight}px`
|
this.$refs.area.style.height = `${newHeight}px`
|
||||||
},
|
},
|
||||||
|
|
||||||
getStyle(name: string): number {
|
getStyle(name) {
|
||||||
return parseInt(getComputedStyle(this.$refs.area, null)[name])
|
return parseInt(getComputedStyle(this.$refs.area, null)[name])
|
||||||
},
|
},
|
||||||
|
|
||||||
handlePaste(evt: ClipboardEvent): void {
|
|
||||||
if ([...evt.clipboardData?.items || []]
|
|
||||||
.filter(item => item.kind !== 'string')
|
|
||||||
.length === 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We have something else than text, prevent using clipboard and
|
|
||||||
* pasting and emit an event containing the file data
|
|
||||||
*/
|
|
||||||
evt.stopPropagation()
|
|
||||||
evt.preventDefault()
|
|
||||||
|
|
||||||
for (const item of evt.clipboardData?.items || []) {
|
|
||||||
if (item.kind === 'string') {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$emit('pasteFile', item.getAsFile())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted(): void {
|
mounted() {
|
||||||
this.changeSize()
|
this.changeSize()
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -99,5 +67,5 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -4,18 +4,18 @@
|
||||||
<a
|
<a
|
||||||
class="navbar-brand"
|
class="navbar-brand"
|
||||||
href="#"
|
href="#"
|
||||||
@click.prevent="$emit('navigate', '/')"
|
@click.prevent="$root.navigate('/')"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
v-if="!appIcon"
|
v-if="!$root.customize.appIcon"
|
||||||
class="fas fa-user-secret me-1"
|
class="fas fa-user-secret mr-1"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
v-else
|
v-else
|
||||||
class="me-1"
|
class="mr-1"
|
||||||
:src="appIcon"
|
:src="$root.customize.appIcon"
|
||||||
>
|
>
|
||||||
<span v-if="!customize.disableAppTitle">{{ customize.appTitle }}</span>
|
<span v-if="!$root.customize.disableAppTitle">{{ $root.customize.appTitle }}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
<a
|
<a
|
||||||
class="nav-link"
|
class="nav-link"
|
||||||
href="#"
|
href="#"
|
||||||
@click.prevent="$emit('navigate', '/explanation')"
|
@click.prevent="$root.navigate('/explanation')"
|
||||||
>
|
>
|
||||||
<i class="fas fa-circle-info" /> {{ $t('btn-show-explanation') }}
|
<i class="fas fa-circle-info" /> {{ $t('btn-show-explanation') }}
|
||||||
</a>
|
</a>
|
||||||
|
@ -48,122 +48,35 @@
|
||||||
<a
|
<a
|
||||||
class="nav-link"
|
class="nav-link"
|
||||||
href="#"
|
href="#"
|
||||||
@click.prevent="$emit('navigate', '/')"
|
@click.prevent="$root.navigate('/')"
|
||||||
>
|
>
|
||||||
<i class="fas fa-plus" /> {{ $t('btn-new-secret') }}
|
<i class="fas fa-plus" /> {{ $t('btn-new-secret') }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<form
|
<form
|
||||||
v-if="!customize.disableThemeSwitcher"
|
v-if="!$root.customize.disableThemeSwitcher"
|
||||||
class="d-flex align-items-center btn-group"
|
class="d-flex align-items-center"
|
||||||
>
|
>
|
||||||
<input
|
<i class="fas fa-sun me-2" />
|
||||||
id="theme-light"
|
<div class="form-check form-switch">
|
||||||
v-model="intTheme"
|
<input
|
||||||
type="radio"
|
id="themeswitch"
|
||||||
name="theme"
|
v-model="$root.darkTheme"
|
||||||
class="btn-check"
|
class="form-check-input"
|
||||||
value="light"
|
type="checkbox"
|
||||||
>
|
role="switch"
|
||||||
<label
|
>
|
||||||
class="btn btn-outline-secondary btn-sm"
|
</div>
|
||||||
for="theme-light"
|
<i class="fas fa-moon" />
|
||||||
>
|
|
||||||
<i class="fas fa-sun" />
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<input
|
|
||||||
id="theme-auto"
|
|
||||||
v-model="intTheme"
|
|
||||||
type="radio"
|
|
||||||
name="theme"
|
|
||||||
class="btn-check"
|
|
||||||
value="auto"
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
class="btn btn-outline-secondary btn-sm"
|
|
||||||
for="theme-auto"
|
|
||||||
>
|
|
||||||
{{ $t('btn-theme-switcher-auto') }}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<input
|
|
||||||
id="theme-dark"
|
|
||||||
v-model="intTheme"
|
|
||||||
type="radio"
|
|
||||||
name="theme"
|
|
||||||
class="btn-check"
|
|
||||||
value="dark"
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
class="btn btn-outline-secondary btn-sm"
|
|
||||||
for="theme-dark"
|
|
||||||
>
|
|
||||||
<i class="fas fa-moon" />
|
|
||||||
</label>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script>
|
||||||
import { defineComponent } from 'vue'
|
export default {
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
computed: {
|
|
||||||
appIcon(): string {
|
|
||||||
// Use specified icon or fall back to null
|
|
||||||
const appIcon = this.$parent.customize.appIcon || null
|
|
||||||
// Use specified icon or fall back to light-mode appIcon (which might be null)
|
|
||||||
const darkIcon = this.$parent.customize.appIconDark || appIcon
|
|
||||||
|
|
||||||
return this.$root.theme === 'dark' ? darkIcon : appIcon
|
|
||||||
},
|
|
||||||
|
|
||||||
customize(): any {
|
|
||||||
return this.$parent.customize || {}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
intTheme: '',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
emits: ['navigate', 'update:theme'],
|
|
||||||
|
|
||||||
mounted(): void {
|
|
||||||
this.intTheme = this.theme
|
|
||||||
},
|
|
||||||
|
|
||||||
name: 'AppNavbar',
|
name: 'AppNavbar',
|
||||||
|
}
|
||||||
props: {
|
|
||||||
theme: {
|
|
||||||
required: true,
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
intTheme(to: string, from: string): void {
|
|
||||||
if (to === from) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$emit('update:theme', to)
|
|
||||||
},
|
|
||||||
|
|
||||||
theme(to: string, from: string): void {
|
|
||||||
if (to === from) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.intTheme = to
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<button
|
<button
|
||||||
v-if="!customize.disableQRSupport"
|
v-if="!$root.customize.disableQRSupport"
|
||||||
id="secret-url-qrcode"
|
id="secret-url-qrcode"
|
||||||
ref="qrButton"
|
ref="qrButton"
|
||||||
class="btn btn-secondary"
|
class="btn btn-secondary"
|
||||||
|
@ -9,19 +9,11 @@
|
||||||
<i class="fas fa-qrcode" />
|
<i class="fas fa-qrcode" />
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
<script>
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent } from 'vue'
|
|
||||||
import { Popover } from 'bootstrap'
|
import { Popover } from 'bootstrap'
|
||||||
import qrcode from 'qrcode'
|
import qrcode from 'qrcode'
|
||||||
|
|
||||||
export default defineComponent({
|
export default {
|
||||||
computed: {
|
|
||||||
customize(): any {
|
|
||||||
return window.OTSCustomize || {}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
qrDataURL: null,
|
qrDataURL: null,
|
||||||
|
@ -29,8 +21,8 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
generateQR(): void {
|
generateQR() {
|
||||||
if (window.OTSCustomize.disableQRSupport) {
|
if (this.$root.customize.disableQRSupport) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +33,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted(): void {
|
mounted() {
|
||||||
this.generateQR()
|
this.generateQR()
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -59,7 +51,7 @@ export default defineComponent({
|
||||||
this.generateQR()
|
this.generateQR()
|
||||||
},
|
},
|
||||||
|
|
||||||
qrDataURL(to: string): void {
|
qrDataURL(to) {
|
||||||
if (this.popover) {
|
if (this.popover) {
|
||||||
this.popover.dispose()
|
this.popover.dispose()
|
||||||
}
|
}
|
||||||
|
@ -77,5 +69,5 @@ export default defineComponent({
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
<!-- eslint-disable vue/no-v-html -->
|
||||||
<template>
|
<template>
|
||||||
<div class="card border-primary-subtle mb-3">
|
<div class="card border-primary-subtle mb-3">
|
||||||
<div
|
<div
|
||||||
|
@ -43,7 +44,7 @@
|
||||||
/>
|
/>
|
||||||
<a
|
<a
|
||||||
class="btn btn-secondary"
|
class="btn btn-secondary"
|
||||||
:href="secretContentBlobURL || ''"
|
:href="secretContentBlobURL"
|
||||||
download
|
download
|
||||||
:title="$t('tooltip-download-as-file')"
|
:title="$t('tooltip-download-as-file')"
|
||||||
>
|
>
|
||||||
|
@ -55,29 +56,49 @@
|
||||||
</div>
|
</div>
|
||||||
<template v-if="files.length > 0">
|
<template v-if="files.length > 0">
|
||||||
<p v-html="$t('text-attached-files')" />
|
<p v-html="$t('text-attached-files')" />
|
||||||
<FilesDisplay :files="files" />
|
<div class="list-group mb-3">
|
||||||
|
<a
|
||||||
|
v-for="file in files"
|
||||||
|
:key="file.name"
|
||||||
|
class="list-group-item list-group-item-action font-monospace d-flex align-items-center"
|
||||||
|
:href="file.url"
|
||||||
|
:download="file.name"
|
||||||
|
@click="$set(hasDownloaded, file.name, true)"
|
||||||
|
>
|
||||||
|
<i :class="fasFileType(file.type)" />
|
||||||
|
<span>{{ file.name }}</span>
|
||||||
|
<span class="ms-auto">{{ bytesToHuman(file.size) }}</span>
|
||||||
|
<i
|
||||||
|
v-if="!hasDownloaded[file.name]"
|
||||||
|
class="fas fa-fw fa-download ms-2 text-warning"
|
||||||
|
/>
|
||||||
|
<i
|
||||||
|
v-else
|
||||||
|
class="fas fa-fw fa-circle-check ms-2 text-success"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<p v-html="$t('text-hint-burned')" />
|
<p v-html="$t('text-hint-burned')" />
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<script>
|
||||||
<script lang="ts">
|
|
||||||
import appClipboardButton from './clipboard-button.vue'
|
import appClipboardButton from './clipboard-button.vue'
|
||||||
import appCrypto from '../crypto.ts'
|
import appCrypto from '../crypto.js'
|
||||||
import appQrButton from './qr-button.vue'
|
import appQrButton from './qr-button.vue'
|
||||||
import { defineComponent } from 'vue'
|
import { bytesToHuman } from '../helpers'
|
||||||
import FilesDisplay from './fileDisplay.vue'
|
|
||||||
import GrowArea from './growarea.vue'
|
import GrowArea from './growarea.vue'
|
||||||
import OTSMeta from '../ots-meta'
|
import OTSMeta from '../ots-meta'
|
||||||
|
|
||||||
export default defineComponent({
|
export default {
|
||||||
components: { FilesDisplay, GrowArea, appClipboardButton, appQrButton },
|
components: { GrowArea, appClipboardButton, appQrButton },
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
files: [],
|
files: [],
|
||||||
|
hasDownloaded: {},
|
||||||
popover: null,
|
popover: null,
|
||||||
secret: null,
|
secret: null,
|
||||||
secretContentBlobURL: null,
|
secretContentBlobURL: null,
|
||||||
|
@ -85,11 +106,28 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
emits: ['error'],
|
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
bytesToHuman,
|
||||||
|
|
||||||
|
fasFileType(type) {
|
||||||
|
return [
|
||||||
|
'fas',
|
||||||
|
'fa-fw',
|
||||||
|
'me-2',
|
||||||
|
...[
|
||||||
|
{ icon: ['fa-file-pdf'], match: /application\/pdf/ },
|
||||||
|
{ icon: ['fa-file-audio'], match: /^audio\// },
|
||||||
|
{ icon: ['fa-file-image'], match: /^image\// },
|
||||||
|
{ icon: ['fa-file-lines'], match: /^text\// },
|
||||||
|
{ icon: ['fa-file-video'], match: /^video\// },
|
||||||
|
{ icon: ['fa-file-zipper'], match: /^application\/(gzip|x-tar|zip)$/ },
|
||||||
|
{ icon: ['fa-file-circle-question'], match: /.*/ },
|
||||||
|
].filter(el => el.match.test(type))[0].icon,
|
||||||
|
].join(' ')
|
||||||
|
},
|
||||||
|
|
||||||
// requestSecret requests the encrypted secret from the backend
|
// requestSecret requests the encrypted secret from the backend
|
||||||
requestSecret(): void {
|
requestSecret() {
|
||||||
this.secretLoading = true
|
this.secretLoading = true
|
||||||
window.history.replaceState({}, '', window.location.href.split('#')[0])
|
window.history.replaceState({}, '', window.location.href.split('#')[0])
|
||||||
fetch(`api/get/${this.secretId}`)
|
fetch(`api/get/${this.secretId}`)
|
||||||
|
@ -123,13 +161,7 @@ export default defineComponent({
|
||||||
file.arrayBuffer()
|
file.arrayBuffer()
|
||||||
.then(ab => {
|
.then(ab => {
|
||||||
const blobURL = window.URL.createObjectURL(new Blob([ab], { type: file.type }))
|
const blobURL = window.URL.createObjectURL(new Blob([ab], { type: file.type }))
|
||||||
this.files.push({
|
this.files.push({ name: file.name, size: ab.byteLength, type: file.type, url: blobURL })
|
||||||
id: window.crypto.randomUUID(),
|
|
||||||
name: file.name,
|
|
||||||
size: ab.byteLength,
|
|
||||||
type: file.type,
|
|
||||||
url: blobURL,
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
this.secretLoading = false
|
this.secretLoading = false
|
||||||
|
@ -166,5 +198,5 @@ export default defineComponent({
|
||||||
this.secretContentBlobURL = window.URL.createObjectURL(new Blob([to], { type: 'text/plain' }))
|
this.secretContentBlobURL = window.URL.createObjectURL(new Blob([to], { type: 'text/plain' }))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import base64 from 'base64-js'
|
import base64 from 'base64-js'
|
||||||
|
|
||||||
const opensslBanner = new Uint8Array(new TextEncoder().encode('Salted__'))
|
const opensslBanner = new Uint8Array(new TextEncoder('utf8').encode('Salted__'))
|
||||||
const pbkdf2Params = { hash: 'SHA-512', iterations: 300000, name: 'PBKDF2' }
|
const pbkdf2Params = { hash: 'SHA-512', iterations: 300000, name: 'PBKDF2' }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} cipherText Encrypted data in base64 encoded form
|
* @param {String} cipherText Encrypted data in base64 encoded form
|
||||||
* @param {string} passphrase Encryption passphrase used for key-derivation
|
* @param {String} passphrase Encryption passphrase used for key-derivation
|
||||||
|
* @returns String
|
||||||
*/
|
*/
|
||||||
function dec(cipherText: string, passphrase: string): Promise<string> {
|
function dec(cipherText, passphrase) {
|
||||||
return decrypt(passphrase, cipherText)
|
return decrypt(passphrase, cipherText)
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -16,7 +17,7 @@ function dec(cipherText: string, passphrase: string): Promise<string> {
|
||||||
* @param {String} passphrase Encryption passphrase used for key-derivation
|
* @param {String} passphrase Encryption passphrase used for key-derivation
|
||||||
* @returns String
|
* @returns String
|
||||||
*/
|
*/
|
||||||
function enc(plainText: string, passphrase: string): Promise<string> {
|
function enc(plainText, passphrase) {
|
||||||
return encrypt(passphrase, generateSalt(), plainText)
|
return encrypt(passphrase, generateSalt(), plainText)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +26,7 @@ function enc(plainText: string, passphrase: string): Promise<string> {
|
||||||
* @param {String} encData Encrypted data in base64 encoded form
|
* @param {String} encData Encrypted data in base64 encoded form
|
||||||
* @returns String
|
* @returns String
|
||||||
*/
|
*/
|
||||||
function decrypt(passphrase: string, encData: string): Promise<string> {
|
function decrypt(passphrase, encData) {
|
||||||
const data = base64.toByteArray(encData)
|
const data = base64.toByteArray(encData)
|
||||||
|
|
||||||
return deriveKey(passphrase, data.slice(8, 16))
|
return deriveKey(passphrase, data.slice(8, 16))
|
||||||
|
@ -39,8 +40,8 @@ function decrypt(passphrase: string, encData: string): Promise<string> {
|
||||||
* @param {Uint8Array} salt
|
* @param {Uint8Array} salt
|
||||||
* @returns Object
|
* @returns Object
|
||||||
*/
|
*/
|
||||||
function deriveKey(passphrase: string, salt: Uint8Array): any {
|
function deriveKey(passphrase, salt) {
|
||||||
return window.crypto.subtle.importKey('raw', new TextEncoder().encode(passphrase), 'PBKDF2', false, ['deriveBits'])
|
return window.crypto.subtle.importKey('raw', new TextEncoder('utf8').encode(passphrase), 'PBKDF2', false, ['deriveBits'])
|
||||||
.then(passwordKey => window.crypto.subtle.deriveBits({ ...pbkdf2Params, salt }, passwordKey, 384))
|
.then(passwordKey => window.crypto.subtle.deriveBits({ ...pbkdf2Params, salt }, passwordKey, 384))
|
||||||
.then(key => window.crypto.subtle.importKey('raw', key.slice(0, 32), { name: 'AES-CBC' }, false, ['encrypt', 'decrypt'])
|
.then(key => window.crypto.subtle.importKey('raw', key.slice(0, 32), { name: 'AES-CBC' }, false, ['encrypt', 'decrypt'])
|
||||||
.then(aesKey => ({ iv: key.slice(32, 48), key: aesKey })))
|
.then(aesKey => ({ iv: key.slice(32, 48), key: aesKey })))
|
||||||
|
@ -52,9 +53,9 @@ function deriveKey(passphrase: string, salt: Uint8Array): any {
|
||||||
* @param {String} plainData Data to encrypt
|
* @param {String} plainData Data to encrypt
|
||||||
* @returns String
|
* @returns String
|
||||||
*/
|
*/
|
||||||
function encrypt(passphrase: string, salt: Uint8Array, plainData: string): Promise<string> {
|
function encrypt(passphrase, salt, plainData) {
|
||||||
return deriveKey(passphrase, salt)
|
return deriveKey(passphrase, salt)
|
||||||
.then(({ iv, key }) => window.crypto.subtle.encrypt({ iv, name: 'AES-CBC' }, key, new TextEncoder().encode(plainData)))
|
.then(({ iv, key }) => window.crypto.subtle.encrypt({ iv, name: 'AES-CBC' }, key, new TextEncoder('utf8').encode(plainData)))
|
||||||
.then(encData => new Uint8Array([...opensslBanner, ...salt, ...new Uint8Array(encData)]))
|
.then(encData => new Uint8Array([...opensslBanner, ...salt, ...new Uint8Array(encData)]))
|
||||||
.then(data => base64.fromByteArray(data))
|
.then(data => base64.fromByteArray(data))
|
||||||
}
|
}
|
||||||
|
@ -64,7 +65,7 @@ function encrypt(passphrase: string, salt: Uint8Array, plainData: string): Promi
|
||||||
*
|
*
|
||||||
* @returns Uint8Array
|
* @returns Uint8Array
|
||||||
*/
|
*/
|
||||||
function generateSalt(): Uint8Array {
|
function generateSalt() {
|
||||||
const salt = new Uint8Array(8) // Salt MUST consist of 8 byte
|
const salt = new Uint8Array(8) // Salt MUST consist of 8 byte
|
||||||
return window.crypto.getRandomValues(salt)
|
return window.crypto.getRandomValues(salt)
|
||||||
}
|
}
|
14
src/global.d.ts
vendored
14
src/global.d.ts
vendored
|
@ -1,14 +0,0 @@
|
||||||
export { }
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface Window {
|
|
||||||
getTheme: () => string;
|
|
||||||
getThemeFromStorage: () => string;
|
|
||||||
maxSecretExpire: number;
|
|
||||||
OTSCustomize: any;
|
|
||||||
refreshTheme: () => void;
|
|
||||||
setTheme: (string) => void;
|
|
||||||
useFormalLanguage: boolean;
|
|
||||||
version: string;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +1,9 @@
|
||||||
/**
|
/**
|
||||||
* Converts number of bytes into human format (524288 -> "512.0 KiB")
|
* Converts number of bytes into human format (524288 -> "512.0 KiB")
|
||||||
* @param {number} bytes Byte amount to convert into human readable format
|
* @param {Number} bytes Byte amount to convert into human readable format
|
||||||
* @returns string
|
* @returns String
|
||||||
*/
|
*/
|
||||||
function bytesToHuman(bytes: number): string {
|
function bytesToHuman(bytes) {
|
||||||
for (const t of [
|
for (const t of [
|
||||||
{ thresh: 1024 * 1024, unit: 'MiB' },
|
{ thresh: 1024 * 1024, unit: 'MiB' },
|
||||||
{ thresh: 1024, unit: 'KiB' },
|
{ thresh: 1024, unit: 'KiB' },
|
16
src/i18n.ts
16
src/i18n.ts
|
@ -1,16 +0,0 @@
|
||||||
import { createI18n } from "vue-i18n";
|
|
||||||
|
|
||||||
import messages from './langs/langs.js'
|
|
||||||
|
|
||||||
const cookieSet = Object.fromEntries(document.cookie.split('; ')
|
|
||||||
.map(el => el.split('=')
|
|
||||||
.map(el => decodeURIComponent(el))))
|
|
||||||
|
|
||||||
const i18n = createI18n({
|
|
||||||
legacy: false,
|
|
||||||
fallbackLocale: 'en',
|
|
||||||
locale: cookieSet.lang || navigator?.language || 'en',
|
|
||||||
messages,
|
|
||||||
})
|
|
||||||
|
|
||||||
export default i18n
|
|
|
@ -6,31 +6,27 @@ export default {
|
||||||
'ca': JSON.parse('{"alert-secret-not-found":"Aquest no és el secret que busques\u0026hellip; - Si esperaves que el secret estiguera ací, és possible que s\'haja vist compromés, ja que una altra persona podria haver obert l\'enllaç en comptes de tu.","alert-something-went-wrong":"Alguna cosa ha eixit malament. Ens sap molt greu\u0026hellip;","btn-create-secret":"Crea el secret!","btn-create-secret-processing":"El secret s\'està creant...","btn-new-secret":"Nou secret","btn-reveal-secret":"Mostra\'m el secret!","btn-reveal-secret-processing":"El secret s\'està desxifrant...","btn-show-explanation":"Com funciona?","expire-default":"Caducitat predeterminada","expire-n-days":"{n} dia | {n} dies","expire-n-hours":"{n} hora | {n} hores","expire-n-minutes":"{n} minut | {n} minuts","expire-n-seconds":"{n} segon | {n} segons","items-explanation":["Introduïx un secret en el formulari que hi ha en aquesta pàgina","El teu navegador xifra el secret utilitzant una contrasenya generada","Únicament s\'envia al servidor el secret xifrat (mai s\'envien ni el secret sense xifrar ni la contrasenya!)","El servidor emmagatzema el secret xifrat durant un temps limitat","Envia al destinatari l\'enllaç mostrat, que conté l\'identificador del secret i la contrasenya de desxifrat","El destinatari pot veure el secret una sola vegada: si no pot, el secret podria haver sigut vist per una altra persona!","Quan s\'ha obtingut per primera i única vegada el secret xifrat, s\'elimina del servidor"],"label-expiry":"Caduca en:","label-secret-data":"Informació secreta:","label-secret-files":"Adjuntar arxius:","text-attached-files":"El remitent ha adjuntat arxius al secret. Assegura\'t que confies en aquesta persona, ja que els arxius no han sigut revisats.","text-burn-hint":"Per favor, recorda no accedir a aquest enllaç tu mateix, ja que això destruiria el secret. Només has de passar-li\'l a una altra persona!","text-burn-time":"Si no es mostra abans, aquest secret s\'eliminarà automàticament:","text-hint-burned":"\u003cstrong\u003eAtenció:\u003c/strong\u003e Només veuràs això una vegada. Quan recarregues la pàgina, el secret desapareixerà, així que copia\'l ja\u0026hellip;","text-max-filesize":"Mida màxima: {maxSize}","text-max-filesize-exceeded":"Els arxius seleccionats són massa grans per adjuntar-los: {curSize} / {maxSize}","text-powered-by":"Funciona amb","text-pre-reveal-hint":"Per a mostrar el secret prem aquest botó, però tingues en compte que en fer-ho es destruirà. Només pots veure\'l una vegada!","text-pre-url":"El teu secret ha sigut creat i emmagatzemat en el següent enllaç:","text-secret-create-disabled":"La creació de nous secrets està desactivada en aquesta instància.","title-explanation":"Així és com funciona\u0026hellip;","title-new-secret":"Crea un nou secret","title-reading-secret":"Obtenint el teu secret\u0026hellip;","title-secret-create-disabled":"S\'ha desactivat la creació de secrets...","title-secret-created":"Secret creat!"}'),
|
'ca': JSON.parse('{"alert-secret-not-found":"Aquest no és el secret que busques\u0026hellip; - Si esperaves que el secret estiguera ací, és possible que s\'haja vist compromés, ja que una altra persona podria haver obert l\'enllaç en comptes de tu.","alert-something-went-wrong":"Alguna cosa ha eixit malament. Ens sap molt greu\u0026hellip;","btn-create-secret":"Crea el secret!","btn-create-secret-processing":"El secret s\'està creant...","btn-new-secret":"Nou secret","btn-reveal-secret":"Mostra\'m el secret!","btn-reveal-secret-processing":"El secret s\'està desxifrant...","btn-show-explanation":"Com funciona?","expire-default":"Caducitat predeterminada","expire-n-days":"{n} dia | {n} dies","expire-n-hours":"{n} hora | {n} hores","expire-n-minutes":"{n} minut | {n} minuts","expire-n-seconds":"{n} segon | {n} segons","items-explanation":["Introduïx un secret en el formulari que hi ha en aquesta pàgina","El teu navegador xifra el secret utilitzant una contrasenya generada","Únicament s\'envia al servidor el secret xifrat (mai s\'envien ni el secret sense xifrar ni la contrasenya!)","El servidor emmagatzema el secret xifrat durant un temps limitat","Envia al destinatari l\'enllaç mostrat, que conté l\'identificador del secret i la contrasenya de desxifrat","El destinatari pot veure el secret una sola vegada: si no pot, el secret podria haver sigut vist per una altra persona!","Quan s\'ha obtingut per primera i única vegada el secret xifrat, s\'elimina del servidor"],"label-expiry":"Caduca en:","label-secret-data":"Informació secreta:","label-secret-files":"Adjuntar arxius:","text-attached-files":"El remitent ha adjuntat arxius al secret. Assegura\'t que confies en aquesta persona, ja que els arxius no han sigut revisats.","text-burn-hint":"Per favor, recorda no accedir a aquest enllaç tu mateix, ja que això destruiria el secret. Només has de passar-li\'l a una altra persona!","text-burn-time":"Si no es mostra abans, aquest secret s\'eliminarà automàticament:","text-hint-burned":"\u003cstrong\u003eAtenció:\u003c/strong\u003e Només veuràs això una vegada. Quan recarregues la pàgina, el secret desapareixerà, així que copia\'l ja\u0026hellip;","text-max-filesize":"Mida màxima: {maxSize}","text-max-filesize-exceeded":"Els arxius seleccionats són massa grans per adjuntar-los: {curSize} / {maxSize}","text-powered-by":"Funciona amb","text-pre-reveal-hint":"Per a mostrar el secret prem aquest botó, però tingues en compte que en fer-ho es destruirà. Només pots veure\'l una vegada!","text-pre-url":"El teu secret ha sigut creat i emmagatzemat en el següent enllaç:","text-secret-create-disabled":"La creació de nous secrets està desactivada en aquesta instància.","title-explanation":"Així és com funciona\u0026hellip;","title-new-secret":"Crea un nou secret","title-reading-secret":"Obtenint el teu secret\u0026hellip;","title-secret-create-disabled":"S\'ha desactivat la creació de secrets...","title-secret-created":"Secret creat!"}'),
|
||||||
|
|
||||||
'de': switchFormal(
|
'de': switchFormal(
|
||||||
JSON.parse('{"alert-insecure-environment":"Sie besuchen diese Instanz über eine unsichere Verbindung. Sie können deswegen keine Secrets erstellen oder lesen.","alert-secret-not-found":"Dieses Secret existiert nicht. - Falls Sie diesen Link noch nicht selbst geöffnet haben, könnte der Inhalt kompromittiert sein, da jemand anderes den Link geöffnet haben könnte.","alert-something-went-wrong":"Irgendwas ging schief. Entschuldigung\u0026hellip;","btn-create-secret":"Secret erstellen!","btn-create-secret-processing":"Secret wird erstellt…","btn-new-secret":"Neues Secret","btn-reveal-secret":"Secret anzeigen","btn-reveal-secret-processing":"Secret wird entschlüsselt…","btn-show-explanation":"Wie funktioniert das?","btn-theme-switcher-auto":"Auto","expire-default":"Server-Standard","expire-n-days":"{n} Tag | {n} Tage","expire-n-hours":"{n} Stunde | {n} Stunden","expire-n-minutes":"{n} Minute | {n} Minuten","expire-n-seconds":"{n} Sekunde | {n} Sekunden","items-explanation":["Sie geben ein Secret auf dieser Seite ein","Ihr Browser verschlüsselt das Secret mit einem generierten Passwort","Nur das verschlüsselte Secret wird an den Server geschickt (das Passwort oder das Secret im Klartext werden niemals übertragen!)","Der Server speichert das verschlüsselte Secret für eine Weile","Sie geben die angezeigte URL, welche die ID und das Passwort des Secrets enthält, an den Empfänger","Der Empfänger kann das Secret einmalig abrufen: Funktioniert das nicht, könnte jemand anderes es abgerufen haben!","Wenn das verschlüsselte Secret das erste Mal abgerufen wurde, wird es automatisch vom Server gelöscht"],"label-expiry":"Ablauf in:","label-secret-data":"Inhalt des Secrets:","label-secret-files":"Dateien Anhängen:","text-attached-files":"Der Absender hat Dateien an das Secret angehängt. Stellen Sie sicher, dass Sie dem Absender vertrauen, da die Dateien nicht geprüft wurden!","text-burn-hint":"Bitte rufen Sie die URL nicht selbst auf, da das Secret dadurch zerstört würde.","text-burn-time":"Wenn es vorher nicht eingesehen wurde, wird dieses Secret automatisch gelöscht:","text-hint-burned":"\u003cstrong\u003eAchtung:\u003c/strong\u003e Sie können das Secret nur einmal ansehen! Sobald Sie die Seite neu laden, kann das Secret nicht erneut abgerufen werden, also besser direkt kopieren und sicher abspeichern\u0026hellip;","text-invalid-files-selected":"Mindestens eine der ausgewählten Dateien ist nicht als Anhang erlaubt.","text-max-filesize":"Maximale Größe: {maxSize}","text-max-filesize-exceeded":"Die ausgewählten Dateien übersteigen die maximale Größe: {curSize} / {maxSize}","text-powered-by":"Läuft mit","text-pre-reveal-hint":"Klicken Sie auf diesen Button um das Secret anzuzeigen, bedenken Sie aber, dass das Secret nur einmal angezeigt und dabei gelöscht wird.","text-pre-url":"Das Secret wurde angelegt und unter folgender URL gespeichert:","text-secret-burned":"Das Secret wurde zerstört.","text-secret-create-disabled":"Auf dieser Instanz wurde das Erstellen neuer Secrets deaktiviert.","title-explanation":"So funktioniert es\u0026hellip;","title-new-secret":"Ein neues Secret erstellen","title-reading-secret":"Secret auslesen\u0026hellip;","title-secret-create-disabled":"Erstellen von Secrets deaktiviert…","title-secret-created":"Secret erstellt!","tooltip-burn-secret":"Secret jetzt zerstören!","tooltip-copy-to-clipboard":"In die Zwischenablage kopieren","tooltip-download-as-file":"Als Datei herunterladen"}'),
|
JSON.parse('{"alert-secret-not-found":"Dieses Secret existiert nicht. - Falls Sie diesen Link noch nicht selbst geöffnet haben, könnte der Inhalt kompromittiert sein, da jemand anderes den Link geöffnet haben könnte.","alert-something-went-wrong":"Irgendwas ging schief. Entschuldigung\u0026hellip;","btn-create-secret":"Secret erstellen!","btn-create-secret-processing":"Secret wird erstellt…","btn-new-secret":"Neues Secret","btn-reveal-secret":"Secret anzeigen","btn-reveal-secret-processing":"Secret wird entschlüsselt…","btn-show-explanation":"Wie funktioniert das?","expire-default":"Server-Standard","expire-n-days":"{n} Tag | {n} Tage","expire-n-hours":"{n} Stunde | {n} Stunden","expire-n-minutes":"{n} Minute | {n} Minuten","expire-n-seconds":"{n} Sekunde | {n} Sekunden","items-explanation":["Sie geben ein Secret auf dieser Seite ein","Ihr Browser verschlüsselt das Secret mit einem generierten Passwort","Nur das verschlüsselte Secret wird an den Server geschickt (das Passwort oder das Secret im Klartext werden niemals übertragen!)","Der Server speichert das verschlüsselte Secret für eine Weile","Sie geben die angezeigte URL, welche die ID und das Passwort des Secrets enthält, an den Empfänger","Der Empfänger kann das Secret einmalig abrufen: Funktioniert das nicht, könnte jemand anderes es abgerufen haben!","Wenn das verschlüsselte Secret das erste Mal abgerufen wurde, wird es automatisch vom Server gelöscht"],"label-expiry":"Ablauf in:","label-secret-data":"Inhalt des Secrets:","label-secret-files":"Dateien Anhängen:","text-attached-files":"Der Absender hat Dateien an das Secret angehängt. Stellen Sie sicher, dass Sie dem Absender vertrauen, da die Dateien nicht geprüft wurden!","text-burn-hint":"Bitte rufen Sie die URL nicht selbst auf, da das Secret dadurch zerstört würde.","text-burn-time":"Wenn es vorher nicht eingesehen wurde, wird dieses Secret automatisch gelöscht:","text-hint-burned":"\u003cstrong\u003eAchtung:\u003c/strong\u003e Sie können das Secret nur einmal ansehen! Sobald Sie die Seite neu laden, kann das Secret nicht erneut abgerufen werden, also besser direkt kopieren und sicher abspeichern\u0026hellip;","text-invalid-files-selected":"Mindestens eine der ausgewählten Dateien ist nicht als Anhang erlaubt.","text-max-filesize":"Maximale Größe: {maxSize}","text-max-filesize-exceeded":"Die ausgewählten Dateien übersteigen die maximale Größe: {curSize} / {maxSize}","text-powered-by":"Läuft mit","text-pre-reveal-hint":"Klicken Sie auf diesen Button um das Secret anzuzeigen, bedenken Sie aber, dass das Secret nur einmal angezeigt und dabei gelöscht wird.","text-pre-url":"Das Secret wurde angelegt und unter folgender URL gespeichert:","text-secret-create-disabled":"Auf dieser Instanz wurde das Erstellen neuer Secrets deaktiviert.","title-explanation":"So funktioniert es\u0026hellip;","title-new-secret":"Ein neues Secret erstellen","title-reading-secret":"Secret auslesen\u0026hellip;","title-secret-create-disabled":"Erstellen von Secrets deaktiviert…","title-secret-created":"Secret erstellt!","tooltip-copy-to-clipboard":"In die Zwischenablage kopieren","tooltip-download-as-file":"Als Datei herunterladen"}'),
|
||||||
JSON.parse('{"alert-insecure-environment":"Du besuchst diese Instanz über eine unsichere Verbindung. Du kannst deswegen keine Secrets erstellen oder lesen.","alert-secret-not-found":"Das ist nicht das Secret, was du suchst\u0026hellip; - Falls du diesen Link noch nicht selbst geöffnet hast, könnte das Secret kompromittiert sein, da jemand anderes den Link geöffnet haben könnte.","alert-something-went-wrong":"Irgendwas ging schief. Entschuldigung\u0026hellip;","btn-create-secret":"Secret erstellen!","btn-create-secret-processing":"Secret wird erstellt…","btn-new-secret":"Neues Secret","btn-reveal-secret":"Zeig mir das Secret!","btn-reveal-secret-processing":"Secret wird entschlüsselt…","btn-show-explanation":"Wie funktioniert das?","btn-theme-switcher-auto":"Auto","expire-default":"Server-Standard","expire-n-days":"{n} Tag | {n} Tage","expire-n-hours":"{n} Stunde | {n} Stunden","expire-n-minutes":"{n} Minute | {n} Minuten","expire-n-seconds":"{n} Sekunde | {n} Sekunden","items-explanation":["Du gibst ein Secret auf dieser Seite ein","Dein Browser verschlüsselt das Secret mit einem generierten Passwort","Nur das verschlüsselte Secret wird an den Server geschickt (das Passwort oder das Secret im Klartext werden niemals übertragen!)","Der Server speichert das verschlüsselte Secret für eine Weile","Du gibst die angezeigte URL, welche die ID und das Passwort des Secrets enthält, an den Empfänger","Der Empfänger kann das Secret einmalig abrufen: Funktioniert das nicht, könnte jemand anderes es abgerufen haben!","Wenn das verschlüsselte Secret das erste Mal abgerufen wurde, wird es automatisch vom Server gelöscht"],"label-expiry":"Ablauf in:","label-secret-data":"Inhalt des Secrets:","label-secret-files":"Dateien Anhängen:","text-attached-files":"Der Absender hat Dateien an das Secret angehängt. Stell sicher, dass du dem Absender vertraust, da die Dateien nicht geprüft wurden!","text-burn-hint":"Bitte rufe die URL nicht selbst auf, da das Secret dadurch zerstört würde. Gib sie einfach weiter!","text-burn-time":"Wenn es vorher nicht eingesehen wurde, wird dieses Secret automatisch gelöscht:","text-hint-burned":"\u003cstrong\u003eAchtung:\u003c/strong\u003e Du kannst das nur einmal ansehen! Sobald du die Seite neu lädst, ist das Secret verschwunden, also besser direkt kopieren und sicher abspeichern\u0026hellip;","text-invalid-files-selected":"Mindestens eine der ausgewählten Dateien ist nicht als Anhang erlaubt.","text-max-filesize":"Maximale Größe: {maxSize}","text-max-filesize-exceeded":"Die ausgewählten Dateien übersteigen die maximale Größe: {curSize} / {maxSize}","text-powered-by":"Läuft mit","text-pre-reveal-hint":"Um das Secret anzuzeigen klicke diesen Button aber denk dran, dass das Secret nur einmal angezeigt und dabei gelöscht wird.","text-pre-url":"Dein Secret wurde angelegt und unter folgender URL gespeichert:","text-secret-burned":"Das Secret wurde zerstört.","text-secret-create-disabled":"Auf dieser Instanz wurde das Erstellen neuer Secrets deaktiviert.","title-explanation":"So funktioniert es\u0026hellip;","title-new-secret":"Erstelle ein neues Secret","title-reading-secret":"Secret auslesen\u0026hellip;","title-secret-create-disabled":"Erstellen von Secrets deaktiviert…","title-secret-created":"Secret erstellt!","tooltip-burn-secret":"Secret jetzt zerstören!","tooltip-copy-to-clipboard":"In die Zwischenablage kopieren","tooltip-download-as-file":"Als Datei herunterladen"}'),
|
JSON.parse('{"alert-secret-not-found":"Das ist nicht das Secret, was du suchst\u0026hellip; - Falls du diesen Link noch nicht selbst geöffnet hast, könnte das Secret kompromittiert sein, da jemand anderes den Link geöffnet haben könnte.","alert-something-went-wrong":"Irgendwas ging schief. Entschuldigung\u0026hellip;","btn-create-secret":"Secret erstellen!","btn-create-secret-processing":"Secret wird erstellt…","btn-new-secret":"Neues Secret","btn-reveal-secret":"Zeig mir das Secret!","btn-reveal-secret-processing":"Secret wird entschlüsselt…","btn-show-explanation":"Wie funktioniert das?","expire-default":"Server-Standard","expire-n-days":"{n} Tag | {n} Tage","expire-n-hours":"{n} Stunde | {n} Stunden","expire-n-minutes":"{n} Minute | {n} Minuten","expire-n-seconds":"{n} Sekunde | {n} Sekunden","items-explanation":["Du gibst ein Secret auf dieser Seite ein","Dein Browser verschlüsselt das Secret mit einem generierten Passwort","Nur das verschlüsselte Secret wird an den Server geschickt (das Passwort oder das Secret im Klartext werden niemals übertragen!)","Der Server speichert das verschlüsselte Secret für eine Weile","Du gibst die angezeigte URL, welche die ID und das Passwort des Secrets enthält, an den Empfänger","Der Empfänger kann das Secret einmalig abrufen: Funktioniert das nicht, könnte jemand anderes es abgerufen haben!","Wenn das verschlüsselte Secret das erste Mal abgerufen wurde, wird es automatisch vom Server gelöscht"],"label-expiry":"Ablauf in:","label-secret-data":"Inhalt des Secrets:","label-secret-files":"Dateien Anhängen:","text-attached-files":"Der Absender hat Dateien an das Secret angehängt. Stell sicher, dass du dem Absender vertraust, da die Dateien nicht geprüft wurden!","text-burn-hint":"Bitte rufe die URL nicht selbst auf, da das Secret dadurch zerstört würde. Gib sie einfach weiter!","text-burn-time":"Wenn es vorher nicht eingesehen wurde, wird dieses Secret automatisch gelöscht:","text-hint-burned":"\u003cstrong\u003eAchtung:\u003c/strong\u003e Du kannst das nur einmal ansehen! Sobald du die Seite neu lädst, ist das Secret verschwunden, also besser direkt kopieren und sicher abspeichern\u0026hellip;","text-invalid-files-selected":"Mindestens eine der ausgewählten Dateien ist nicht als Anhang erlaubt.","text-max-filesize":"Maximale Größe: {maxSize}","text-max-filesize-exceeded":"Die ausgewählten Dateien übersteigen die maximale Größe: {curSize} / {maxSize}","text-powered-by":"Läuft mit","text-pre-reveal-hint":"Um das Secret anzuzeigen klicke diesen Button aber denk dran, dass das Secret nur einmal angezeigt und dabei gelöscht wird.","text-pre-url":"Dein Secret wurde angelegt und unter folgender URL gespeichert:","text-secret-create-disabled":"Auf dieser Instanz wurde das Erstellen neuer Secrets deaktiviert.","title-explanation":"So funktioniert es\u0026hellip;","title-new-secret":"Erstelle ein neues Secret","title-reading-secret":"Secret auslesen\u0026hellip;","title-secret-create-disabled":"Erstellen von Secrets deaktiviert…","title-secret-created":"Secret erstellt!","tooltip-copy-to-clipboard":"In die Zwischenablage kopieren","tooltip-download-as-file":"Als Datei herunterladen"}'),
|
||||||
),
|
),
|
||||||
|
|
||||||
'en': JSON.parse('{"alert-insecure-environment":"You are accessing this instance using an insecure connection. You will not be able to create or read secrets.","alert-secret-not-found":"This is not the secret you are looking for\u0026hellip; - If you expected the secret to be here it might be compromised as someone else might have opened the link already.","alert-something-went-wrong":"Something went wrong. I\'m very sorry about this\u0026hellip;","btn-create-secret":"Create the secret!","btn-create-secret-processing":"Secret is being created…","btn-new-secret":"New Secret","btn-reveal-secret":"Show me the secret!","btn-reveal-secret-processing":"Secret is being decrypted…","btn-show-explanation":"How does this work?","btn-theme-switcher-auto":"Auto","expire-default":"Default Expiry","expire-n-days":"{n} day | {n} days","expire-n-hours":"{n} hour | {n} hours","expire-n-minutes":"{n} minute | {n} minutes","expire-n-seconds":"{n} second | {n} seconds","items-explanation":["You enter a secret into the field on this page","Your browser encrypts the secret using a generated password","Only the encrypted secret is sent to the server (neither the plain secret nor the password are ever sent!)","The server stores the encrypted secret for a certain time","You pass the displayed URL containing the ID and the decryption password to the recipient","The recipient can view the secret exactly once: If they can\'t, the secret might have been viewed by someone else!","After the encrypted secret has been retrieved once, it is deleted from the server"],"label-expiry":"Expire in:","label-secret-data":"Secret data:","label-secret-files":"Attach Files:","text-attached-files":"The sender attached files to the secret. Make sure you trust the sender as the files were not checked!","text-burn-hint":"Please remember not to go to this URL yourself as that would destroy the secret. Just pass it to someone else!","text-burn-time":"If not viewed before, this secret will automatically be deleted:","text-hint-burned":"\u003cstrong\u003eAttention:\u003c/strong\u003e You\'re only seeing this once. As soon as you reload the page the secret will be gone so maybe copy it now\u0026hellip;","text-invalid-files-selected":"At least one of the selected files is not allowed as an attachment.","text-max-filesize":"Maximum size: {maxSize}","text-max-filesize-exceeded":"The file(s) you chose are too big to attach: {curSize} / {maxSize}","text-powered-by":"Powered by","text-pre-reveal-hint":"To reveal the secret click this button but be aware doing so will destroy the secret. You can only view it once!","text-pre-url":"Your secret was created and stored using this URL:","text-secret-burned":"The secret was successfully destroyed.","text-secret-create-disabled":"The creation of new secrets is disabled in this instance.","title-explanation":"This is how it works\u0026hellip;","title-new-secret":"Create a new secret","title-reading-secret":"Reading your secret\u0026hellip;","title-secret-create-disabled":"Secret creation disabled…","title-secret-created":"Secret created!","tooltip-burn-secret":"Burn Secret now!","tooltip-copy-to-clipboard":"Copy to Clipboard","tooltip-download-as-file":"Download as File"}'),
|
'en': JSON.parse('{"alert-secret-not-found":"This is not the secret you are looking for\u0026hellip; - If you expected the secret to be here it might be compromised as someone else might have opened the link already.","alert-something-went-wrong":"Something went wrong. I\'m very sorry about this\u0026hellip;","btn-create-secret":"Create the secret!","btn-create-secret-processing":"Secret is being created…","btn-new-secret":"New Secret","btn-reveal-secret":"Show me the secret!","btn-reveal-secret-processing":"Secret is being decrypted…","btn-show-explanation":"How does this work?","expire-default":"Default Expiry","expire-n-days":"{n} day | {n} days","expire-n-hours":"{n} hour | {n} hours","expire-n-minutes":"{n} minute | {n} minutes","expire-n-seconds":"{n} second | {n} seconds","items-explanation":["You enter a secret into the field on this page","Your browser encrypts the secret using a generated password","Only the encrypted secret is sent to the server (neither the plain secret nor the password are ever sent!)","The server stores the encrypted secret for a certain time","You pass the displayed URL containing the ID and the decryption password to the recipient","The recipient can view the secret exactly once: If they can\'t, the secret might have been viewed by someone else!","After the encrypted secret has been retrieved once, it is deleted from the server"],"label-expiry":"Expire in:","label-secret-data":"Secret data:","label-secret-files":"Attach Files:","text-attached-files":"The sender attached files to the secret. Make sure you trust the sender as the files were not checked!","text-burn-hint":"Please remember not to go to this URL yourself as that would destroy the secret. Just pass it to someone else!","text-burn-time":"If not viewed before, this secret will automatically be deleted:","text-hint-burned":"\u003cstrong\u003eAttention:\u003c/strong\u003e You\'re only seeing this once. As soon as you reload the page the secret will be gone so maybe copy it now\u0026hellip;","text-invalid-files-selected":"At least one of the selected files is not allowed as an attachment.","text-max-filesize":"Maximum size: {maxSize}","text-max-filesize-exceeded":"The file(s) you chose are too big to attach: {curSize} / {maxSize}","text-powered-by":"Powered by","text-pre-reveal-hint":"To reveal the secret click this button but be aware doing so will destroy the secret. You can only view it once!","text-pre-url":"Your secret was created and stored using this URL:","text-secret-create-disabled":"The creation of new secrets is disabled in this instance.","title-explanation":"This is how it works\u0026hellip;","title-new-secret":"Create a new secret","title-reading-secret":"Reading your secret\u0026hellip;","title-secret-create-disabled":"Secret creation disabled…","title-secret-created":"Secret created!","tooltip-copy-to-clipboard":"Copy to Clipboard","tooltip-download-as-file":"Download as File"}'),
|
||||||
|
|
||||||
'es': JSON.parse('{"alert-secret-not-found":"Este no es el secreto que buscas\u0026hellip; - Si esperabas que el secreto estuviera aquí, es posible que se haya visto comprometido, ya que otra persona podría haber abierto el enlace en tu lugar.","alert-something-went-wrong":"Algo ha salido mal. Lo sentimos mucho\u0026hellip;","btn-create-secret":"¡Crea el secreto!","btn-create-secret-processing":"El secreto se está creando...","btn-new-secret":"Nuevo secreto","btn-reveal-secret":"¡Muéstrame el secreto!","btn-reveal-secret-processing":"El secreto se está descifrando...","btn-show-explanation":"¿Cómo funciona?","expire-default":"Caducidad predeterminada","expire-n-days":"{n} día | {n} días","expire-n-hours":"{n} hora | {n} horas","expire-n-minutes":"{n} minuto | {n} minutos","expire-n-seconds":"{n} segundo | {n} segundos","items-explanation":["Introduce un secreto en el formulario que hay en esta página","Tu navegador cifra el secreto utilizando una contraseña generada","Únicamente se envía al servidor el secreto cifrado (¡nunca se envían ni el secreto sin cifrar ni la contraseña!)","El servidor almacena el secreto cifrado durante un tiempo limitado","Envía al destinatario el enlace mostrado, que contiene el identificador del secreto y la contraseña de descifrado","El destinatario puede ver el secreto una sola vez: si no puede, ¡el secreto podría haber sido visto por otra persona!","Cuando se ha obtenido por primera y única vez el secreto cifrado, se elimina del servidor"],"label-expiry":"Caduca en:","label-secret-data":"Información secreta:","label-secret-files":"Adjuntar archivos:","text-attached-files":"El remitente ha adjuntado archivos al secreto. Asegúrate de que confías en esta persona, ya que los archivos no han sido revisados.","text-burn-hint":"Por favor, recuerda no acceder a este enlace tú mismo, ya que esto destruiría el secreto. ¡Solo tienes que pasárselo a otra persona!","text-burn-time":"Si no se muestra antes, este secreto se eliminará automáticamente:","text-hint-burned":"\u003cstrong\u003eAtención:\u003c/strong\u003e Solo verás esto una vez. En cuanto recargues la página, el secreto desaparecerá, así que cópialo ya\u0026hellip;","text-max-filesize":"Tamaño máximo: {maxSize}","text-max-filesize-exceeded":"Los archivos seleccionados son demasiado grandes para adjuntarlos: {curSize} / {maxSize}","text-powered-by":"Funciona con","text-pre-reveal-hint":"Para mostrar el secreto pulsa este botón, pero ten en cuenta que al hacerlo se destruirá. ¡Solo puedes verlo una vez!","text-pre-url":"Tu secreto ha sido creado y almacenado en el siguiente enlace:","text-secret-create-disabled":"La creación de nuevos secretos está desactivada en esta instancia.","title-explanation":"Así es como funciona\u0026hellip;","title-new-secret":"Crea un nuevo secreto","title-reading-secret":"Obteniendo tu secreto\u0026hellip;","title-secret-create-disabled":"Creación de secretos desactivada...","title-secret-created":"¡Secreto creado!"}'),
|
'es': JSON.parse('{"alert-secret-not-found":"Este no es el secreto que buscas\u0026hellip; - Si esperabas que el secreto estuviera aquí, es posible que se haya visto comprometido, ya que otra persona podría haber abierto el enlace en tu lugar.","alert-something-went-wrong":"Algo ha salido mal. Lo sentimos mucho\u0026hellip;","btn-create-secret":"¡Crea el secreto!","btn-create-secret-processing":"El secreto se está creando...","btn-new-secret":"Nuevo secreto","btn-reveal-secret":"¡Muéstrame el secreto!","btn-reveal-secret-processing":"El secreto se está descifrando...","btn-show-explanation":"¿Cómo funciona?","expire-default":"Caducidad predeterminada","expire-n-days":"{n} día | {n} días","expire-n-hours":"{n} hora | {n} horas","expire-n-minutes":"{n} minuto | {n} minutos","expire-n-seconds":"{n} segundo | {n} segundos","items-explanation":["Introduce un secreto en el formulario que hay en esta página","Tu navegador cifra el secreto utilizando una contraseña generada","Únicamente se envía al servidor el secreto cifrado (¡nunca se envían ni el secreto sin cifrar ni la contraseña!)","El servidor almacena el secreto cifrado durante un tiempo limitado","Envía al destinatario el enlace mostrado, que contiene el identificador del secreto y la contraseña de descifrado","El destinatario puede ver el secreto una sola vez: si no puede, ¡el secreto podría haber sido visto por otra persona!","Cuando se ha obtenido por primera y única vez el secreto cifrado, se elimina del servidor"],"label-expiry":"Caduca en:","label-secret-data":"Información secreta:","label-secret-files":"Adjuntar archivos:","text-attached-files":"El remitente ha adjuntado archivos al secreto. Asegúrate de que confías en esta persona, ya que los archivos no han sido revisados.","text-burn-hint":"Por favor, recuerda no acceder a este enlace tú mismo, ya que esto destruiría el secreto. ¡Solo tienes que pasárselo a otra persona!","text-burn-time":"Si no se muestra antes, este secreto se eliminará automáticamente:","text-hint-burned":"\u003cstrong\u003eAtención:\u003c/strong\u003e Solo verás esto una vez. En cuanto recargues la página, el secreto desaparecerá, así que cópialo ya\u0026hellip;","text-max-filesize":"Tamaño máximo: {maxSize}","text-max-filesize-exceeded":"Los archivos seleccionados son demasiado grandes para adjuntarlos: {curSize} / {maxSize}","text-powered-by":"Funciona con","text-pre-reveal-hint":"Para mostrar el secreto pulsa este botón, pero ten en cuenta que al hacerlo se destruirá. ¡Solo puedes verlo una vez!","text-pre-url":"Tu secreto ha sido creado y almacenado en el siguiente enlace:","text-secret-create-disabled":"La creación de nuevos secretos está desactivada en esta instancia.","title-explanation":"Así es como funciona\u0026hellip;","title-new-secret":"Crea un nuevo secreto","title-reading-secret":"Obteniendo tu secreto\u0026hellip;","title-secret-create-disabled":"Creación de secretos desactivada...","title-secret-created":"¡Secreto creado!"}'),
|
||||||
|
|
||||||
'fr': JSON.parse('{"alert-secret-not-found":"Ce secret n\'est pas celui que vous cherchez\u0026hellip; - Si vous comptiez trouvez ce secret ici, il a pu être compromis car quelqu\'un a probablement déjà ouvert le lien.","alert-something-went-wrong":"Un problème est survenu. Nous en sommes désolés\u0026hellip;","btn-create-secret":"Créer le secret!","btn-create-secret-processing":"Secret en cours de création ...","btn-new-secret":"Nouveau secret","btn-reveal-secret":"Voir le secret!","btn-reveal-secret-processing":"Secret en cours de déchiffrement ...","btn-show-explanation":"Comment ça fonctionne?","expire-default":"Expiration par défaut","expire-n-days":"{n} jour | {n} jours","expire-n-hours":"{n} heure | {n} heures","expire-n-minutes":"{n} minute | {n} minutes","expire-n-seconds":"{n} seconde | {n} secondes","items-explanation":["Vous saisissez le secret dans un champ sur cette page","Votre navigateur chiffre le secret en utilisant un mot de passe généré","Seul le secret chiffré est envoyé au serveur (ni le secret en clair, ni le mot de passe ne sont envoyés!)","Le serveur stocke le secret chiffré pendant un certain temps","Vous fournissez l\'URL affichée contenant l\'identifiant et le mot de passe de déchiffrage au destinataire","Le destinataire ne peut voir le secret qu\'une fois: si cela ne fonctionne pas, c\'est que le secret a été consulté par quelqu\'un d\'autre!","Dès que le secret chiffré a été récupéré, il est supprimé du serveur"],"label-expiry":"Expiration dans:","label-secret-data":"Données secrètes:","label-secret-files":"Attacher des fichiers:","text-attached-files":"L\'émetteur a attaché des fichiers au secret. Assurez-vous d\'avoir confiance en l\'émetteur, les fichiers n\'ont pas été vérifiés !","text-burn-hint":"Attention de ne pas ouvrir cette URL vous-même, cela détruirait le secret. Fournissez-la à quelqu\'un d\'autre!","text-burn-time":"S\'il n\'a pas été vu auparavant, ce secret sera automatiquement supprimé :","text-hint-burned":"\u003cstrong\u003eAttention:\u003c/strong\u003e Vous ne pouvez consulter ce contenu qu\'une fois. Le secret sera détruit dès que vous rechargez la page, donc copiez le maintenant\u0026hellip;","text-invalid-files-selected":"Au moins l\'un des fichiers sélectionnés n\'est pas autorisé comme pièce-jointe.","text-max-filesize":"Taille maximum: {maxSize}","text-max-filesize-exceeded":"Le(s) fichier(s) que vous avez choisis sont trop volumineux pour être attachés : {curSize} / {maxSize}","text-powered-by":"Propulsé par","text-pre-reveal-hint":"Pour afficher le secret, cliquez sur ce bouton, mais soyez conscient que cela le détruira. Vous ne pouvez l\'afficher qu\'une fois!","text-pre-url":"Votre secret a été créé et stocké à cette URL:","text-secret-create-disabled":"La création de nouveaux secrets est désactivée dans ce cas.","title-explanation":"Voici comment ça fonctionne\u0026hellip;","title-new-secret":"Créer un nouveau secret","title-reading-secret":"Lecture du secret\u0026hellip;","title-secret-create-disabled":"Création secrète désactivée...","title-secret-created":"Secret créé!","tooltip-copy-to-clipboard":"Copier dans le presse-papiers","tooltip-download-as-file":"Télécharger en tant que fichier"}'),
|
'fr': JSON.parse('{"alert-secret-not-found":"Ce secret n\'est pas celui que vous cherchez\u0026hellip; - Si vous comptiez trouvez ce secret ici, il a pu être compromis car quelqu\'un a probablement déjà ouvert le lien.","alert-something-went-wrong":"Un problème est survenu. Nous en sommes désolés\u0026hellip;","btn-create-secret":"Créer le secret!","btn-new-secret":"Nouveau secret","btn-reveal-secret":"Voir le secret!","btn-show-explanation":"Comment ça fonctionne?","expire-n-days":"{n} jour | {n} jours","expire-n-hours":"{n} heure | {n} heures","expire-n-minutes":"{n} minute | {n} minutes","expire-n-seconds":"{n} seconde | {n} secondes","items-explanation":["Vous saisissez le secret dans un champ sur cette page","Votre navigateur chiffre le secret en utilisant un mot de passe généré","Seul le secret chiffré est envoyé au serveur (ni le secret en clair, ni le mot de passe ne sont envoyés!)","Le serveur stocke le secret chiffré pendant un certain temps","Vous fournissez l\'URL affichée contenant l\'identifiant et le mot de passe de déchiffrage au destinataire","Le destintaire ne peut voir le secret qu\'une fois: si cela ne fonctionne pas, c\'est que le secret a été consulté par quelqu\'un d\'autre!","Dès que le secret chiffré a été récupéré, il est supprimé du serveur"],"label-secret-data":"Données secrètes:","text-burn-hint":"Attention de ne pas ouvrir cette URL vous-même, cela détruirait le secret. Fournissez-la à quelqu\'un d\'autre!","text-hint-burned":"\u003cstrong\u003eAttention:\u003c/strong\u003e Vous ne pouvez consulter ce contenu qu\'une fois. Le secret sera détruit dès que vous rechargez la page, donc copiez le maintenant\u0026hellip;","text-powered-by":"Propulsé par","text-pre-reveal-hint":"Pour afficher le secret, cliquez sur ce bouton, mais soyez conscient que cela le détruira. Vous ne pouvez l\'afficher qu\'une fois!","text-pre-url":"Votre secret a été créé et stocké à cette URL:","text-secret-create-disabled":"La création de nouveaux secrets est désactivée dans ce cas.","title-explanation":"Voici comment ça fonctionne\u0026hellip;","title-new-secret":"Créer un nouveau secret","title-reading-secret":"Lecture du secret\u0026hellip;","title-secret-create-disabled":"Création secrète désactivée...","title-secret-created":"Secret créé!"}'),
|
||||||
|
|
||||||
'it': JSON.parse('{"alert-secret-not-found":"Questo non è il secret che stai cercando\u0026hellip; - Se ti aspettavi di vedere il secret allora potrebbe essere stato compromesso poichè qualcun altro potrebbe aver già aperto il link.","alert-something-went-wrong":"Qualcosa non ha funzionato. Mi dispiace davvero\u0026hellip;","btn-create-secret":"Crea il secret!","btn-create-secret-processing":"Creazione del secret in corso…","btn-new-secret":"Nuovo secret","btn-reveal-secret":"Mostrami il secret!","btn-reveal-secret-processing":"Decrittazione del secret in corso…","btn-show-explanation":"Come funziona?","expire-default":"Scadenza predefinita","expire-n-days":"{n} giorno | {n} giorni","expire-n-hours":"{n} ora | {n} ore","expire-n-minutes":"{n} minuto | {n} minuti","expire-n-seconds":"{n} secondo | {n} secondi","items-explanation":["Inserisci un secret nel campo di testo di questa pagina","Il tuo browser critta il secret usando una password generata in modo casuale","Solo il secret crittato viene inviato al server (nè il secret decrittato nè la password vengono mai inviati!)","Il server conserva il secret crittato per un certo periodo di tempo","Passi l\'URL visualizzato contenente l\'ID e la password di decrittazione al destinatario","Il destinatario può vedere il secret esattamente una sola volta: se non può, il secret potrebbe essere stato visto da qualcun altro!","Dopo che il secret crittato è stato visualizzato la prima volta, viene cancellato dal server"],"label-expiry":"Scade in:","label-secret-data":"Dati del secret:","label-secret-files":"Allega files:","text-attached-files":"Il mittente ha allegato alcuni files al secret. Assicurati di fidarti del mittente perchè i files non sono stati controllati!","text-burn-hint":"Per favore ricorda di non visitare questo URL perchè il secret verrebbe cancellato. Passalo semplicemente a qualcun altro!","text-burn-time":"Se non viene visualizzato prima, questo secret verrà cancellato automaticamente:","text-hint-burned":"\u003cstrong\u003eAttenzione:\u003c/strong\u003e Vedrai il secret solo questa volta. Non appena ricaricherai la pagina verrà cancellato, quindi magari copialo ora\u0026hellip;","text-invalid-files-selected":"Almeno uno dei files selezionati non è consentito come allegato.","text-max-filesize":"Dimensione massima: {maxSize}","text-max-filesize-exceeded":"Il/I file(s) che hai scelto ha/hanno una dimensione troppo grande per essere allegato/allegati: {curSize} / {maxSize}","text-powered-by":"Realizzato con","text-pre-reveal-hint":"Per rivelare il secret clicca su questo pulsante, ma attenzione perchè farlo lo cancellerà. Lo puoi vedere solo una volta!","text-pre-url":"Il tuo secret è stato creato e salvato usando questo URL:","text-secret-create-disabled":"La creazione di nuovi secrets è disabilitata su questa istanza.","title-explanation":"Questo è come funziona\u0026hellip;","title-new-secret":"Crea un nuovo secret","title-reading-secret":"Leggi il tuo secret\u0026hellip;","title-secret-create-disabled":"Creazione secret disabilitata…","title-secret-created":"Secret creato!","tooltip-copy-to-clipboard":"Copia nella clipboard","tooltip-download-as-file":"Scarica come file"}'),
|
|
||||||
|
|
||||||
'lv': JSON.parse('{"alert-secret-not-found":"\u003cstrong\u003eZiņa nav atrasta!\u003c/strong\u003e\u0026hellip; - Ja ievadītā saite ir pareiza, tad ir beidzies ziņas glabāšanas laiks, vai arī tā jau vienreiz ir atvērta.","alert-something-went-wrong":"Neparedzēta sistēmas kļūda. Atvainojiet par sagādātajām neērtībām\u0026hellip;","btn-create-secret":"Šifrēt ziņu!","btn-new-secret":"Jauna ziņa","btn-reveal-secret":"Atvērt ziņu!","btn-show-explanation":"Kā tas strādā?","expire-n-days":"{n} diena | {n} dienas","expire-n-hours":"{n} stunda | {n} stundas","expire-n-minutes":"{n} minūte | {n} minūtes","expire-n-seconds":"{n} sekundes | {n} sekundes","items-explanation":["Tu ievadi ziņu ievades laukā","Pārlūks nošifrē ziņu ar uzģenerētu paroli","Tikai šifrētā ziņa tiek nosūtīta serverim (nešifrētā ziņa un parole sūtīta netiek!)","Serveris noteiktu laiku glabā šifrēto ziņu","Tu nodod URL ar ziņas ID un atšifrēšanas paroli saņēmējam","Saņēmējs var atvērt ziņu tikai vienreiz: ja tas neizdodas, iespējams, ziņu jau atvēris kāds cits!","Kad ziņa tiek atvērta pirmo reizi, tā no servera tiek dzēsta"],"label-secret-data":"Ziņa:","text-burn-hint":"Lūdzu atceries neatvērt saiti pats, jo tad ziņa tiks dzēsta. Nodod saiti ziņas saņēmējam!","text-hint-burned":"\u003cstrong\u003eUzmanību:\u003c/strong\u003e Ziņa tiek parādīta tikai vienu reizi. Līdzko lapa tiks pārlādēta, ziņa būs neatgriezeniski zaudēta, tāpēc nepieciešamības gadījumā nokopē to tagad\u0026hellip;","text-powered-by":"Darbina","text-pre-reveal-hint":"Lai parādītu ziņu nospied šo pogu, bet rēķinies ar to, ka pēc apskates ziņa vairs nebūs pieejama. To var atvērt tikai vienreiz!","text-pre-url":"Ziņa ir nošifrēta un ir atverama šajā adresē:","text-secret-create-disabled":"Šajā gadījumā jaunu noslēpumu izveide ir atspējota.","title-explanation":"Tā tas strādā\u0026hellip;","title-new-secret":"Šifrēt ziņu","title-reading-secret":"Atver ziņu\u0026hellip;","title-secret-create-disabled":"Slepena izveide atspējota...","title-secret-created":"Ziņa nošifrēta!"}'),
|
'lv': JSON.parse('{"alert-secret-not-found":"\u003cstrong\u003eZiņa nav atrasta!\u003c/strong\u003e\u0026hellip; - Ja ievadītā saite ir pareiza, tad ir beidzies ziņas glabāšanas laiks, vai arī tā jau vienreiz ir atvērta.","alert-something-went-wrong":"Neparedzēta sistēmas kļūda. Atvainojiet par sagādātajām neērtībām\u0026hellip;","btn-create-secret":"Šifrēt ziņu!","btn-new-secret":"Jauna ziņa","btn-reveal-secret":"Atvērt ziņu!","btn-show-explanation":"Kā tas strādā?","expire-n-days":"{n} diena | {n} dienas","expire-n-hours":"{n} stunda | {n} stundas","expire-n-minutes":"{n} minūte | {n} minūtes","expire-n-seconds":"{n} sekundes | {n} sekundes","items-explanation":["Tu ievadi ziņu ievades laukā","Pārlūks nošifrē ziņu ar uzģenerētu paroli","Tikai šifrētā ziņa tiek nosūtīta serverim (nešifrētā ziņa un parole sūtīta netiek!)","Serveris noteiktu laiku glabā šifrēto ziņu","Tu nodod URL ar ziņas ID un atšifrēšanas paroli saņēmējam","Saņēmējs var atvērt ziņu tikai vienreiz: ja tas neizdodas, iespējams, ziņu jau atvēris kāds cits!","Kad ziņa tiek atvērta pirmo reizi, tā no servera tiek dzēsta"],"label-secret-data":"Ziņa:","text-burn-hint":"Lūdzu atceries neatvērt saiti pats, jo tad ziņa tiks dzēsta. Nodod saiti ziņas saņēmējam!","text-hint-burned":"\u003cstrong\u003eUzmanību:\u003c/strong\u003e Ziņa tiek parādīta tikai vienu reizi. Līdzko lapa tiks pārlādēta, ziņa būs neatgriezeniski zaudēta, tāpēc nepieciešamības gadījumā nokopē to tagad\u0026hellip;","text-powered-by":"Darbina","text-pre-reveal-hint":"Lai parādītu ziņu nospied šo pogu, bet rēķinies ar to, ka pēc apskates ziņa vairs nebūs pieejama. To var atvērt tikai vienreiz!","text-pre-url":"Ziņa ir nošifrēta un ir atverama šajā adresē:","text-secret-create-disabled":"Šajā gadījumā jaunu noslēpumu izveide ir atspējota.","title-explanation":"Tā tas strādā\u0026hellip;","title-new-secret":"Šifrēt ziņu","title-reading-secret":"Atver ziņu\u0026hellip;","title-secret-create-disabled":"Slepena izveide atspējota...","title-secret-created":"Ziņa nošifrēta!"}'),
|
||||||
|
|
||||||
'nl': JSON.parse('{"alert-secret-not-found":"De gegevens die je zocht bestaan niet (meer)\u0026hellip; - Als je hier informatie verwachtte dan is de link mogelijk al door iemand anders bekeken!","alert-something-went-wrong":"Er ging iets verkeerd, sorry\u0026hellip;","btn-create-secret":"Geheim aanmaken!","btn-create-secret-processing":"Geheim wordt aangemaakt...","btn-new-secret":"Nieuw","btn-reveal-secret":"Toon mij het geheim!","btn-reveal-secret-processing":"Geheim wordt ontsleuteld...","btn-show-explanation":"Hoe werkt dit?","btn-theme-switcher-auto":"Auto","expire-default":"Standaard vervaltermijn","expire-n-days":"{n} dag | {n} dagen","expire-n-hours":"{n} uur | {n} uur","expire-n-minutes":"{n} minuut | {n} minuten","expire-n-seconds":"{n} seconde | {n} seconden","items-explanation":["Je vult vertrouwelijke informatie in op deze pagina.","Je browser versleutelt de ingevulde tekst via een automatisch gegenereerd wachtwoord.","Alleen de versleutelde data wordt naar de server gestuurd (de onversleutelde data of het wachtwoord worden nooit verstuurd!)","De server slaat de versleutelde data voor een bepaalde periode op","Je geeft de URL met ID en het gegenereerde wachtwoord aan de ontvanger.","De ontvanger kan de vertrouwelijke informatie exact eenmaal bekijken: indien het niet lukt heeft mogelijk iemand anders de info gezien!","Nadat het versleutelde geheim eenmalig bekeken is, wordt deze van de server verwijderd"],"label-expiry":"Verwijder na:","label-secret-data":"Vertrouwelijke info:","label-secret-files":"Bestanden toevoegen:","text-attached-files":"De afzender heeft bestanden toegevoegd. Deze zijn niet gecontroleerd, gebruik deze enkel als je de afzender vertrouwt!","text-burn-hint":"Open de URL niet zelf, deze is slechts eenmalig te gebruiken. Geef de URL aan de ontvanger.","text-burn-time":"Deze vertrouwelijke informatie wordt automatisch gewist indien niet bekeken voor:","text-hint-burned":"\u003cstrong\u003eOpgelet:\u003c/strong\u003e Je ziet deze informatie alleen nu. Je kan het niet meer opnieuw opvragen als je de pagina verlaat.","text-max-filesize":"Maximum grootte: {maxSize}","text-max-filesize-exceeded":"De bestanden die je toevoegde zijn te groot: {curSize} / {maxSize}","text-powered-by":"Mogelijk gemaakt door","text-pre-reveal-hint":"Gebruik deze knop om het geheim weer te geven. Let op: Je kan dit slechts eenmaal doen!","text-pre-url":"Het geheim kan opgevraagd worden via deze URL:","text-secret-create-disabled":"Het aanmaken van nieuwe geheimen is in deze omgeving uitgeschakeld.","title-explanation":"Dit is hoe het werkt\u0026hellip;","title-new-secret":"Nieuw geheim aanmaken","title-reading-secret":"Geheim wordt gelezen\u0026hellip;","title-secret-create-disabled":"Aanmaken geheimen uitgeschakeld...","title-secret-created":"Geheim aangemaakt!"}'),
|
'nl': JSON.parse('{"alert-secret-not-found":"De gegevens die je zocht bestaan niet (meer)\u0026hellip; - Als je hier informatie verwachtte dan is de link mogelijk al door iemand anders bekeken!","alert-something-went-wrong":"Er ging iets verkeerd, sorry\u0026hellip;","btn-create-secret":"Nieuwe vertrouwelijke info aanmaken!","btn-create-secret-processing":"Vertrouwelijke info wordt versleuteld...","btn-new-secret":"Nieuw","btn-reveal-secret":"Toon mij de vertrouwelijke info!","btn-reveal-secret-processing":"Vertrouwelijke info wordt ontsleuteld...","btn-show-explanation":"Hoe werkt dit?","expire-default":"Standaard termijn","expire-n-days":"{n} dag | {n} dagen","expire-n-hours":"{n} uur | {n} uur","expire-n-minutes":"{n} minuut | {n} minuten","expire-n-seconds":"{n} seconde | {n} seconden","items-explanation":["Je vult vertrouwelijke informatie in op deze pagina.","Je browser versleutelt de ingevulde tekst via een automatisch gegenereerd wachtwoord.","Alleen de versleutelde data wordt naar de server gestuurd. (De leesbare versie of het wachtwoord worden nooit verstuurd!)","De server slaat de versleutelde data gedurende een beperkte periode op.","Je geeft de URL met identificatie en het gegenereerde wachtwoord aan de ontvanger.","De ontvanger kan de vertrouwelijke informatie exact eenmaal bekijken: indien het niet lukt heeft mogelijk iemand anders de info gezien!","De versleutelde data wordt van de server gewist van zodra de ontvanger het bekeken heeft."],"label-expiry":"Verwijder na:","label-secret-data":"Vertrouwelijke info:","label-secret-files":"Bestanden toevoegen:","text-attached-files":"De afzender heeft bestanden toegevoegd. Deze werden niet gecontroleerd, gebruik deze enkel als je de afzender vertrouwt!","text-burn-hint":"Bezoek de URL niet zelf: je kan deze slechts eenmaal gebruiken. Geef de URL aan de ontvanger.","text-burn-time":"Deze vertrouwelijke informatie wordt automatisch gewist indien niet bekeken voor:","text-hint-burned":"\u003cstrong\u003eOpgelet:\u003c/strong\u003e Je ziet deze informatie alleen nu. Je kan het niet meer opnieuw opvragen als je de pagina verlaat.","text-max-filesize":"Maximum grootte: {maxSize}","text-max-filesize-exceeded":"De bestanden die je toevoegde zijn te groot: {curSize} / {maxSize}","text-powered-by":"Mogelijk gemaakt door","text-pre-reveal-hint":"Gebruik deze knop om de vertrouwelijke info op te halen. Let op: Je kan dit slechts eenmaal doen!","text-pre-url":"Je vertrouwelijke informatie kan opgevraagd worden via deze URL:","text-secret-create-disabled":"Het aanmaken van nieuwe geheimen is in dit geval uitgeschakeld.","title-explanation":"Dit is hoe het werkt\u0026hellip;","title-new-secret":"Nieuwe vertrouwelijke info opslaan","title-reading-secret":"Vertrouwelijke info lezen\u0026hellip;","title-secret-create-disabled":"Geheime creatie uitgeschakeld...","title-secret-created":"Vertrouwelijke info opgeslaan!"}'),
|
||||||
|
|
||||||
'nl-BE': JSON.parse('{"alert-secret-not-found":"De gegevens die je zocht bestaan niet (meer)\u0026hellip; - Als je hier informatie verwachtte dan is de link mogelijk al door iemand anders bekeken!","alert-something-went-wrong":"Er ging iets verkeerd, sorry\u0026hellip;","btn-create-secret":"Nieuwe vertrouwelijke info aanmaken!","btn-create-secret-processing":"Vertrouwelijke info wordt versleuteld...","btn-new-secret":"Nieuw","btn-reveal-secret":"Toon mij de vertrouwelijke info!","btn-reveal-secret-processing":"Vertrouwelijke info wordt ontsleuteld...","btn-show-explanation":"Hoe werkt dit?","expire-default":"Standaard termijn","expire-n-days":"{n} dag | {n} dagen","expire-n-hours":"{n} uur | {n} uur","expire-n-minutes":"{n} minuut | {n} minuten","expire-n-seconds":"{n} seconde | {n} seconden","items-explanation":["Je vult vertrouwelijke informatie in op deze pagina.","Je browser versleutelt de ingevulde tekst via een automatisch gegenereerd wachtwoord.","Alleen de versleutelde data wordt naar de server gestuurd. (De leesbare versie of het wachtwoord worden nooit verstuurd!)","De server slaat de versleutelde data gedurende een beperkte periode op.","Je geeft de URL met identificatie en het gegenereerde wachtwoord aan de ontvanger.","De ontvanger kan de vertrouwelijke informatie exact eenmaal bekijken: indien het niet lukt heeft mogelijk iemand anders de info gezien!","De versleutelde data wordt van de server gewist van zodra de ontvanger het bekeken heeft."],"label-expiry":"Verwijder na:","label-secret-data":"Vertrouwelijke info:","label-secret-files":"Bestanden toevoegen:","text-attached-files":"De afzender heeft bestanden toegevoegd. Deze werden niet gecontroleerd, gebruik deze enkel als je de afzender vertrouwt!","text-burn-hint":"Bezoek de URL niet zelf: je kan deze slechts eenmaal gebruiken. Geef de URL aan de ontvanger.","text-burn-time":"Deze vertrouwelijke informatie wordt automatisch gewist indien niet bekeken voor:","text-hint-burned":"\u003cstrong\u003eOpgelet:\u003c/strong\u003e Je ziet deze informatie alleen nu. Je kan het niet meer opnieuw opvragen als je de pagina verlaat.","text-max-filesize":"Maximum grootte: {maxSize}","text-max-filesize-exceeded":"De bestanden die je toevoegde zijn te groot: {curSize} / {maxSize}","text-powered-by":"Mogelijk gemaakt door","text-pre-reveal-hint":"Gebruik deze knop om de vertrouwelijke info op te halen. Let op: Je kan dit slechts eenmaal doen!","text-pre-url":"Je vertrouwelijke informatie kan opgevraagd worden via deze URL:","text-secret-create-disabled":"Het aanmaken van nieuwe geheimen is in dit geval uitgeschakeld.","title-explanation":"Dit is hoe het werkt\u0026hellip;","title-new-secret":"Nieuwe vertrouwelijke info opslaan","title-reading-secret":"Vertrouwelijke info lezen\u0026hellip;","title-secret-create-disabled":"Geheime creatie uitgeschakeld...","title-secret-created":"Vertrouwelijke info opgeslaan!"}'),
|
'pl': JSON.parse('{"alert-secret-not-found":"To nie jest sekret, którego szukasz\u0026hellip; - Jeśli spodziewałeś się tu sekretu, to może być on zagrożony, ponieważ ktoś inny mógł już otworzyć ten link.","alert-something-went-wrong":"Coś poszło nie tak. Bardzo mi przykro\u0026hellip;","btn-create-secret":"Stwórz sekret!","btn-create-secret-processing":"Sekret jest tworzony...","btn-new-secret":"Nowy sekret","btn-reveal-secret":"Pokaż mi sekret!","btn-reveal-secret-processing":"Sekret jest odszyfrowywany...","btn-show-explanation":"Jak to działa?","expire-default":"Domyślne wygasanie","expire-n-days":"{n} dzień | {n} dni","expire-n-hours":"{n} godzina | {n} godzin(y)","expire-n-minutes":"{n} minuta | {n} minut(y)","expire-n-seconds":"{n} sekunda | {n} sekund(y)","items-explanation":["Wpisujesz sekret w pole na tej stronie","Twoja przeglądarka szyfruje sekret korzystając z wygenerowanego hasła","Tylko zaszyfrowany sekret jest wysyłany na serwer (ani czysty sekret, ani hasło nie są nigdy wysyłane!)","Serwer przechowuje zaszyfrowany sekret przez określony czas","Przekazujesz wyświetlony adres URL zawierający identyfikator sekretu oraz hasło deszyfrujące do odbiorcy","Odbiorca może zobaczyć sekret tylko raz: jeżeli nie może tego zrobić, to sekret mógł być już wyświetlony przez kogoś innego!","Po jednorazowym odczytaniu zaszyfrowanego sekretu, jest on usuwany z serwera"],"label-expiry":"Wygasa po:","label-secret-data":"Dane sekretu:","label-secret-files":"Dołącz pliki:","text-attached-files":"Nadawca dołączył pliki do tego sekretu. Upewnij się, że ufasz nadawcy, bo pliki nie są sprawdzane!","text-burn-hint":"Pamiętaj, aby nie przechodzić na ten adres URL samemu, ponieważ to zniszczy sekret. Po prostu przekaż go odbiorcy!","text-burn-time":"Jeżeli nie zostanie wyświetlony, ten sekret zostanie automatycznie usunięty:","text-hint-burned":"\u003cstrong\u003eUwaga:\u003c/strong\u003e Zobaczysz to tylko raz. Gdy odświeżysz stronę, to sekret nie będzie już dostępny, więc lepiej skopiuj go teraz\u0026hellip;","text-invalid-files-selected":"Co najmniej jeden z załączonych plików nie jest dopuszczalny jako załącznik.","text-max-filesize":"Maksymalny rozmiar: {maxSize}","text-max-filesize-exceeded":"Wybrane załączniki przekraczają maksymalny rozmiar: {curSize} / {maxSize}","text-powered-by":"Obsługiwane przez","text-pre-reveal-hint":"Aby odsłonić sekret, naciśnij ten przycisk, jednak wiedz, że to zniszczy sekret. Możesz go zobaczyć tylko raz!","text-pre-url":"Twój sekret został stworzony i zachowany pod tym adresem URL:","text-secret-create-disabled":"Tworzenie nowych sekretów jest wyłączone na tej instancji.","title-explanation":"Oto, jak to działa\u0026hellip;","title-new-secret":"Stwórz nowy sekret","title-reading-secret":"Odczytywanie Twojego sekretu\u0026hellip;","title-secret-create-disabled":"Tworzenie sekretów wyłączone\u0026hellip;","title-secret-created":"Sekret utworzony!","tooltip-copy-to-clipboard":"Skopiuj do schowka","tooltip-download-as-file":"Pobierz jako plik"}'),
|
||||||
|
|
||||||
'pl': JSON.parse('{"alert-insecure-environment":"Odwiedzasz tę instancję przez niezabezpieczone połączenie. Nie będziesz mógł tworzyć ani odczytywać sekretów.","alert-secret-not-found":"To nie jest sekret, którego szukasz\u0026hellip; - Jeśli spodziewałeś się tu sekretu, to może być on zagrożony, ponieważ ktoś inny mógł już otworzyć ten link.","alert-something-went-wrong":"Coś poszło nie tak. Bardzo mi przykro\u0026hellip;","btn-create-secret":"Stwórz sekret!","btn-create-secret-processing":"Sekret jest tworzony...","btn-new-secret":"Nowy sekret","btn-reveal-secret":"Pokaż mi sekret!","btn-reveal-secret-processing":"Sekret jest odszyfrowywany...","btn-show-explanation":"Jak to działa?","btn-theme-switcher-auto":"Auto","expire-default":"Domyślne wygasanie","expire-n-days":"{n} dzień | {n} dni","expire-n-hours":"{n} godzina | {n} godzin(y)","expire-n-minutes":"{n} minuta | {n} minut(y)","expire-n-seconds":"{n} sekunda | {n} sekund(y)","items-explanation":["Wpisujesz sekret w pole na tej stronie","Twoja przeglądarka szyfruje sekret korzystając z wygenerowanego hasła","Tylko zaszyfrowany sekret jest wysyłany na serwer (ani czysty sekret, ani hasło nie są nigdy wysyłane!)","Serwer przechowuje zaszyfrowany sekret przez określony czas","Przekazujesz wyświetlony adres URL zawierający identyfikator sekretu oraz hasło deszyfrujące do odbiorcy","Odbiorca może zobaczyć sekret tylko raz: jeżeli nie może tego zrobić, to sekret mógł być już wyświetlony przez kogoś innego!","Po jednorazowym odczytaniu zaszyfrowanego sekretu, jest on usuwany z serwera"],"label-expiry":"Wygasa po:","label-secret-data":"Dane sekretu:","label-secret-files":"Dołącz pliki:","text-attached-files":"Nadawca dołączył pliki do tego sekretu. Upewnij się, że ufasz nadawcy, bo pliki nie są sprawdzane!","text-burn-hint":"Pamiętaj, aby nie przechodzić na ten adres URL samemu, ponieważ to zniszczy sekret. Po prostu przekaż go odbiorcy!","text-burn-time":"Jeżeli nie zostanie wyświetlony, ten sekret zostanie automatycznie usunięty:","text-hint-burned":"\u003cstrong\u003eUwaga:\u003c/strong\u003e Zobaczysz to tylko raz. Gdy odświeżysz stronę, to sekret nie będzie już dostępny, więc lepiej skopiuj go teraz\u0026hellip;","text-invalid-files-selected":"Co najmniej jeden z załączonych plików nie jest dopuszczalny jako załącznik.","text-max-filesize":"Maksymalny rozmiar: {maxSize}","text-max-filesize-exceeded":"Wybrane załączniki przekraczają maksymalny rozmiar: {curSize} / {maxSize}","text-powered-by":"Obsługiwane przez","text-pre-reveal-hint":"Aby odsłonić sekret, naciśnij ten przycisk, jednak wiedz, że to zniszczy sekret. Możesz go zobaczyć tylko raz!","text-pre-url":"Twój sekret został stworzony i zachowany pod tym adresem URL:","text-secret-burned":"Sekret został pomyślnie zniszczony.","text-secret-create-disabled":"Tworzenie nowych sekretów jest wyłączone na tej instancji.","title-explanation":"Oto, jak to działa\u0026hellip;","title-new-secret":"Stwórz nowy sekret","title-reading-secret":"Odczytywanie Twojego sekretu\u0026hellip;","title-secret-create-disabled":"Tworzenie sekretów wyłączone\u0026hellip;","title-secret-created":"Sekret utworzony!","tooltip-burn-secret":"Zniszcz sekret teraz!","tooltip-copy-to-clipboard":"Skopiuj do schowka","tooltip-download-as-file":"Pobierz jako plik"}'),
|
|
||||||
|
|
||||||
'pt-BR': JSON.parse('{"alert-secret-not-found":"Esta não é o segredo que você está procurando… - Se você esperava que o segredo estaria aqui, ele pode ter sido comprometido por alguém que já acessou o link.","alert-something-went-wrong":"Desculpe, algo deu errado…","btn-create-secret":"Criar segredo!","btn-new-secret":"Novo segredo","btn-reveal-secret":"Mostrar o segredo!","btn-show-explanation":"Como funciona?","expire-n-days":"{n} dia | {n} dias","expire-n-hours":"{n} hora | {n} horas","expire-n-minutes":"{n} minutos | {n} minutos","expire-n-seconds":"{n} segundos | {n} segundos","items-explanation":["Você insere o segredo no campo de texto desta página","Seu navegador criptografa o segredo usando uma senha gerada","Somente o segredo criptografado é enviado para o servidor (nem o segredo em texto claro, nem a senha é enviada para o servidor!)","O servidor armazena o segredo criptografado por um certo tempo","Você envia a URL mostrada contendo a ID e a senha de descriptografia para o destinatário","O destinatário pode ver o segredo apenas uma vez: se ele não pode ver, o segredo pode ter sido visto por outra pessoa!","Após o segredo ter sido obtido uma vez, o mesmo é deletado do servidor"],"label-secret-data":"Informação secreta:","text-burn-hint":"Importante você lembrar de não acessar esta URL, pois isto irá indisponibilizar o segredo. Apenas encaminhe para outra pessoa!","text-hint-burned":"Atenção: Você está vendo esta informação apenas uma vez. Logo que você recarregar a página o segredo ficará indisponível. É recomendado que você copie a informação agora…","text-powered-by":"Powered by","text-pre-reveal-hint":"Para revelar o segredo clique neste botão, mas lembre-se que esta ação vai destruir o segredo. Você só pode ver uma única vez!","text-pre-url":"Seu segredo foi criado e armazenado na seguinte URL:","text-secret-create-disabled":"A criação de novos segredos é desativada nesse caso.","title-explanation":"É assim como funciona…","title-new-secret":"Criar um novo segredo","title-reading-secret":"Lendo seu segredo…","title-secret-create-disabled":"Criação secreta desativada...","title-secret-created":"Segredo criado!"}'),
|
'pt-BR': JSON.parse('{"alert-secret-not-found":"Esta não é o segredo que você está procurando… - Se você esperava que o segredo estaria aqui, ele pode ter sido comprometido por alguém que já acessou o link.","alert-something-went-wrong":"Desculpe, algo deu errado…","btn-create-secret":"Criar segredo!","btn-new-secret":"Novo segredo","btn-reveal-secret":"Mostrar o segredo!","btn-show-explanation":"Como funciona?","expire-n-days":"{n} dia | {n} dias","expire-n-hours":"{n} hora | {n} horas","expire-n-minutes":"{n} minutos | {n} minutos","expire-n-seconds":"{n} segundos | {n} segundos","items-explanation":["Você insere o segredo no campo de texto desta página","Seu navegador criptografa o segredo usando uma senha gerada","Somente o segredo criptografado é enviado para o servidor (nem o segredo em texto claro, nem a senha é enviada para o servidor!)","O servidor armazena o segredo criptografado por um certo tempo","Você envia a URL mostrada contendo a ID e a senha de descriptografia para o destinatário","O destinatário pode ver o segredo apenas uma vez: se ele não pode ver, o segredo pode ter sido visto por outra pessoa!","Após o segredo ter sido obtido uma vez, o mesmo é deletado do servidor"],"label-secret-data":"Informação secreta:","text-burn-hint":"Importante você lembrar de não acessar esta URL, pois isto irá indisponibilizar o segredo. Apenas encaminhe para outra pessoa!","text-hint-burned":"Atenção: Você está vendo esta informação apenas uma vez. Logo que você recarregar a página o segredo ficará indisponível. É recomendado que você copie a informação agora…","text-powered-by":"Powered by","text-pre-reveal-hint":"Para revelar o segredo clique neste botão, mas lembre-se que esta ação vai destruir o segredo. Você só pode ver uma única vez!","text-pre-url":"Seu segredo foi criado e armazenado na seguinte URL:","text-secret-create-disabled":"A criação de novos segredos é desativada nesse caso.","title-explanation":"É assim como funciona…","title-new-secret":"Criar um novo segredo","title-reading-secret":"Lendo seu segredo…","title-secret-create-disabled":"Criação secreta desativada...","title-secret-created":"Segredo criado!"}'),
|
||||||
|
|
||||||
'ru': JSON.parse('{"alert-secret-not-found":"Секрет недоступен\u0026hellip; - Помните, он может быть скомпрометирован. Возможно кто-то другой уже открыл вашу ссылку.","alert-something-went-wrong":"Что-то пошло не так. Приносим свои извинения\u0026hellip;","btn-create-secret":"Создать секрет!","btn-create-secret-processing":"Секрет создаётся ...","btn-new-secret":"Новый секрет","btn-reveal-secret":"Показать секрет!","btn-reveal-secret-processing":"Секрет декодируется ...","btn-show-explanation":"Как это работает?","expire-default":"Значение по умолчанию до исчезновения","expire-n-days":"{n} день | {n} дней","expire-n-hours":"{n} час | {n} часов","expire-n-minutes":"{n} минут | {n} минут","expire-n-seconds":"{n} секунда | {n} секунд","items-explanation":["Вы вводите секрет в поле на этой странице.","Ваш браузер шифрует секрет с помощью сгенерированного пароля.","На сервер отправляется только зашифрованный секрет (ни текст секрета, ни пароль никогда не отправляются!)","Сервер хранит зашифрованный секрет в течение определенного времени.","Вы передаете отображаемый URL-адрес, содержащий идентификатор и пароль для расшифровки, получателю.","Получатель может просмотреть секрет ровно один раз: если он не смог, секрет возможно был просмотрен кем-то другим!","После того как зашифрованный секрет был извлечен, он удаляется с сервера."],"label-expiry":"Исчезнет через:","label-secret-data":"Секретные данные:","label-secret-files":"Прикреплённые файлы:","text-attached-files":"Отправитель прикрепил файлы к секрету. Убедитесь, что вы доверяете отправителю, так как файлы не были проверены!","text-burn-hint":"Пожалуйста, не переходите по этому URL для проверки, так как это удалит секрет. Просто скопируйте и передайте его!","text-burn-time":"Если этот секрет не был просмотрен ранее, он будет автоматически удален:","text-hint-burned":"\u003cstrong\u003eВнимание:\u003c/strong\u003e Секрет будет показан только один раз. Как только вы перезагрузите страницу, секрет исчезнет, скопируйте его незамедлительно\u0026hellip;","text-max-filesize":"Максимальный размер: {maxSize}","text-max-filesize-exceeded":"Ваш файл(ы) слишком большой для прикрепления к секрету: {curSize} / {maxSize}","text-powered-by":"Основан","text-pre-reveal-hint":"Чтобы раскрыть секрет, нажмите эту кнопку, но имейте в виду, что это приведет к уничтожению секрета. Вы можете просмотреть его только один раз!","text-pre-url":"Ваш секрет создан и сохранён, его URL:","text-secret-create-disabled":"Создание новых секретов в этом случае отключено.","title-explanation":"Как это работает\u0026hellip;","title-new-secret":"Создать новый секрет","title-reading-secret":"Читаем ваш секрет\u0026hellip;","title-secret-create-disabled":"Секретное создание отключено...","title-secret-created":"Секрет создан!"}'),
|
'ru': JSON.parse('{"alert-secret-not-found":"Секрет недоступен\u0026hellip; - Помните, он может быть скомпрометирован. Возможно кто-то другой уже открыл вашу ссылку.","alert-something-went-wrong":"Что-то пошло не так. Приносим свои извинения\u0026hellip;","btn-create-secret":"Создать секрет!","btn-create-secret-processing":"Секрет создаётся ...","btn-new-secret":"Новый секрет","btn-reveal-secret":"Показать секрет!","btn-reveal-secret-processing":"Секрет декодируется ...","btn-show-explanation":"Как это работает?","expire-default":"Значение по умолчанию до исчезновения","expire-n-days":"{n} день | {n} дней","expire-n-hours":"{n} час | {n} часов","expire-n-minutes":"{n} минут | {n} минут","expire-n-seconds":"{n} секунда | {n} секунд","items-explanation":["Вы вводите секрет в поле на этой странице.","Ваш браузер шифрует секрет с помощью сгенерированного пароля.","На сервер отправляется только зашифрованный секрет (ни текст секрета, ни пароль никогда не отправляются!)","Сервер хранит зашифрованный секрет в течение определенного времени.","Вы передаете отображаемый URL-адрес, содержащий идентификатор и пароль для расшифровки, получателю.","Получатель может просмотреть секрет ровно один раз: если он не смог, секрет возможно был просмотрен кем-то другим!","После того как зашифрованный секрет был извлечен, он удаляется с сервера."],"label-expiry":"Исчезнет через:","label-secret-data":"Секретные данные:","label-secret-files":"Прикреплённые файлы:","text-attached-files":"Отправитель прикрепил файлы к секрету. Убедитесь, что вы доверяете отправителю, так как файлы не были проверены!","text-burn-hint":"Пожалуйста, не переходите по этому URL для проверки, так как это удалит секрет. Просто скопируйте и передайте его!","text-burn-time":"Если этот секрет не был просмотрен ранее, он будет автоматически удален:","text-hint-burned":"\u003cstrong\u003eВнимание:\u003c/strong\u003e Секрет будет показан только один раз. Как только вы перезагрузите страницу, секрет исчезнет, скопируйте его незамедлительно\u0026hellip;","text-max-filesize":"Максимальный размер: {maxSize}","text-max-filesize-exceeded":"Ваш файл(ы) слишком большой для прикрепления к секрету: {curSize} / {maxSize}","text-powered-by":"Основан","text-pre-reveal-hint":"Чтобы раскрыть секрет, нажмите эту кнопку, но имейте в виду, что это приведет к уничтожению секрета. Вы можете просмотреть его только один раз!","text-pre-url":"Ваш секрет создан и сохранён, его URL:","text-secret-create-disabled":"Создание новых секретов в этом случае отключено.","title-explanation":"Как это работает\u0026hellip;","title-new-secret":"Создать новый секрет","title-reading-secret":"Читаем ваш секрет\u0026hellip;","title-secret-create-disabled":"Секретное создание отключено...","title-secret-created":"Секрет создан!"}'),
|
||||||
|
|
||||||
'sv': JSON.parse('{"alert-secret-not-found":"Hemlighet hittades inte\u0026hellip; - Om du förväntade dig att hemligheten skulle finnas här kan den vara röjd då någon annan kan ha öppnat denna länk tidigare.","alert-something-went-wrong":"Något gick fel. Jag ber om ursäkt för detta!\u0026hellip;","btn-create-secret":"Skapa hemlighet!","btn-create-secret-processing":"Hemlighet håller på att skapas..","btn-new-secret":"Ny hemlighet.","btn-reveal-secret":"Visa mig hemligheten!","btn-reveal-secret-processing":"Hemlighet håller på att dekrypteras..","btn-show-explanation":"Hur fungerar detta?","expire-default":"Standard utgångstid","expire-n-days":"{n} dag | {n} dagar","expire-n-hours":"{n} timme | {n} timmar","expire-n-minutes":"{n} minut | {n} minuter","expire-n-seconds":"{n} sekund | {n} sekunder","items-explanation":["Skriv in en hemlighet i rutan nedan","Din webbläsare krypterar hemligheten med hjälp av ett genererat lösenord","Endast den krypterade hemligheten skickas till servern. (varken lösenordet eller hemligheten i klartext skickas!)","Servern lagrar den krypterade hemligheten för en begränsad tid","Du skickar URL-länken med ID-numret och avkrypteringslösenordet till mottagaren","Mottagaren kan se hemligheten exakt en gång: Om detta misslyckas kan hemligheten redan ha setts av någon annan!","När hemligheten har setts en gång, raderas den från servern"],"label-expiry":"Expirerar om:","label-secret-data":"Hemlig data:","label-secret-files":"Bifoga filer:","text-attached-files":"Avsändaren har bifogat filer till denna hemlighet. Se till att du litar på avsändaren, eftersom filerna inte har kontrollerats!","text-burn-hint":"Kom ihåg att inte gå till denna URL själv eftersom detta skulle förbruka hemligheten. Skicka bara vidare den till mottagaren!","text-burn-time":"Om hemligheten inte visas innan, kommer den att förbrukas automatiskt:","text-hint-burned":"\u003cstrong\u003eObservera:\u003c/strong\u003e Du kan endast se denna sida en gång. Så fort du laddar om sidan kommer hemligheten att försvinna så kopiera den nu\u0026hellip;","text-max-filesize":"Maximal storlek: {maxSize}","text-max-filesize-exceeded":"Filerna du valt är för stora för att kunna bifogas: {curSize} / {maxSize}","text-powered-by":"Drivs av","text-pre-reveal-hint":"För att visa hemligheten klicka på denna knapp. Var medveten om att när du gör det kommer hemligheten att förbrukas, du kan endast se den en gång!","text-pre-url":"Din hemlighet har skapats och lagrats med denna URL:","text-secret-create-disabled":"Skapandet av nya hemligheter blockeras i detta fall.","title-explanation":"Såhär fungerar det\u0026hellip;","title-new-secret":"Skapa ny hemlighet","title-reading-secret":"Läs din hemlighet\u0026hellip;","title-secret-create-disabled":"Skapande av hemlighet avaktiverat...","title-secret-created":"Hemlighet skapad!"}'),
|
'sv': JSON.parse('{"alert-secret-not-found":"Hemlighet hittades inte\u0026hellip; - Om du förväntade dig att hemligheten skulle finnas här kan den vara röjd då någon annan kan ha öppnat denna länk tidigare.","alert-something-went-wrong":"Något gick fel. Jag ber om ursäkt för detta!\u0026hellip;","btn-create-secret":"Skapa hemliget!","btn-create-secret-processing":"Hemlighet håller på att skapas..","btn-new-secret":"Ny hemlighet.","btn-reveal-secret":"Visa mig hemligheten!","btn-reveal-secret-processing":"Hemlighet håller på att dekrypteras..","btn-show-explanation":"Hur fungerar detta?","expire-default":"Standard utgångstid","expire-n-days":"{n} dag | {n} dagar","expire-n-hours":"{n} timme | {n} timmar","expire-n-minutes":"{n} minut | {n} minuter","expire-n-seconds":"{n} sekund | {n} sekunder","items-explanation":["Skriv in en hemlighet i rutan nedan","Din webbläsare krypterar hemligheten med hjälp av ett genererat lösenord","Endast den krypterade hemligheten skickas till servern. (varken lösenordet eller hemligheten i klartext skickas!)","Servern lagrar den krypterade hemligheten för en begränsad tid","Du skickar URL-länken med ID-numret och avkrypteringslösenordet till mottagaren","Mottagaren kan se hemligheten exakt en gång: Om detta misslyckas kan hemligheten redan ha setts av någon annan!","När hemligheten har setts en gång, raderas den från servern"],"label-expiry":"Expirerar om:","label-secret-data":"Hemlig data:","label-secret-files":"Bifoga filer:","text-attached-files":"Avsändaren har bifogat filer till denna hemlighet. Se till att du litar på avsändaren, eftersom filerna inte har kontrollerats!","text-burn-hint":"Kom ihåg att inte gå till denna URL själv eftersom detta skulle förbruka hemligheten. Skicka bara vidare den till mottagaren!","text-burn-time":"Om hemligheten inte visas innan, kommer den att förbrukas automatiskt:","text-hint-burned":"\u003cstrong\u003eObservera:\u003c/strong\u003e Du kan endast se denna sida en gång. Så fort du laddar om sidan kommer hemligheten att försvinna så kopiera den nu\u0026hellip;","text-max-filesize":"Maximal storlek: {maxSize}","text-max-filesize-exceeded":"Filerna du valt är för stora för att kunna bifogas: {curSize} / {maxSize}","text-powered-by":"Drivs av","text-pre-reveal-hint":"För att visa hemligheten klicka på denna knapp. Var medveten om att när du gör det kommer hemligheten att förbrukas, du kan endast se den en gång!","text-pre-url":"Din hemlighet har skapats och lagrats med denna URL:","text-secret-create-disabled":"Skapandet av nya hemligheter blockeras i detta fall.","title-explanation":"Såhär fungerar det\u0026hellip;","title-new-secret":"Skapa ny hemlighet","title-reading-secret":"Läs din hemlighet\u0026hellip;","title-secret-create-disabled":"Hemlig skapelse avaktiverad...","title-secret-created":"Hemlighet skapad!"}'),
|
||||||
|
|
||||||
'tr': JSON.parse('{"alert-secret-not-found":"Aradığınız sır bu değil… - Sırrın burada olmasını bekliyorsanız, bu link başkası tarafından açılmış ve sırrınız tehlikede olabilir.","alert-something-went-wrong":"Bir şeyler ters gitti. Bunun için çok üzgünüm…","btn-create-secret":"Sır oluştur!","btn-new-secret":"Yeni sır","btn-reveal-secret":"Sırrı göster!","btn-show-explanation":"Nasıl çalışır?","expire-n-days":"{n} gün | {n} gün","expire-n-hours":"{n} saat | {n} saat","expire-n-minutes":"{n} dakika | {n} dakika","expire-n-seconds":"{n} saniye | {n} saniye","items-explanation":["Bu sayfadaki alana sırrınızı giriniz","Internet tarayıcınız oluşturulan şifre yardımı ile sırrınızı enkripte eder","Sadece ektripte edilmiş sır sunucuya gönderilir (ne sır metni nede şifre gönderilmez!)","Sunucu enkripte edilmiş sırrı bünyesinde belli bir süre saklar","Gösterilen linki, id ve deşifre bilgisi ile birlikte alıcıya gönder","Alcı sırrı tam olarak sadece bir kez görebilir: Eğer göremez ise, sır bir başkası tarafından daha önce görülmüş olabilir!","Sır bir kez gösterildikten sonra hemen sunucudan silinir"],"label-secret-data":"Sır bilgisi:","text-burn-hint":"Lütfen linki kendiniz acmayın, bu sırrın silinmesine neden olur. Linki sadece alıcıya gönderin!","text-hint-burned":"Dikkat: Bunu sadece bir kez göreceksiniz. Sayfayı güncellediğinizde yada kapattiğınızda sır kaybolacaktır, belkide şimdi sırrı kopyalamanız akıllıca olacaktır…","text-powered-by":"Tarafından desteklenmektedir","text-pre-reveal-hint":"Sırrı görmek için bu düğmeye tıklayın, ama bunu yaptıktan sonra sırrın silineceğini unutmayın. Bunu sadece bir kez görebilirsin!","text-pre-url":"Sırrınız oluşturuldu ve bu link kullanılarak kaydedildi:","text-secret-create-disabled":"Bu durumda yeni gizli dizilerin oluşturulması devre dışı bırakılır.","title-explanation":"Bu sekilde çalışır…","title-new-secret":"Yeni sır oluştur","title-reading-secret":"Sırrınız okunuyor…","title-secret-create-disabled":"Gizli yaratım devre dışı bırakıldı...","title-secret-created":"Sır oluşturuldu!"}'),
|
'tr': JSON.parse('{"alert-secret-not-found":"Aradığınız sır bu değil… - Sırrın burada olmasını bekliyorsanız, bu link başkası tarafından açılmış ve sırrınız tehlikede olabilir.","alert-something-went-wrong":"Bir şeyler ters gitti. Bunun için çok üzgünüm…","btn-create-secret":"Sır oluştur!","btn-new-secret":"Yeni sır","btn-reveal-secret":"Sırrı göster!","btn-show-explanation":"Nasıl çalışır?","expire-n-days":"{n} gün | {n} gün","expire-n-hours":"{n} saat | {n} saat","expire-n-minutes":"{n} dakika | {n} dakika","expire-n-seconds":"{n} saniye | {n} saniye","items-explanation":["Bu sayfadaki alana sırrınızı giriniz","Internet tarayıcınız oluşturulan şifre yardımı ile sırrınızı enkripte eder","Sadece ektripte edilmiş sır sunucuya gönderilir (ne sır metni nede şifre gönderilmez!)","Sunucu enkripte edilmiş sırrı bünyesinde belli bir süre saklar","Gösterilen linki, id ve deşifre bilgisi ile birlikte alıcıya gönder","Alcı sırrı tam olarak sadece bir kez görebilir: Eğer göremez ise, sır bir başkası tarafından daha önce görülmüş olabilir!","Sır bir kez gösterildikten sonra hemen sunucudan silinir"],"label-secret-data":"Sır bilgisi:","text-burn-hint":"Lütfen linki kendiniz acmayın, bu sırrın silinmesine neden olur. Linki sadece alıcıya gönderin!","text-hint-burned":"Dikkat: Bunu sadece bir kez göreceksiniz. Sayfayı güncellediğinizde yada kapattiğınızda sır kaybolacaktır, belkide şimdi sırrı kopyalamanız akıllıca olacaktır…","text-powered-by":"Tarafından desteklenmektedir","text-pre-reveal-hint":"Sırrı görmek için bu düğmeye tıklayın, ama bunu yaptıktan sonra sırrın silineceğini unutmayın. Bunu sadece bir kez görebilirsin!","text-pre-url":"Sırrınız oluşturuldu ve bu link kullanılarak kaydedildi:","text-secret-create-disabled":"Bu durumda yeni gizli dizilerin oluşturulması devre dışı bırakılır.","title-explanation":"Bu sekilde çalışır…","title-new-secret":"Yeni sır oluştur","title-reading-secret":"Sırrınız okunuyor…","title-secret-create-disabled":"Gizli yaratım devre dışı bırakıldı...","title-secret-created":"Sır oluşturuldu!"}'),
|
||||||
|
|
||||||
|
|
21
src/lato.scss
Normal file
21
src/lato.scss
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
/* lato-regular - latin-ext_latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Lato';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url('latofont/lato-v20-latin-ext_latin-regular.woff2') format('woff2'); /* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||||
|
}
|
||||||
|
/* lato-italic - latin-ext_latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Lato';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url('latofont/lato-v20-latin-ext_latin-italic.woff2') format('woff2'); /* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||||
|
}
|
||||||
|
/* lato-700 - latin-ext_latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Lato';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
src: url('latofont/lato-v20-latin-ext_latin-700.woff2') format('woff2'); /* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||||
|
}
|
94
src/latofont/OFL.txt
Executable file
94
src/latofont/OFL.txt
Executable file
|
@ -0,0 +1,94 @@
|
||||||
|
Copyright (c) 2010-2015, Łukasz Dziedzic (dziedzic@typoland.com),
|
||||||
|
with Reserved Font Name Lato.
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
BIN
src/latofont/lato-v20-latin-ext_latin-700.woff2
Normal file
BIN
src/latofont/lato-v20-latin-ext_latin-700.woff2
Normal file
Binary file not shown.
BIN
src/latofont/lato-v20-latin-ext_latin-italic.woff2
Normal file
BIN
src/latofont/lato-v20-latin-ext_latin-italic.woff2
Normal file
Binary file not shown.
BIN
src/latofont/lato-v20-latin-ext_latin-regular.woff2
Normal file
BIN
src/latofont/lato-v20-latin-ext_latin-regular.woff2
Normal file
Binary file not shown.
74
src/main.js
Normal file
74
src/main.js
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
/* eslint-disable sort-imports */
|
||||||
|
/* global version */
|
||||||
|
|
||||||
|
import Vue from 'vue'
|
||||||
|
import VueI18n from 'vue-i18n'
|
||||||
|
import VueRouter from 'vue-router'
|
||||||
|
|
||||||
|
import './style.scss'
|
||||||
|
|
||||||
|
import app from './app.vue'
|
||||||
|
import messages from './langs/langs.js'
|
||||||
|
import router from './router'
|
||||||
|
|
||||||
|
Vue.use(VueI18n)
|
||||||
|
Vue.use(VueRouter)
|
||||||
|
|
||||||
|
const cookieSet = Object.fromEntries(document.cookie.split('; ')
|
||||||
|
.map(el => el.split('=')
|
||||||
|
.map(el => decodeURIComponent(el))))
|
||||||
|
|
||||||
|
const i18n = new VueI18n({
|
||||||
|
fallbackLocale: 'en',
|
||||||
|
locale: cookieSet.lang || navigator?.language || 'en',
|
||||||
|
messages,
|
||||||
|
})
|
||||||
|
|
||||||
|
Vue.mixin({
|
||||||
|
beforeRouteLeave(_to, _from, next) {
|
||||||
|
// Before leaving the component, reset the errors the component displayed
|
||||||
|
this.$emit('error', null)
|
||||||
|
next()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
new Vue({
|
||||||
|
components: { app },
|
||||||
|
|
||||||
|
data: {
|
||||||
|
customize: {},
|
||||||
|
darkTheme: false,
|
||||||
|
version,
|
||||||
|
},
|
||||||
|
|
||||||
|
el: '#app',
|
||||||
|
i18n,
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
navigate(to) {
|
||||||
|
this.$router.replace(to)
|
||||||
|
.catch(err => {
|
||||||
|
if (VueRouter.isNavigationFailure(err, VueRouter.NavigationFailureType.duplicated)) {
|
||||||
|
// Hide duplicate nav errors
|
||||||
|
return
|
||||||
|
}
|
||||||
|
throw err
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.customize = window.OTSCustomize
|
||||||
|
this.darkTheme = window.getTheme() === 'dark'
|
||||||
|
},
|
||||||
|
|
||||||
|
name: 'OTS',
|
||||||
|
render: createElement => createElement('app'),
|
||||||
|
router,
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
darkTheme(to) {
|
||||||
|
window.setTheme(to ? 'dark' : 'light')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
18
src/main.ts
18
src/main.ts
|
@ -1,18 +0,0 @@
|
||||||
import { createApp, h } from 'vue'
|
|
||||||
|
|
||||||
import './style.scss'
|
|
||||||
import '@fortawesome/fontawesome-free/css/all.css' // All FA free icons
|
|
||||||
import appView from './app.vue'
|
|
||||||
|
|
||||||
import i18n from './i18n.ts'
|
|
||||||
import router from './router.ts'
|
|
||||||
|
|
||||||
const app = createApp({
|
|
||||||
name: 'OTS',
|
|
||||||
render() { return h(appView) },
|
|
||||||
})
|
|
||||||
|
|
||||||
app.use(i18n)
|
|
||||||
app.use(router)
|
|
||||||
|
|
||||||
app.mount('#app')
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { createMemoryHistory, createRouter } from 'vue-router'
|
|
||||||
|
|
||||||
import AppCreate from './components/create.vue'
|
import AppCreate from './components/create.vue'
|
||||||
import AppDisplayURL from './components/display-url.vue'
|
import AppDisplayURL from './components/display-url.vue'
|
||||||
import AppExplanation from './components/explanation.vue'
|
import AppExplanation from './components/explanation.vue'
|
||||||
import AppSecretDisplay from './components/secret-display.vue'
|
import AppSecretDisplay from './components/secret-display.vue'
|
||||||
|
|
||||||
|
import VueRouter from 'vue-router'
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
component: AppCreate,
|
component: AppCreate,
|
||||||
|
@ -37,8 +37,8 @@ const routes = [
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const router = createRouter({
|
const router = new VueRouter({
|
||||||
history: createMemoryHistory(),
|
mode: 'abstract',
|
||||||
routes,
|
routes,
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
// Force local fonts
|
// Force local fonts
|
||||||
$web-font-path: '';
|
$web-font-path: '';
|
||||||
|
|
||||||
@use "../node_modules/bootstrap/dist/css/bootstrap.css";
|
@import "../node_modules/bootstrap/dist/css/bootstrap.css";
|
||||||
|
@import "lato";
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
textarea {
|
textarea {
|
||||||
font-family: monospace !important;
|
font-family: monospace !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cursor-pointer {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue