diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..75fe935 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,97 @@ +name: release + +on: + push: + # Sequence of patterns matched against refs/tags + tags: + - 'v*' +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + github_release: + name: Create Release + runs-on: ubuntu-latest + steps: + - + name: Checkout code + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - + name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: 1.17 + - + name: Cache Go modules + uses: actions/cache@v1 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + - + name: Tests + run: | + cd ./src/etcdkeeper + go mod tidy + go test -v ./... + - + name: Run GoReleaser + uses: goreleaser/goreleaser-action@v2 + if: success() && startsWith(github.ref, 'refs/tags/') + with: + version: latest + args: release --rm-dist + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # Duplication with docker.yml workflow cause the previous automatic release don't trigger a release created event + docker: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - + name: Cache Docker layers + uses: actions/cache@v2 + id: cache + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + - + name: Docker meta + id: meta + uses: docker/metadata-action@v3 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + - + name: Login to docker registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - + name: Build and Push docker image + id: docker_build + uses: docker/build-push-action@v2.7.0 + with: + push: true + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: "type=local,dest=/tmp/.buildx-cache" + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + - + name: Clear + if: always() + run: | + rm -f ${HOME}/.docker/config.json diff --git a/.gitignore b/.gitignore index 0060276..a52a788 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,5 @@ bin # Package Files *.war -*.ear \ No newline at end of file +*.ear +dist/ diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 0000000..232aba3 --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,36 @@ +project_name: etcdkeeper + +builds: + - env: + - CGO_ENABLED=0 + goos: + - linux + - windows + - darwin + goarch: + - amd64 + - arm64 + ignore: + - goos: windows + goarch: arm64 + dir: ./src/etcdkeeper +archives: + - replacements: + 386: i386 + amd64: x86_64 + format: zip + name_template: "{{ .ProjectName }}-v{{ .Version }}-{{ .Os }}_{{ .Arch }}" + wrap_in_directory: true + files: + - LICENSE + - README.md +checksum: + name_template: 'checksums.txt' +snapshot: + name_template: "{{ incpatch .Version }}-snapshot" +changelog: + sort: asc + filters: + exclude: + - '^docs:' + - '^test:' diff --git a/Dockerfile b/Dockerfile index 2e48cd3..875aad5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,30 +1,30 @@ -FROM golang:1.12 as build +FROM golang:1.17-alpine3.14 as build -ENV GO111MODULE on -ENV GOPROXY "https://goproxy.io" +WORKDIR /app +ADD . /app +WORKDIR /app/src/etcdkeeper -WORKDIR /opt -RUN mkdir etcdkeeper -ADD . /opt/etcdkeeper -WORKDIR /opt/etcdkeeper/src/etcdkeeper +ENV CGO_ENABLED=0 -RUN go mod download \ - && go build -o etcdkeeper.bin main.go +RUN go mod download +RUN go build -o ../../etcdkeeper -ldflags='-w -s' -a -tags netgo -installsuffix netgo main.go - -FROM alpine:3.10 +FROM alpine:3.14.1 ENV HOST="0.0.0.0" ENV PORT="8080" -# RUN apk add --no-cache ca-certificates +RUN apk add --no-cache ca-certificates -RUN mkdir /lib64 && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2 +# Create a user 'etcdkeeper' member of group 'etcdkeeper' +RUN addgroup -S etcdkeeper && \ + adduser -S -D -h /etcdkeeper -G etcdkeeper etcdkeeper WORKDIR /opt/etcdkeeper -COPY --from=build /opt/etcdkeeper/src/etcdkeeper/etcdkeeper.bin . -ADD assets assets +COPY --from=build --chown=etcdkeeper:etcdkeeper /app/etcdkeeper . EXPOSE ${PORT} +USER etcdkeeper -ENTRYPOINT ./etcdkeeper.bin -h $HOST -p $PORT \ No newline at end of file +ENTRYPOINT ["./etcdkeeper"] +CMD "-h $HOST -p $PORT" diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d0e34ab --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +.PHONY: all + +all: + docker image build -t etcdkeeper:1.0.0-snapshot . + +lint: + cd src/etcdkeeper && golangci-lint run --new-from-rev=HEAD~ + +dev: + cd src/etcdkeeper && go run main.go -h localhost \ No newline at end of file diff --git a/src/etcdkeeper/.golangci.yml b/src/etcdkeeper/.golangci.yml new file mode 100644 index 0000000..d981b06 --- /dev/null +++ b/src/etcdkeeper/.golangci.yml @@ -0,0 +1,721 @@ +# This file contains all available configuration options +# with their default values. + +# options for analysis running +run: + # default concurrency is a available CPU number + concurrency: 4 + + # timeout for analysis, e.g. 30s, 5m, default is 1m + timeout: 1m + + # exit code when at least one issue was found, default is 1 + issues-exit-code: 1 + + # include test files or not, default is true + tests: true + + # list of build tags, all linters use it. Default is empty list. + build-tags: + - mytag + + # which dirs to skip: issues from them won't be reported; + # can use regexp here: generated.*, regexp is applied on full path; + # default value is empty list, but default dirs are skipped independently + # from this option's value (see skip-dirs-use-default). + # "/" will be replaced by current OS file path separator to properly work + # on Windows. + skip-dirs: + - src/external_libs + - autogenerated_by_my_lib + + # default is true. Enables skipping of directories: + # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ + skip-dirs-use-default: true + + # which files to skip: they will be analyzed, but issues from them + # won't be reported. Default value is empty list, but there is + # no need to include all autogenerated files, we confidently recognize + # autogenerated files. If it's not please let us know. + # "/" will be replaced by current OS file path separator to properly work + # on Windows. + skip-files: + - ".*\\.my\\.go$" + - lib/bad.go + + # by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules": + # If invoked with -mod=readonly, the go command is disallowed from the implicit + # automatic updating of go.mod described above. Instead, it fails when any changes + # to go.mod are needed. This setting is most useful to check that go.mod does + # not need updates, such as in a continuous integration and testing system. + # If invoked with -mod=vendor, the go command assumes that the vendor + # directory holds the correct copies of dependencies and ignores + # the dependency descriptions in go.mod. + # modules-download-mode: mod + + # Allow multiple parallel golangci-lint instances running. + # If false (default) - golangci-lint acquires file lock on start. + allow-parallel-runners: false + + +# output configuration options +output: + # colored-line-number|line-number|json|tab|checkstyle|code-climate|junit-xml|github-actions + # default is "colored-line-number" + format: colored-line-number + + # print lines of code with issue, default is true + print-issued-lines: true + + # print linter name in the end of issue text, default is true + print-linter-name: true + + # make issues output unique by line, default is true + uniq-by-line: true + + # add a prefix to the output file references; default is no prefix + path-prefix: "" + + # sorts results by: filepath, line and column + sort-results: false + + +# all available settings of specific linters +linters-settings: + + cyclop: + # the maximal code complexity to report + max-complexity: 20 + # the maximal average package complexity. If it's higher than 0.0 (float) the check is enabled (default 0.0) + package-average: 0.0 + # should ignore tests (default false) + skip-tests: false + + dogsled: + # checks assignments with too many blank identifiers; default is 2 + max-blank-identifiers: 2 + + dupl: + # tokens count to trigger issue, 150 by default + threshold: 200 + + errcheck: + # report about not checking of errors in type assertions: `a := b.(MyStruct)`; + # default is false: such cases aren't reported by default. + check-type-assertions: false + + # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; + # default is false: such cases aren't reported by default. + check-blank: false + + # [deprecated] comma-separated list of pairs of the form pkg:regex + # the regex is used to ignore names within pkg. (default "fmt:.*"). + # see https://github.com/kisielk/errcheck#the-deprecated-method for details + ignore: fmt:.*,io/ioutil:^Read.* + + # path to a file containing a list of functions to exclude from checking + # see https://github.com/kisielk/errcheck#excluding-functions for details + + + errorlint: + # Check whether fmt.Errorf uses the %w verb for formatting errors. See the readme for caveats + errorf: true + # Check for plain type assertions and type switches + asserts: true + # Check for plain error comparisons + comparison: true + + exhaustive: + # check switch statements in generated files also + check-generated: false + # indicates that switch statements are to be considered exhaustive if a + # 'default' case is present, even if all enum members aren't listed in the + # switch + default-signifies-exhaustive: false + + # exhaustivestruct: + # # Struct Patterns is list of expressions to match struct packages and names + # # The struct packages have the form example.com/package.ExampleStruct + # # The matching patterns can use matching syntax from https://pkg.go.dev/path#Match + # # If this list is empty, all structs are tested. + # struct-patterns: + # - '*.Test' + # - 'example.com/package.ExampleStruct' + + forbidigo: + # Forbid the following identifiers (identifiers are written using regexp): + forbid: + - ^print.*$ + - 'fmt\.Print.*' + # Exclude godoc examples from forbidigo checks. Default is true. + exclude_godoc_examples: false + + funlen: + lines: 200 + statements: 100 + + gocognit: + # minimal code complexity to report, 30 by default (but we recommend 10-20) + min-complexity: 30 + + nestif: + # minimal complexity of if statements to report, 5 by default + min-complexity: 5 + + goconst: + # minimal length of string constant, 3 by default + min-len: 3 + # minimal occurrences count to trigger, 3 by default + min-occurrences: 3 + + gocritic: + # Which checks should be enabled; can't be combined with 'disabled-checks'; + # See https://go-critic.github.io/overview#checks-overview + # To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run` + # By default list of stable checks is used. + # enabled-checks: + # - rangeValCopy + + # Which checks should be disabled; can't be combined with 'enabled-checks'; default is empty + disabled-checks: + - regexpMust + + # Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks. + # Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags". + enabled-tags: + - performance + disabled-tags: + - experimental + + # Settings passed to gocritic. + # The settings key is the name of a supported gocritic checker. + # The list of supported checkers can be find in https://go-critic.github.io/overview. + settings: + captLocal: # must be valid enabled check name + # whether to restrict checker to params only (default true) + paramsOnly: true + elseif: + # whether to skip balanced if-else pairs (default true) + skipBalanced: true + hugeParam: + # size in bytes that makes the warning trigger (default 80) + sizeThreshold: 80 + # nestingReduce: + # # min number of statements inside a branch to trigger a warning (default 5) + # bodyWidth: 5 + rangeExprCopy: + # size in bytes that makes the warning trigger (default 512) + sizeThreshold: 512 + # whether to check test functions (default true) + skipTestFuncs: true + # rangeValCopy: + # # size in bytes that makes the warning trigger (default 128) + # sizeThreshold: 32 + # # whether to check test functions (default true) + # skipTestFuncs: true + + # truncateCmp: + # # whether to skip int/uint/uintptr types (default true) + # skipArchDependent: true + underef: + # whether to skip (*x).method() calls where x is a pointer receiver (default true) + skipRecvDeref: true + # unnamedResult: + # # whether to check exported functions + # checkExported: true + + gocyclo: + # minimal code complexity to report, 30 by default (but we recommend 10-20) + min-complexity: 20 + + godox: + # report any comments starting with keywords, this is useful for TODO or FIXME comments that + # might be left in the code accidentally and should be resolved before merging + keywords: # default keywords are TODO, BUG, and FIXME, these can be overwritten by this setting + - NOTE + - OPTIMIZE # marks code that should be optimized before merging + - HACK # marks hack-arounds that should be removed before merging + + gofmt: + # simplify code: gofmt with `-s` option, true by default + simplify: true + + gofumpt: + # Choose whether or not to use the extra rules that are disabled + # by default + extra-rules: false + + golint: + # minimal confidence for issues, default is 0.8 + min-confidence: 0.8 + + gomnd: + settings: + mnd: + # the list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description. + checks: + - argument + - case + - condition + - operation + - return + - assign + # ignored-numbers: 1000 + # ignored-files: magic_.*.go + # ignored-functions: math.* + + gomoddirectives: + # Allow local `replace` directives. Default is false. + replace-local: false + # List of allowed `replace` directives. Default is empty. + replace-allow-list: + - launchpad.net/gocheck + # Allow to not explain why the version has been retracted in the `retract` directives. Default is false. + retract-allow-no-explanation: false + # Forbid the use of the `exclude` directives. Default is false. + exclude-forbidden: false + + gosec: + # To select a subset of rules to run. + # Available rules: https://github.com/securego/gosec#available-rules + includes: + - G306 + - G101 + - G109 + # To specify a set of rules to explicitly exclude. + # Available rules: https://github.com/securego/gosec#available-rules + excludes: + - G401 + - G204 + # To specify the configuration of rules. + # The configuration of rules is not fully documented by gosec: + # https://github.com/securego/gosec#configuration + # https://github.com/securego/gosec/blob/569328eade2ccbad4ce2d0f21ee158ab5356a5cf/rules/rulelist.go#L60-L102 + config: + G306: "0600" + G101: + pattern: "(?i)example" + ignore_entropy: false + entropy_threshold: "80.0" + per_char_threshold: "3.0" + truncate: "32" + + gosimple: + # Select the Go version to target. The default is '1.13'. + go: "1.16" + # https://staticcheck.io/docs/options#checks + checks: [ "all" ] + + govet: + # report about shadowed variables + check-shadowing: false + + # settings per analyzer + settings: + printf: # analyzer name, run `go tool vet help` to see all analyzers + funcs: # run `go tool vet help printf` to see available settings for `printf` analyzer + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf + + # enable or disable analyzers by name + # run `go tool vet help` to see all analyzers + enable: + - atomicalign + enable-all: false + disable: + - shadow + disable-all: false + + depguard: + list-type: blacklist + include-go-root: false + packages: + - github.com/sirupsen/logrus + packages-with-error-message: + # specify an error message to output when a blacklisted package is used + - github.com/sirupsen/logrus: "logging is allowed only by logutils.Log" + + ifshort: + # Maximum length of variable declaration measured in number of lines, after which linter won't suggest using short syntax. + # Has higher priority than max-decl-chars. + max-decl-lines: 2 + # Maximum length of variable declaration measured in number of characters, after which linter won't suggest using short syntax. + max-decl-chars: 30 + + + lll: + # max line length, lines longer will be reported. Default is 120. + # '\t' is counted as 1 character by default, and can be changed with the tab-width option + line-length: 150 + # tab width in spaces. Default to 1. + tab-width: 1 + + makezero: + # Allow only slices initialized with a length of zero. Default is false. + always: false + + maligned: + # print struct with more effective memory layout or not, false by default + suggest-new: true + + misspell: + # Correct spellings using locale preferences for US or UK. + # Default is to use a neutral variety of English. + # Setting locale to US will correct the British spelling of 'colour' to 'color'. + locale: US + ignore-words: + - someword + + nakedret: + # make an issue if func has more lines of code than this setting and it has naked returns; default is 30 + max-func-lines: 30 + + prealloc: + # XXX: we don't recommend using this linter before doing performance profiling. + # For most programs usage of prealloc will be a premature optimization. + + # Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them. + # True by default. + simple: true + range-loops: true # Report preallocation suggestions on range loops, true by default + for-loops: false # Report preallocation suggestions on for loops, false by default + + promlinter: + # Promlinter cannot infer all metrics name in static analysis. + # Enable strict mode will also include the errors caused by failing to parse the args. + strict: false + # Please refer to https://github.com/yeya24/promlinter#usage for detailed usage. + disabled-linters: + # - "Help" + # - "MetricUnits" + # - "Counter" + # - "HistogramSummaryReserved" + # - "MetricTypeInName" + # - "ReservedChars" + # - "CamelCase" + # - "lintUnitAbbreviations" + + nolintlint: + # Enable to ensure that nolint directives are all used. Default is true. + allow-unused: false + # Disable to ensure that nolint directives don't have a leading space. Default is true. + allow-leading-space: true + # Exclude following linters from requiring an explanation. Default is []. + allow-no-explanation: [] + # Enable to require an explanation of nonzero length after each nolint directive. Default is false. + require-explanation: true + # Enable to require nolint directives to mention the specific linter being suppressed. Default is false. + require-specific: true + + rowserrcheck: + packages: + - github.com/jmoiron/sqlx + + revive: + # see https://github.com/mgechev/revive#available-rules for details. + ignore-generated-header: true + severity: warning + rules: + - name: indent-error-flow + severity: warning + # - name: add-constant + # severity: warning + # arguments: + # - maxLitCount: "3" + # allowStrs: '""' + # allowInts: "0,1,2" + # allowFloats: "0.0,0.,1.0,1.,2.0,2." + - name: unused-parameter + severity: warning + - name: unreachable-code + severity: warning + - name: atomic + severity: error + - name: unexported-naming + severity: warning + - name: unconditional-recursion + severity: error + - name: early-return + severity: warning + - name: duplicated-imports + severity: warning + - name: range-val-address + severity: warning + - name: waitgroup-by-value + severity: warning + - name: range-val-in-closure + severity: warning + + staticcheck: + # Select the Go version to target. The default is '1.13'. + go: "1.16" + # https://staticcheck.io/docs/options#checks + checks: [ "all" ] + + stylecheck: + # Select the Go version to target. The default is '1.13'. + go: "1.17" + # https://staticcheck.io/docs/options#checks + checks: [ "all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022" ] + # https://staticcheck.io/docs/options#dot_import_whitelist + dot-import-whitelist: + - fmt + # https://staticcheck.io/docs/options#initialisms + initialisms: [ "ACL", "API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "QPS", "RAM", "RPC", "SLA", "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL", "UDP", "UI", "GID", "UID", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XMPP", "XSRF", "XSS" ] + # https://staticcheck.io/docs/options#http_status_code_whitelist + http-status-code-whitelist: [ "200", "400", "404", "500" ] + + tagliatelle: + # check the struck tag name case + case: + # use the struct field name to check the name of the struct tag + use-field-name: true + rules: + # any struct tag type can be used. + # support string case: `camel`, `pascal`, `kebab`, `snake`, `goCamel`, `goPascal`, `goKebab`, `goSnake`, `upper`, `lower` + json: snake + yaml: camel + xml: camel + bson: camel + avro: snake + mapstructure: kebab + + testpackage: + # regexp pattern to skip files + skip-regexp: (export|internal)_test\.go + + thelper: + # The following configurations enable all checks. It can be omitted because all checks are enabled by default. + # You can enable only required checks deleting unnecessary checks. + test: + first: true + name: true + begin: true + benchmark: + first: true + name: true + begin: true + tb: + first: true + name: true + begin: true + + unparam: + # Inspect exported functions, default is false. Set to true if no external program/library imports your code. + # XXX: if you enable this setting, unparam will report a lot of false-positives in text editors: + # if it's called for subdir of a project it can't find external interfaces. All text editor integrations + # with golangci-lint call it on a directory with the changed file. + check-exported: false + + unused: + # Select the Go version to target. The default is '1.13'. + go: "1.16" + + whitespace: + multi-if: false # Enforces newlines (or comments) after every multi-line if statement + multi-func: false # Enforces newlines (or comments) after every multi-line function signature + + wrapcheck: + # An array of strings that specify substrings of signatures to ignore. + # If this set, it will override the default set of ignored signatures. + # See https://github.com/tomarrell/wrapcheck#configuration for more information. + ignoreSigs: + - .Errorf( + - errors.New( + - errors.Unwrap( + - .Wrap( + - .Wrapf( + - .WithMessage( + wsl: + # See https://github.com/bombsimon/wsl/blob/master/doc/configuration.md for + # documentation of available settings. These are the defaults for + # `golangci-lint`. + allow-assign-and-anything: true + allow-assign-and-call: true + allow-cuddle-declarations: true + allow-multiline-assign: true + allow-separated-leading-comment: true + allow-trailing-comment: true + force-case-trailing-whitespace: 0 + force-err-cuddling: false + force-short-decl-cuddling: false + strict-append: true + +linters: + enable: + - megacheck + - govet + - asciicheck + - bodyclose + - durationcheck + - errcheck + - errorlint + - exhaustive + - exportloopref + - gosec + - gosimple + - ineffassign + - makezero + - nilerr + # - noctx + - rowserrcheck + - sqlclosecheck + - staticcheck + - typecheck + - unparam + - unused + - varcheck + - cyclop + - dogsled + - dupl + - forbidigo + - forcetypeassert + - funlen + - gocognit + - goconst + - gocritic + - gocyclo + - godox + - gofmt + - goimports + - gomoddirectives + - gomodguard + - ifshort + - lll + - misspell + - nestif + - predeclared + - revive + # - tagliatelle + - testpackage + - tparallel + - unconvert + - wastedassign + - whitespace + disable: + - noctx + - maligned + - prealloc + - gochecknoglobals + - gochecknoinits + - gofumpt + - goheader + - gomnd + - goprintffuncname + - importas + - nolintlint + - paralleltest + - promlinter + - stylecheck + - thelper + - wrapcheck + - scopelint + - nakedret # 对命名返回值的写法会判断错误 + - wsl + - nlreturn + - gci # 与gofmt冲突 + disable-all: false + presets: + - bugs + - unused + fast: false + + +issues: + # List of regexps of issue texts to exclude, empty list by default. + # But independently from this option we use default exclude patterns, + # it can be disabled by `exclude-use-default: false`. To list all + # excluded by default patterns execute `golangci-lint run --help` + exclude: + - abcdef + + # Excluding configuration per-path, per-linter, per-text and per-source + exclude-rules: + # Exclude some linters from running on tests files. + - path: _test\.go + linters: + - gocyclo + - errcheck + - dupl + - gosec + + # Exclude known linters from partially hard-vendored code, + # which is impossible to exclude via "nolint" comments. + - path: internal/hmac/ + text: "weak cryptographic primitive" + linters: + - gosec + + # Exclude some staticcheck messages + - linters: + - staticcheck + text: "SA9003:" + + # Exclude lll issues for long lines with go:generate + - linters: + - lll + source: "^//go:generate " + + # Independently from option `exclude` we use default exclude patterns, + # it can be disabled by this option. To list all + # excluded by default patterns execute `golangci-lint run --help`. + # Default value for this option is true. + exclude-use-default: false + + # The default value is false. If set to true exclude and exclude-rules + # regular expressions become case sensitive. + exclude-case-sensitive: false + + # The list of ids of default excludes to include or disable. By default it's empty. + include: + - EXC0002 # disable excluding of issues about comments from golint + + # Maximum issues count per one linter. Set to 0 to disable. Default is 50. + max-issues-per-linter: 0 + + # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. + max-same-issues: 0 + + # Show only new issues: if there are unstaged changes or untracked files, + # only those changes are analyzed, else only changes in HEAD~ are analyzed. + # It's a super-useful option for integration of golangci-lint into existing + # large codebase. It's not practical to fix all existing issues at the moment + # of integration: much better don't allow issues in new code. + # Default is false. + new: false + + # Show only new issues created after git revision `REV` + # new-from-rev: REV + + # Show only new issues created in git patch with set file path. + # new-from-patch: path/to/patch/file + + # Fix found issues (if it's supported by the linter) + fix: false + +severity: + # Default value is empty string. + # Set the default severity for issues. If severity rules are defined and the issues + # do not match or no severity is provided to the rule this will be the default + # severity applied. Severities should match the supported severity names of the + # selected out format. + # - Code climate: https://docs.codeclimate.com/docs/issues#issue-severity + # - Checkstyle: https://checkstyle.sourceforge.io/property_types.html#severity + # - Github: https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message + default-severity: error + + # The default value is false. + # If set to true severity-rules regular expressions become case sensitive. + case-sensitive: false + + # Default value is empty list. + # When a list of severity rules are provided, severity information will be added to lint + # issues. Severity rules have the same filtering capability as exclude rules except you + # are allowed to specify one matcher per severity rule. + # Only affects out formats that support setting severity information. + rules: + - linters: + - dupl + severity: info diff --git a/assets/framework/ace/ace.js b/src/etcdkeeper/assets/static/framework/ace/ace.js similarity index 100% rename from assets/framework/ace/ace.js rename to src/etcdkeeper/assets/static/framework/ace/ace.js diff --git a/assets/framework/ace/ext-beautify.js b/src/etcdkeeper/assets/static/framework/ace/ext-beautify.js similarity index 100% rename from assets/framework/ace/ext-beautify.js rename to src/etcdkeeper/assets/static/framework/ace/ext-beautify.js diff --git a/assets/framework/ace/ext-elastic_tabstops_lite.js b/src/etcdkeeper/assets/static/framework/ace/ext-elastic_tabstops_lite.js similarity index 100% rename from assets/framework/ace/ext-elastic_tabstops_lite.js rename to src/etcdkeeper/assets/static/framework/ace/ext-elastic_tabstops_lite.js diff --git a/assets/framework/ace/ext-emmet.js b/src/etcdkeeper/assets/static/framework/ace/ext-emmet.js similarity index 100% rename from assets/framework/ace/ext-emmet.js rename to src/etcdkeeper/assets/static/framework/ace/ext-emmet.js diff --git a/assets/framework/ace/ext-error_marker.js b/src/etcdkeeper/assets/static/framework/ace/ext-error_marker.js similarity index 100% rename from assets/framework/ace/ext-error_marker.js rename to src/etcdkeeper/assets/static/framework/ace/ext-error_marker.js diff --git a/assets/framework/ace/ext-keybinding_menu.js b/src/etcdkeeper/assets/static/framework/ace/ext-keybinding_menu.js similarity index 100% rename from assets/framework/ace/ext-keybinding_menu.js rename to src/etcdkeeper/assets/static/framework/ace/ext-keybinding_menu.js diff --git a/assets/framework/ace/ext-language_tools.js b/src/etcdkeeper/assets/static/framework/ace/ext-language_tools.js similarity index 100% rename from assets/framework/ace/ext-language_tools.js rename to src/etcdkeeper/assets/static/framework/ace/ext-language_tools.js diff --git a/assets/framework/ace/ext-linking.js b/src/etcdkeeper/assets/static/framework/ace/ext-linking.js similarity index 100% rename from assets/framework/ace/ext-linking.js rename to src/etcdkeeper/assets/static/framework/ace/ext-linking.js diff --git a/assets/framework/ace/ext-modelist.js b/src/etcdkeeper/assets/static/framework/ace/ext-modelist.js similarity index 100% rename from assets/framework/ace/ext-modelist.js rename to src/etcdkeeper/assets/static/framework/ace/ext-modelist.js diff --git a/assets/framework/ace/ext-old_ie.js b/src/etcdkeeper/assets/static/framework/ace/ext-old_ie.js similarity index 100% rename from assets/framework/ace/ext-old_ie.js rename to src/etcdkeeper/assets/static/framework/ace/ext-old_ie.js diff --git a/assets/framework/ace/ext-searchbox.js b/src/etcdkeeper/assets/static/framework/ace/ext-searchbox.js similarity index 100% rename from assets/framework/ace/ext-searchbox.js rename to src/etcdkeeper/assets/static/framework/ace/ext-searchbox.js diff --git a/assets/framework/ace/ext-settings_menu.js b/src/etcdkeeper/assets/static/framework/ace/ext-settings_menu.js similarity index 100% rename from assets/framework/ace/ext-settings_menu.js rename to src/etcdkeeper/assets/static/framework/ace/ext-settings_menu.js diff --git a/assets/framework/ace/ext-spellcheck.js b/src/etcdkeeper/assets/static/framework/ace/ext-spellcheck.js similarity index 100% rename from assets/framework/ace/ext-spellcheck.js rename to src/etcdkeeper/assets/static/framework/ace/ext-spellcheck.js diff --git a/assets/framework/ace/ext-split.js b/src/etcdkeeper/assets/static/framework/ace/ext-split.js similarity index 100% rename from assets/framework/ace/ext-split.js rename to src/etcdkeeper/assets/static/framework/ace/ext-split.js diff --git a/assets/framework/ace/ext-static_highlight.js b/src/etcdkeeper/assets/static/framework/ace/ext-static_highlight.js similarity index 100% rename from assets/framework/ace/ext-static_highlight.js rename to src/etcdkeeper/assets/static/framework/ace/ext-static_highlight.js diff --git a/assets/framework/ace/ext-statusbar.js b/src/etcdkeeper/assets/static/framework/ace/ext-statusbar.js similarity index 100% rename from assets/framework/ace/ext-statusbar.js rename to src/etcdkeeper/assets/static/framework/ace/ext-statusbar.js diff --git a/assets/framework/ace/ext-textarea.js b/src/etcdkeeper/assets/static/framework/ace/ext-textarea.js similarity index 100% rename from assets/framework/ace/ext-textarea.js rename to src/etcdkeeper/assets/static/framework/ace/ext-textarea.js diff --git a/assets/framework/ace/ext-themelist.js b/src/etcdkeeper/assets/static/framework/ace/ext-themelist.js similarity index 100% rename from assets/framework/ace/ext-themelist.js rename to src/etcdkeeper/assets/static/framework/ace/ext-themelist.js diff --git a/assets/framework/ace/ext-whitespace.js b/src/etcdkeeper/assets/static/framework/ace/ext-whitespace.js similarity index 100% rename from assets/framework/ace/ext-whitespace.js rename to src/etcdkeeper/assets/static/framework/ace/ext-whitespace.js diff --git a/assets/framework/ace/keybinding-emacs.js b/src/etcdkeeper/assets/static/framework/ace/keybinding-emacs.js similarity index 100% rename from assets/framework/ace/keybinding-emacs.js rename to src/etcdkeeper/assets/static/framework/ace/keybinding-emacs.js diff --git a/assets/framework/ace/keybinding-vim.js b/src/etcdkeeper/assets/static/framework/ace/keybinding-vim.js similarity index 100% rename from assets/framework/ace/keybinding-vim.js rename to src/etcdkeeper/assets/static/framework/ace/keybinding-vim.js diff --git a/assets/framework/ace/mode-abap.js b/src/etcdkeeper/assets/static/framework/ace/mode-abap.js similarity index 100% rename from assets/framework/ace/mode-abap.js rename to src/etcdkeeper/assets/static/framework/ace/mode-abap.js diff --git a/assets/framework/ace/mode-abc.js b/src/etcdkeeper/assets/static/framework/ace/mode-abc.js similarity index 100% rename from assets/framework/ace/mode-abc.js rename to src/etcdkeeper/assets/static/framework/ace/mode-abc.js diff --git a/assets/framework/ace/mode-actionscript.js b/src/etcdkeeper/assets/static/framework/ace/mode-actionscript.js similarity index 100% rename from assets/framework/ace/mode-actionscript.js rename to src/etcdkeeper/assets/static/framework/ace/mode-actionscript.js diff --git a/assets/framework/ace/mode-ada.js b/src/etcdkeeper/assets/static/framework/ace/mode-ada.js similarity index 100% rename from assets/framework/ace/mode-ada.js rename to src/etcdkeeper/assets/static/framework/ace/mode-ada.js diff --git a/assets/framework/ace/mode-apache_conf.js b/src/etcdkeeper/assets/static/framework/ace/mode-apache_conf.js similarity index 100% rename from assets/framework/ace/mode-apache_conf.js rename to src/etcdkeeper/assets/static/framework/ace/mode-apache_conf.js diff --git a/assets/framework/ace/mode-applescript.js b/src/etcdkeeper/assets/static/framework/ace/mode-applescript.js similarity index 100% rename from assets/framework/ace/mode-applescript.js rename to src/etcdkeeper/assets/static/framework/ace/mode-applescript.js diff --git a/assets/framework/ace/mode-asciidoc.js b/src/etcdkeeper/assets/static/framework/ace/mode-asciidoc.js similarity index 100% rename from assets/framework/ace/mode-asciidoc.js rename to src/etcdkeeper/assets/static/framework/ace/mode-asciidoc.js diff --git a/assets/framework/ace/mode-assembly_x86.js b/src/etcdkeeper/assets/static/framework/ace/mode-assembly_x86.js similarity index 100% rename from assets/framework/ace/mode-assembly_x86.js rename to src/etcdkeeper/assets/static/framework/ace/mode-assembly_x86.js diff --git a/assets/framework/ace/mode-autohotkey.js b/src/etcdkeeper/assets/static/framework/ace/mode-autohotkey.js similarity index 100% rename from assets/framework/ace/mode-autohotkey.js rename to src/etcdkeeper/assets/static/framework/ace/mode-autohotkey.js diff --git a/assets/framework/ace/mode-batchfile.js b/src/etcdkeeper/assets/static/framework/ace/mode-batchfile.js similarity index 100% rename from assets/framework/ace/mode-batchfile.js rename to src/etcdkeeper/assets/static/framework/ace/mode-batchfile.js diff --git a/assets/framework/ace/mode-bro.js b/src/etcdkeeper/assets/static/framework/ace/mode-bro.js similarity index 100% rename from assets/framework/ace/mode-bro.js rename to src/etcdkeeper/assets/static/framework/ace/mode-bro.js diff --git a/assets/framework/ace/mode-c9search.js b/src/etcdkeeper/assets/static/framework/ace/mode-c9search.js similarity index 100% rename from assets/framework/ace/mode-c9search.js rename to src/etcdkeeper/assets/static/framework/ace/mode-c9search.js diff --git a/assets/framework/ace/mode-c_cpp.js b/src/etcdkeeper/assets/static/framework/ace/mode-c_cpp.js similarity index 100% rename from assets/framework/ace/mode-c_cpp.js rename to src/etcdkeeper/assets/static/framework/ace/mode-c_cpp.js diff --git a/assets/framework/ace/mode-cirru.js b/src/etcdkeeper/assets/static/framework/ace/mode-cirru.js similarity index 100% rename from assets/framework/ace/mode-cirru.js rename to src/etcdkeeper/assets/static/framework/ace/mode-cirru.js diff --git a/assets/framework/ace/mode-clojure.js b/src/etcdkeeper/assets/static/framework/ace/mode-clojure.js similarity index 100% rename from assets/framework/ace/mode-clojure.js rename to src/etcdkeeper/assets/static/framework/ace/mode-clojure.js diff --git a/assets/framework/ace/mode-cobol.js b/src/etcdkeeper/assets/static/framework/ace/mode-cobol.js similarity index 100% rename from assets/framework/ace/mode-cobol.js rename to src/etcdkeeper/assets/static/framework/ace/mode-cobol.js diff --git a/assets/framework/ace/mode-coffee.js b/src/etcdkeeper/assets/static/framework/ace/mode-coffee.js similarity index 100% rename from assets/framework/ace/mode-coffee.js rename to src/etcdkeeper/assets/static/framework/ace/mode-coffee.js diff --git a/assets/framework/ace/mode-coldfusion.js b/src/etcdkeeper/assets/static/framework/ace/mode-coldfusion.js similarity index 100% rename from assets/framework/ace/mode-coldfusion.js rename to src/etcdkeeper/assets/static/framework/ace/mode-coldfusion.js diff --git a/assets/framework/ace/mode-csharp.js b/src/etcdkeeper/assets/static/framework/ace/mode-csharp.js similarity index 100% rename from assets/framework/ace/mode-csharp.js rename to src/etcdkeeper/assets/static/framework/ace/mode-csharp.js diff --git a/assets/framework/ace/mode-css.js b/src/etcdkeeper/assets/static/framework/ace/mode-css.js similarity index 100% rename from assets/framework/ace/mode-css.js rename to src/etcdkeeper/assets/static/framework/ace/mode-css.js diff --git a/assets/framework/ace/mode-curly.js b/src/etcdkeeper/assets/static/framework/ace/mode-curly.js similarity index 100% rename from assets/framework/ace/mode-curly.js rename to src/etcdkeeper/assets/static/framework/ace/mode-curly.js diff --git a/assets/framework/ace/mode-d.js b/src/etcdkeeper/assets/static/framework/ace/mode-d.js similarity index 100% rename from assets/framework/ace/mode-d.js rename to src/etcdkeeper/assets/static/framework/ace/mode-d.js diff --git a/assets/framework/ace/mode-dart.js b/src/etcdkeeper/assets/static/framework/ace/mode-dart.js similarity index 100% rename from assets/framework/ace/mode-dart.js rename to src/etcdkeeper/assets/static/framework/ace/mode-dart.js diff --git a/assets/framework/ace/mode-diff.js b/src/etcdkeeper/assets/static/framework/ace/mode-diff.js similarity index 100% rename from assets/framework/ace/mode-diff.js rename to src/etcdkeeper/assets/static/framework/ace/mode-diff.js diff --git a/assets/framework/ace/mode-django.js b/src/etcdkeeper/assets/static/framework/ace/mode-django.js similarity index 100% rename from assets/framework/ace/mode-django.js rename to src/etcdkeeper/assets/static/framework/ace/mode-django.js diff --git a/assets/framework/ace/mode-dockerfile.js b/src/etcdkeeper/assets/static/framework/ace/mode-dockerfile.js similarity index 100% rename from assets/framework/ace/mode-dockerfile.js rename to src/etcdkeeper/assets/static/framework/ace/mode-dockerfile.js diff --git a/assets/framework/ace/mode-dot.js b/src/etcdkeeper/assets/static/framework/ace/mode-dot.js similarity index 100% rename from assets/framework/ace/mode-dot.js rename to src/etcdkeeper/assets/static/framework/ace/mode-dot.js diff --git a/assets/framework/ace/mode-drools.js b/src/etcdkeeper/assets/static/framework/ace/mode-drools.js similarity index 100% rename from assets/framework/ace/mode-drools.js rename to src/etcdkeeper/assets/static/framework/ace/mode-drools.js diff --git a/assets/framework/ace/mode-eiffel.js b/src/etcdkeeper/assets/static/framework/ace/mode-eiffel.js similarity index 100% rename from assets/framework/ace/mode-eiffel.js rename to src/etcdkeeper/assets/static/framework/ace/mode-eiffel.js diff --git a/assets/framework/ace/mode-ejs.js b/src/etcdkeeper/assets/static/framework/ace/mode-ejs.js similarity index 100% rename from assets/framework/ace/mode-ejs.js rename to src/etcdkeeper/assets/static/framework/ace/mode-ejs.js diff --git a/assets/framework/ace/mode-elixir.js b/src/etcdkeeper/assets/static/framework/ace/mode-elixir.js similarity index 100% rename from assets/framework/ace/mode-elixir.js rename to src/etcdkeeper/assets/static/framework/ace/mode-elixir.js diff --git a/assets/framework/ace/mode-elm.js b/src/etcdkeeper/assets/static/framework/ace/mode-elm.js similarity index 100% rename from assets/framework/ace/mode-elm.js rename to src/etcdkeeper/assets/static/framework/ace/mode-elm.js diff --git a/assets/framework/ace/mode-erlang.js b/src/etcdkeeper/assets/static/framework/ace/mode-erlang.js similarity index 100% rename from assets/framework/ace/mode-erlang.js rename to src/etcdkeeper/assets/static/framework/ace/mode-erlang.js diff --git a/assets/framework/ace/mode-forth.js b/src/etcdkeeper/assets/static/framework/ace/mode-forth.js similarity index 100% rename from assets/framework/ace/mode-forth.js rename to src/etcdkeeper/assets/static/framework/ace/mode-forth.js diff --git a/assets/framework/ace/mode-fortran.js b/src/etcdkeeper/assets/static/framework/ace/mode-fortran.js similarity index 100% rename from assets/framework/ace/mode-fortran.js rename to src/etcdkeeper/assets/static/framework/ace/mode-fortran.js diff --git a/assets/framework/ace/mode-ftl.js b/src/etcdkeeper/assets/static/framework/ace/mode-ftl.js similarity index 100% rename from assets/framework/ace/mode-ftl.js rename to src/etcdkeeper/assets/static/framework/ace/mode-ftl.js diff --git a/assets/framework/ace/mode-gcode.js b/src/etcdkeeper/assets/static/framework/ace/mode-gcode.js similarity index 100% rename from assets/framework/ace/mode-gcode.js rename to src/etcdkeeper/assets/static/framework/ace/mode-gcode.js diff --git a/assets/framework/ace/mode-gherkin.js b/src/etcdkeeper/assets/static/framework/ace/mode-gherkin.js similarity index 100% rename from assets/framework/ace/mode-gherkin.js rename to src/etcdkeeper/assets/static/framework/ace/mode-gherkin.js diff --git a/assets/framework/ace/mode-gitignore.js b/src/etcdkeeper/assets/static/framework/ace/mode-gitignore.js similarity index 100% rename from assets/framework/ace/mode-gitignore.js rename to src/etcdkeeper/assets/static/framework/ace/mode-gitignore.js diff --git a/assets/framework/ace/mode-glsl.js b/src/etcdkeeper/assets/static/framework/ace/mode-glsl.js similarity index 100% rename from assets/framework/ace/mode-glsl.js rename to src/etcdkeeper/assets/static/framework/ace/mode-glsl.js diff --git a/assets/framework/ace/mode-gobstones.js b/src/etcdkeeper/assets/static/framework/ace/mode-gobstones.js similarity index 100% rename from assets/framework/ace/mode-gobstones.js rename to src/etcdkeeper/assets/static/framework/ace/mode-gobstones.js diff --git a/assets/framework/ace/mode-golang.js b/src/etcdkeeper/assets/static/framework/ace/mode-golang.js similarity index 100% rename from assets/framework/ace/mode-golang.js rename to src/etcdkeeper/assets/static/framework/ace/mode-golang.js diff --git a/assets/framework/ace/mode-groovy.js b/src/etcdkeeper/assets/static/framework/ace/mode-groovy.js similarity index 100% rename from assets/framework/ace/mode-groovy.js rename to src/etcdkeeper/assets/static/framework/ace/mode-groovy.js diff --git a/assets/framework/ace/mode-haml.js b/src/etcdkeeper/assets/static/framework/ace/mode-haml.js similarity index 100% rename from assets/framework/ace/mode-haml.js rename to src/etcdkeeper/assets/static/framework/ace/mode-haml.js diff --git a/assets/framework/ace/mode-handlebars.js b/src/etcdkeeper/assets/static/framework/ace/mode-handlebars.js similarity index 100% rename from assets/framework/ace/mode-handlebars.js rename to src/etcdkeeper/assets/static/framework/ace/mode-handlebars.js diff --git a/assets/framework/ace/mode-haskell.js b/src/etcdkeeper/assets/static/framework/ace/mode-haskell.js similarity index 100% rename from assets/framework/ace/mode-haskell.js rename to src/etcdkeeper/assets/static/framework/ace/mode-haskell.js diff --git a/assets/framework/ace/mode-haskell_cabal.js b/src/etcdkeeper/assets/static/framework/ace/mode-haskell_cabal.js similarity index 100% rename from assets/framework/ace/mode-haskell_cabal.js rename to src/etcdkeeper/assets/static/framework/ace/mode-haskell_cabal.js diff --git a/assets/framework/ace/mode-haxe.js b/src/etcdkeeper/assets/static/framework/ace/mode-haxe.js similarity index 100% rename from assets/framework/ace/mode-haxe.js rename to src/etcdkeeper/assets/static/framework/ace/mode-haxe.js diff --git a/assets/framework/ace/mode-hjson.js b/src/etcdkeeper/assets/static/framework/ace/mode-hjson.js similarity index 100% rename from assets/framework/ace/mode-hjson.js rename to src/etcdkeeper/assets/static/framework/ace/mode-hjson.js diff --git a/assets/framework/ace/mode-html.js b/src/etcdkeeper/assets/static/framework/ace/mode-html.js similarity index 100% rename from assets/framework/ace/mode-html.js rename to src/etcdkeeper/assets/static/framework/ace/mode-html.js diff --git a/assets/framework/ace/mode-html_elixir.js b/src/etcdkeeper/assets/static/framework/ace/mode-html_elixir.js similarity index 100% rename from assets/framework/ace/mode-html_elixir.js rename to src/etcdkeeper/assets/static/framework/ace/mode-html_elixir.js diff --git a/assets/framework/ace/mode-html_ruby.js b/src/etcdkeeper/assets/static/framework/ace/mode-html_ruby.js similarity index 100% rename from assets/framework/ace/mode-html_ruby.js rename to src/etcdkeeper/assets/static/framework/ace/mode-html_ruby.js diff --git a/assets/framework/ace/mode-ini.js b/src/etcdkeeper/assets/static/framework/ace/mode-ini.js similarity index 100% rename from assets/framework/ace/mode-ini.js rename to src/etcdkeeper/assets/static/framework/ace/mode-ini.js diff --git a/assets/framework/ace/mode-io.js b/src/etcdkeeper/assets/static/framework/ace/mode-io.js similarity index 100% rename from assets/framework/ace/mode-io.js rename to src/etcdkeeper/assets/static/framework/ace/mode-io.js diff --git a/assets/framework/ace/mode-jack.js b/src/etcdkeeper/assets/static/framework/ace/mode-jack.js similarity index 100% rename from assets/framework/ace/mode-jack.js rename to src/etcdkeeper/assets/static/framework/ace/mode-jack.js diff --git a/assets/framework/ace/mode-jade.js b/src/etcdkeeper/assets/static/framework/ace/mode-jade.js similarity index 100% rename from assets/framework/ace/mode-jade.js rename to src/etcdkeeper/assets/static/framework/ace/mode-jade.js diff --git a/assets/framework/ace/mode-java.js b/src/etcdkeeper/assets/static/framework/ace/mode-java.js similarity index 100% rename from assets/framework/ace/mode-java.js rename to src/etcdkeeper/assets/static/framework/ace/mode-java.js diff --git a/assets/framework/ace/mode-javascript.js b/src/etcdkeeper/assets/static/framework/ace/mode-javascript.js similarity index 100% rename from assets/framework/ace/mode-javascript.js rename to src/etcdkeeper/assets/static/framework/ace/mode-javascript.js diff --git a/assets/framework/ace/mode-json.js b/src/etcdkeeper/assets/static/framework/ace/mode-json.js similarity index 100% rename from assets/framework/ace/mode-json.js rename to src/etcdkeeper/assets/static/framework/ace/mode-json.js diff --git a/assets/framework/ace/mode-jsoniq.js b/src/etcdkeeper/assets/static/framework/ace/mode-jsoniq.js similarity index 100% rename from assets/framework/ace/mode-jsoniq.js rename to src/etcdkeeper/assets/static/framework/ace/mode-jsoniq.js diff --git a/assets/framework/ace/mode-jsp.js b/src/etcdkeeper/assets/static/framework/ace/mode-jsp.js similarity index 100% rename from assets/framework/ace/mode-jsp.js rename to src/etcdkeeper/assets/static/framework/ace/mode-jsp.js diff --git a/assets/framework/ace/mode-jsx.js b/src/etcdkeeper/assets/static/framework/ace/mode-jsx.js similarity index 100% rename from assets/framework/ace/mode-jsx.js rename to src/etcdkeeper/assets/static/framework/ace/mode-jsx.js diff --git a/assets/framework/ace/mode-julia.js b/src/etcdkeeper/assets/static/framework/ace/mode-julia.js similarity index 100% rename from assets/framework/ace/mode-julia.js rename to src/etcdkeeper/assets/static/framework/ace/mode-julia.js diff --git a/assets/framework/ace/mode-kotlin.js b/src/etcdkeeper/assets/static/framework/ace/mode-kotlin.js similarity index 100% rename from assets/framework/ace/mode-kotlin.js rename to src/etcdkeeper/assets/static/framework/ace/mode-kotlin.js diff --git a/assets/framework/ace/mode-latex.js b/src/etcdkeeper/assets/static/framework/ace/mode-latex.js similarity index 100% rename from assets/framework/ace/mode-latex.js rename to src/etcdkeeper/assets/static/framework/ace/mode-latex.js diff --git a/assets/framework/ace/mode-less.js b/src/etcdkeeper/assets/static/framework/ace/mode-less.js similarity index 100% rename from assets/framework/ace/mode-less.js rename to src/etcdkeeper/assets/static/framework/ace/mode-less.js diff --git a/assets/framework/ace/mode-liquid.js b/src/etcdkeeper/assets/static/framework/ace/mode-liquid.js similarity index 100% rename from assets/framework/ace/mode-liquid.js rename to src/etcdkeeper/assets/static/framework/ace/mode-liquid.js diff --git a/assets/framework/ace/mode-lisp.js b/src/etcdkeeper/assets/static/framework/ace/mode-lisp.js similarity index 100% rename from assets/framework/ace/mode-lisp.js rename to src/etcdkeeper/assets/static/framework/ace/mode-lisp.js diff --git a/assets/framework/ace/mode-livescript.js b/src/etcdkeeper/assets/static/framework/ace/mode-livescript.js similarity index 100% rename from assets/framework/ace/mode-livescript.js rename to src/etcdkeeper/assets/static/framework/ace/mode-livescript.js diff --git a/assets/framework/ace/mode-logiql.js b/src/etcdkeeper/assets/static/framework/ace/mode-logiql.js similarity index 100% rename from assets/framework/ace/mode-logiql.js rename to src/etcdkeeper/assets/static/framework/ace/mode-logiql.js diff --git a/assets/framework/ace/mode-lsl.js b/src/etcdkeeper/assets/static/framework/ace/mode-lsl.js similarity index 100% rename from assets/framework/ace/mode-lsl.js rename to src/etcdkeeper/assets/static/framework/ace/mode-lsl.js diff --git a/assets/framework/ace/mode-lua.js b/src/etcdkeeper/assets/static/framework/ace/mode-lua.js similarity index 100% rename from assets/framework/ace/mode-lua.js rename to src/etcdkeeper/assets/static/framework/ace/mode-lua.js diff --git a/assets/framework/ace/mode-luapage.js b/src/etcdkeeper/assets/static/framework/ace/mode-luapage.js similarity index 100% rename from assets/framework/ace/mode-luapage.js rename to src/etcdkeeper/assets/static/framework/ace/mode-luapage.js diff --git a/assets/framework/ace/mode-lucene.js b/src/etcdkeeper/assets/static/framework/ace/mode-lucene.js similarity index 100% rename from assets/framework/ace/mode-lucene.js rename to src/etcdkeeper/assets/static/framework/ace/mode-lucene.js diff --git a/assets/framework/ace/mode-makefile.js b/src/etcdkeeper/assets/static/framework/ace/mode-makefile.js similarity index 100% rename from assets/framework/ace/mode-makefile.js rename to src/etcdkeeper/assets/static/framework/ace/mode-makefile.js diff --git a/assets/framework/ace/mode-markdown.js b/src/etcdkeeper/assets/static/framework/ace/mode-markdown.js similarity index 100% rename from assets/framework/ace/mode-markdown.js rename to src/etcdkeeper/assets/static/framework/ace/mode-markdown.js diff --git a/assets/framework/ace/mode-mask.js b/src/etcdkeeper/assets/static/framework/ace/mode-mask.js similarity index 100% rename from assets/framework/ace/mode-mask.js rename to src/etcdkeeper/assets/static/framework/ace/mode-mask.js diff --git a/assets/framework/ace/mode-matlab.js b/src/etcdkeeper/assets/static/framework/ace/mode-matlab.js similarity index 100% rename from assets/framework/ace/mode-matlab.js rename to src/etcdkeeper/assets/static/framework/ace/mode-matlab.js diff --git a/assets/framework/ace/mode-maze.js b/src/etcdkeeper/assets/static/framework/ace/mode-maze.js similarity index 100% rename from assets/framework/ace/mode-maze.js rename to src/etcdkeeper/assets/static/framework/ace/mode-maze.js diff --git a/assets/framework/ace/mode-mel.js b/src/etcdkeeper/assets/static/framework/ace/mode-mel.js similarity index 100% rename from assets/framework/ace/mode-mel.js rename to src/etcdkeeper/assets/static/framework/ace/mode-mel.js diff --git a/assets/framework/ace/mode-mushcode.js b/src/etcdkeeper/assets/static/framework/ace/mode-mushcode.js similarity index 100% rename from assets/framework/ace/mode-mushcode.js rename to src/etcdkeeper/assets/static/framework/ace/mode-mushcode.js diff --git a/assets/framework/ace/mode-mysql.js b/src/etcdkeeper/assets/static/framework/ace/mode-mysql.js similarity index 100% rename from assets/framework/ace/mode-mysql.js rename to src/etcdkeeper/assets/static/framework/ace/mode-mysql.js diff --git a/assets/framework/ace/mode-nix.js b/src/etcdkeeper/assets/static/framework/ace/mode-nix.js similarity index 100% rename from assets/framework/ace/mode-nix.js rename to src/etcdkeeper/assets/static/framework/ace/mode-nix.js diff --git a/assets/framework/ace/mode-nsis.js b/src/etcdkeeper/assets/static/framework/ace/mode-nsis.js similarity index 100% rename from assets/framework/ace/mode-nsis.js rename to src/etcdkeeper/assets/static/framework/ace/mode-nsis.js diff --git a/assets/framework/ace/mode-objectivec.js b/src/etcdkeeper/assets/static/framework/ace/mode-objectivec.js similarity index 100% rename from assets/framework/ace/mode-objectivec.js rename to src/etcdkeeper/assets/static/framework/ace/mode-objectivec.js diff --git a/assets/framework/ace/mode-ocaml.js b/src/etcdkeeper/assets/static/framework/ace/mode-ocaml.js similarity index 100% rename from assets/framework/ace/mode-ocaml.js rename to src/etcdkeeper/assets/static/framework/ace/mode-ocaml.js diff --git a/assets/framework/ace/mode-pascal.js b/src/etcdkeeper/assets/static/framework/ace/mode-pascal.js similarity index 100% rename from assets/framework/ace/mode-pascal.js rename to src/etcdkeeper/assets/static/framework/ace/mode-pascal.js diff --git a/assets/framework/ace/mode-perl.js b/src/etcdkeeper/assets/static/framework/ace/mode-perl.js similarity index 100% rename from assets/framework/ace/mode-perl.js rename to src/etcdkeeper/assets/static/framework/ace/mode-perl.js diff --git a/assets/framework/ace/mode-pgsql.js b/src/etcdkeeper/assets/static/framework/ace/mode-pgsql.js similarity index 100% rename from assets/framework/ace/mode-pgsql.js rename to src/etcdkeeper/assets/static/framework/ace/mode-pgsql.js diff --git a/assets/framework/ace/mode-php.js b/src/etcdkeeper/assets/static/framework/ace/mode-php.js similarity index 100% rename from assets/framework/ace/mode-php.js rename to src/etcdkeeper/assets/static/framework/ace/mode-php.js diff --git a/assets/framework/ace/mode-plain_text.js b/src/etcdkeeper/assets/static/framework/ace/mode-plain_text.js similarity index 100% rename from assets/framework/ace/mode-plain_text.js rename to src/etcdkeeper/assets/static/framework/ace/mode-plain_text.js diff --git a/assets/framework/ace/mode-powershell.js b/src/etcdkeeper/assets/static/framework/ace/mode-powershell.js similarity index 100% rename from assets/framework/ace/mode-powershell.js rename to src/etcdkeeper/assets/static/framework/ace/mode-powershell.js diff --git a/assets/framework/ace/mode-praat.js b/src/etcdkeeper/assets/static/framework/ace/mode-praat.js similarity index 100% rename from assets/framework/ace/mode-praat.js rename to src/etcdkeeper/assets/static/framework/ace/mode-praat.js diff --git a/assets/framework/ace/mode-prolog.js b/src/etcdkeeper/assets/static/framework/ace/mode-prolog.js similarity index 100% rename from assets/framework/ace/mode-prolog.js rename to src/etcdkeeper/assets/static/framework/ace/mode-prolog.js diff --git a/assets/framework/ace/mode-properties.js b/src/etcdkeeper/assets/static/framework/ace/mode-properties.js similarity index 100% rename from assets/framework/ace/mode-properties.js rename to src/etcdkeeper/assets/static/framework/ace/mode-properties.js diff --git a/assets/framework/ace/mode-protobuf.js b/src/etcdkeeper/assets/static/framework/ace/mode-protobuf.js similarity index 100% rename from assets/framework/ace/mode-protobuf.js rename to src/etcdkeeper/assets/static/framework/ace/mode-protobuf.js diff --git a/assets/framework/ace/mode-python.js b/src/etcdkeeper/assets/static/framework/ace/mode-python.js similarity index 100% rename from assets/framework/ace/mode-python.js rename to src/etcdkeeper/assets/static/framework/ace/mode-python.js diff --git a/assets/framework/ace/mode-r.js b/src/etcdkeeper/assets/static/framework/ace/mode-r.js similarity index 100% rename from assets/framework/ace/mode-r.js rename to src/etcdkeeper/assets/static/framework/ace/mode-r.js diff --git a/assets/framework/ace/mode-razor.js b/src/etcdkeeper/assets/static/framework/ace/mode-razor.js similarity index 100% rename from assets/framework/ace/mode-razor.js rename to src/etcdkeeper/assets/static/framework/ace/mode-razor.js diff --git a/assets/framework/ace/mode-rdoc.js b/src/etcdkeeper/assets/static/framework/ace/mode-rdoc.js similarity index 100% rename from assets/framework/ace/mode-rdoc.js rename to src/etcdkeeper/assets/static/framework/ace/mode-rdoc.js diff --git a/assets/framework/ace/mode-rhtml.js b/src/etcdkeeper/assets/static/framework/ace/mode-rhtml.js similarity index 100% rename from assets/framework/ace/mode-rhtml.js rename to src/etcdkeeper/assets/static/framework/ace/mode-rhtml.js diff --git a/assets/framework/ace/mode-rst.js b/src/etcdkeeper/assets/static/framework/ace/mode-rst.js similarity index 100% rename from assets/framework/ace/mode-rst.js rename to src/etcdkeeper/assets/static/framework/ace/mode-rst.js diff --git a/assets/framework/ace/mode-ruby.js b/src/etcdkeeper/assets/static/framework/ace/mode-ruby.js similarity index 100% rename from assets/framework/ace/mode-ruby.js rename to src/etcdkeeper/assets/static/framework/ace/mode-ruby.js diff --git a/assets/framework/ace/mode-rust.js b/src/etcdkeeper/assets/static/framework/ace/mode-rust.js similarity index 100% rename from assets/framework/ace/mode-rust.js rename to src/etcdkeeper/assets/static/framework/ace/mode-rust.js diff --git a/assets/framework/ace/mode-sass.js b/src/etcdkeeper/assets/static/framework/ace/mode-sass.js similarity index 100% rename from assets/framework/ace/mode-sass.js rename to src/etcdkeeper/assets/static/framework/ace/mode-sass.js diff --git a/assets/framework/ace/mode-scad.js b/src/etcdkeeper/assets/static/framework/ace/mode-scad.js similarity index 100% rename from assets/framework/ace/mode-scad.js rename to src/etcdkeeper/assets/static/framework/ace/mode-scad.js diff --git a/assets/framework/ace/mode-scala.js b/src/etcdkeeper/assets/static/framework/ace/mode-scala.js similarity index 100% rename from assets/framework/ace/mode-scala.js rename to src/etcdkeeper/assets/static/framework/ace/mode-scala.js diff --git a/assets/framework/ace/mode-scheme.js b/src/etcdkeeper/assets/static/framework/ace/mode-scheme.js similarity index 100% rename from assets/framework/ace/mode-scheme.js rename to src/etcdkeeper/assets/static/framework/ace/mode-scheme.js diff --git a/assets/framework/ace/mode-scss.js b/src/etcdkeeper/assets/static/framework/ace/mode-scss.js similarity index 100% rename from assets/framework/ace/mode-scss.js rename to src/etcdkeeper/assets/static/framework/ace/mode-scss.js diff --git a/assets/framework/ace/mode-sh.js b/src/etcdkeeper/assets/static/framework/ace/mode-sh.js similarity index 100% rename from assets/framework/ace/mode-sh.js rename to src/etcdkeeper/assets/static/framework/ace/mode-sh.js diff --git a/assets/framework/ace/mode-sjs.js b/src/etcdkeeper/assets/static/framework/ace/mode-sjs.js similarity index 100% rename from assets/framework/ace/mode-sjs.js rename to src/etcdkeeper/assets/static/framework/ace/mode-sjs.js diff --git a/assets/framework/ace/mode-smarty.js b/src/etcdkeeper/assets/static/framework/ace/mode-smarty.js similarity index 100% rename from assets/framework/ace/mode-smarty.js rename to src/etcdkeeper/assets/static/framework/ace/mode-smarty.js diff --git a/assets/framework/ace/mode-snippets.js b/src/etcdkeeper/assets/static/framework/ace/mode-snippets.js similarity index 100% rename from assets/framework/ace/mode-snippets.js rename to src/etcdkeeper/assets/static/framework/ace/mode-snippets.js diff --git a/assets/framework/ace/mode-soy_template.js b/src/etcdkeeper/assets/static/framework/ace/mode-soy_template.js similarity index 100% rename from assets/framework/ace/mode-soy_template.js rename to src/etcdkeeper/assets/static/framework/ace/mode-soy_template.js diff --git a/assets/framework/ace/mode-space.js b/src/etcdkeeper/assets/static/framework/ace/mode-space.js similarity index 100% rename from assets/framework/ace/mode-space.js rename to src/etcdkeeper/assets/static/framework/ace/mode-space.js diff --git a/assets/framework/ace/mode-sql.js b/src/etcdkeeper/assets/static/framework/ace/mode-sql.js similarity index 100% rename from assets/framework/ace/mode-sql.js rename to src/etcdkeeper/assets/static/framework/ace/mode-sql.js diff --git a/assets/framework/ace/mode-sqlserver.js b/src/etcdkeeper/assets/static/framework/ace/mode-sqlserver.js similarity index 100% rename from assets/framework/ace/mode-sqlserver.js rename to src/etcdkeeper/assets/static/framework/ace/mode-sqlserver.js diff --git a/assets/framework/ace/mode-stylus.js b/src/etcdkeeper/assets/static/framework/ace/mode-stylus.js similarity index 100% rename from assets/framework/ace/mode-stylus.js rename to src/etcdkeeper/assets/static/framework/ace/mode-stylus.js diff --git a/assets/framework/ace/mode-svg.js b/src/etcdkeeper/assets/static/framework/ace/mode-svg.js similarity index 100% rename from assets/framework/ace/mode-svg.js rename to src/etcdkeeper/assets/static/framework/ace/mode-svg.js diff --git a/assets/framework/ace/mode-swift.js b/src/etcdkeeper/assets/static/framework/ace/mode-swift.js similarity index 100% rename from assets/framework/ace/mode-swift.js rename to src/etcdkeeper/assets/static/framework/ace/mode-swift.js diff --git a/assets/framework/ace/mode-tcl.js b/src/etcdkeeper/assets/static/framework/ace/mode-tcl.js similarity index 100% rename from assets/framework/ace/mode-tcl.js rename to src/etcdkeeper/assets/static/framework/ace/mode-tcl.js diff --git a/assets/framework/ace/mode-tex.js b/src/etcdkeeper/assets/static/framework/ace/mode-tex.js similarity index 100% rename from assets/framework/ace/mode-tex.js rename to src/etcdkeeper/assets/static/framework/ace/mode-tex.js diff --git a/assets/framework/ace/mode-text.js b/src/etcdkeeper/assets/static/framework/ace/mode-text.js similarity index 100% rename from assets/framework/ace/mode-text.js rename to src/etcdkeeper/assets/static/framework/ace/mode-text.js diff --git a/assets/framework/ace/mode-textile.js b/src/etcdkeeper/assets/static/framework/ace/mode-textile.js similarity index 100% rename from assets/framework/ace/mode-textile.js rename to src/etcdkeeper/assets/static/framework/ace/mode-textile.js diff --git a/assets/framework/ace/mode-toml.js b/src/etcdkeeper/assets/static/framework/ace/mode-toml.js similarity index 100% rename from assets/framework/ace/mode-toml.js rename to src/etcdkeeper/assets/static/framework/ace/mode-toml.js diff --git a/assets/framework/ace/mode-tsx.js b/src/etcdkeeper/assets/static/framework/ace/mode-tsx.js similarity index 100% rename from assets/framework/ace/mode-tsx.js rename to src/etcdkeeper/assets/static/framework/ace/mode-tsx.js diff --git a/assets/framework/ace/mode-twig.js b/src/etcdkeeper/assets/static/framework/ace/mode-twig.js similarity index 100% rename from assets/framework/ace/mode-twig.js rename to src/etcdkeeper/assets/static/framework/ace/mode-twig.js diff --git a/assets/framework/ace/mode-typescript.js b/src/etcdkeeper/assets/static/framework/ace/mode-typescript.js similarity index 100% rename from assets/framework/ace/mode-typescript.js rename to src/etcdkeeper/assets/static/framework/ace/mode-typescript.js diff --git a/assets/framework/ace/mode-vala.js b/src/etcdkeeper/assets/static/framework/ace/mode-vala.js similarity index 100% rename from assets/framework/ace/mode-vala.js rename to src/etcdkeeper/assets/static/framework/ace/mode-vala.js diff --git a/assets/framework/ace/mode-vbscript.js b/src/etcdkeeper/assets/static/framework/ace/mode-vbscript.js similarity index 100% rename from assets/framework/ace/mode-vbscript.js rename to src/etcdkeeper/assets/static/framework/ace/mode-vbscript.js diff --git a/assets/framework/ace/mode-velocity.js b/src/etcdkeeper/assets/static/framework/ace/mode-velocity.js similarity index 100% rename from assets/framework/ace/mode-velocity.js rename to src/etcdkeeper/assets/static/framework/ace/mode-velocity.js diff --git a/assets/framework/ace/mode-verilog.js b/src/etcdkeeper/assets/static/framework/ace/mode-verilog.js similarity index 100% rename from assets/framework/ace/mode-verilog.js rename to src/etcdkeeper/assets/static/framework/ace/mode-verilog.js diff --git a/assets/framework/ace/mode-vhdl.js b/src/etcdkeeper/assets/static/framework/ace/mode-vhdl.js similarity index 100% rename from assets/framework/ace/mode-vhdl.js rename to src/etcdkeeper/assets/static/framework/ace/mode-vhdl.js diff --git a/assets/framework/ace/mode-wollok.js b/src/etcdkeeper/assets/static/framework/ace/mode-wollok.js similarity index 100% rename from assets/framework/ace/mode-wollok.js rename to src/etcdkeeper/assets/static/framework/ace/mode-wollok.js diff --git a/assets/framework/ace/mode-xml.js b/src/etcdkeeper/assets/static/framework/ace/mode-xml.js similarity index 100% rename from assets/framework/ace/mode-xml.js rename to src/etcdkeeper/assets/static/framework/ace/mode-xml.js diff --git a/assets/framework/ace/mode-xquery.js b/src/etcdkeeper/assets/static/framework/ace/mode-xquery.js similarity index 100% rename from assets/framework/ace/mode-xquery.js rename to src/etcdkeeper/assets/static/framework/ace/mode-xquery.js diff --git a/assets/framework/ace/mode-yaml.js b/src/etcdkeeper/assets/static/framework/ace/mode-yaml.js similarity index 100% rename from assets/framework/ace/mode-yaml.js rename to src/etcdkeeper/assets/static/framework/ace/mode-yaml.js diff --git a/assets/framework/ace/snippets/abap.js b/src/etcdkeeper/assets/static/framework/ace/snippets/abap.js similarity index 100% rename from assets/framework/ace/snippets/abap.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/abap.js diff --git a/assets/framework/ace/snippets/abc.js b/src/etcdkeeper/assets/static/framework/ace/snippets/abc.js similarity index 100% rename from assets/framework/ace/snippets/abc.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/abc.js diff --git a/assets/framework/ace/snippets/actionscript.js b/src/etcdkeeper/assets/static/framework/ace/snippets/actionscript.js similarity index 100% rename from assets/framework/ace/snippets/actionscript.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/actionscript.js diff --git a/assets/framework/ace/snippets/ada.js b/src/etcdkeeper/assets/static/framework/ace/snippets/ada.js similarity index 100% rename from assets/framework/ace/snippets/ada.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/ada.js diff --git a/assets/framework/ace/snippets/apache_conf.js b/src/etcdkeeper/assets/static/framework/ace/snippets/apache_conf.js similarity index 100% rename from assets/framework/ace/snippets/apache_conf.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/apache_conf.js diff --git a/assets/framework/ace/snippets/applescript.js b/src/etcdkeeper/assets/static/framework/ace/snippets/applescript.js similarity index 100% rename from assets/framework/ace/snippets/applescript.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/applescript.js diff --git a/assets/framework/ace/snippets/asciidoc.js b/src/etcdkeeper/assets/static/framework/ace/snippets/asciidoc.js similarity index 100% rename from assets/framework/ace/snippets/asciidoc.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/asciidoc.js diff --git a/assets/framework/ace/snippets/assembly_x86.js b/src/etcdkeeper/assets/static/framework/ace/snippets/assembly_x86.js similarity index 100% rename from assets/framework/ace/snippets/assembly_x86.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/assembly_x86.js diff --git a/assets/framework/ace/snippets/autohotkey.js b/src/etcdkeeper/assets/static/framework/ace/snippets/autohotkey.js similarity index 100% rename from assets/framework/ace/snippets/autohotkey.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/autohotkey.js diff --git a/assets/framework/ace/snippets/batchfile.js b/src/etcdkeeper/assets/static/framework/ace/snippets/batchfile.js similarity index 100% rename from assets/framework/ace/snippets/batchfile.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/batchfile.js diff --git a/assets/framework/ace/snippets/bro.js b/src/etcdkeeper/assets/static/framework/ace/snippets/bro.js similarity index 100% rename from assets/framework/ace/snippets/bro.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/bro.js diff --git a/assets/framework/ace/snippets/c9search.js b/src/etcdkeeper/assets/static/framework/ace/snippets/c9search.js similarity index 100% rename from assets/framework/ace/snippets/c9search.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/c9search.js diff --git a/assets/framework/ace/snippets/c_cpp.js b/src/etcdkeeper/assets/static/framework/ace/snippets/c_cpp.js similarity index 100% rename from assets/framework/ace/snippets/c_cpp.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/c_cpp.js diff --git a/assets/framework/ace/snippets/cirru.js b/src/etcdkeeper/assets/static/framework/ace/snippets/cirru.js similarity index 100% rename from assets/framework/ace/snippets/cirru.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/cirru.js diff --git a/assets/framework/ace/snippets/clojure.js b/src/etcdkeeper/assets/static/framework/ace/snippets/clojure.js similarity index 100% rename from assets/framework/ace/snippets/clojure.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/clojure.js diff --git a/assets/framework/ace/snippets/cobol.js b/src/etcdkeeper/assets/static/framework/ace/snippets/cobol.js similarity index 100% rename from assets/framework/ace/snippets/cobol.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/cobol.js diff --git a/assets/framework/ace/snippets/coffee.js b/src/etcdkeeper/assets/static/framework/ace/snippets/coffee.js similarity index 100% rename from assets/framework/ace/snippets/coffee.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/coffee.js diff --git a/assets/framework/ace/snippets/coldfusion.js b/src/etcdkeeper/assets/static/framework/ace/snippets/coldfusion.js similarity index 100% rename from assets/framework/ace/snippets/coldfusion.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/coldfusion.js diff --git a/assets/framework/ace/snippets/csharp.js b/src/etcdkeeper/assets/static/framework/ace/snippets/csharp.js similarity index 100% rename from assets/framework/ace/snippets/csharp.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/csharp.js diff --git a/assets/framework/ace/snippets/css.js b/src/etcdkeeper/assets/static/framework/ace/snippets/css.js similarity index 100% rename from assets/framework/ace/snippets/css.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/css.js diff --git a/assets/framework/ace/snippets/curly.js b/src/etcdkeeper/assets/static/framework/ace/snippets/curly.js similarity index 100% rename from assets/framework/ace/snippets/curly.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/curly.js diff --git a/assets/framework/ace/snippets/d.js b/src/etcdkeeper/assets/static/framework/ace/snippets/d.js similarity index 100% rename from assets/framework/ace/snippets/d.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/d.js diff --git a/assets/framework/ace/snippets/dart.js b/src/etcdkeeper/assets/static/framework/ace/snippets/dart.js similarity index 100% rename from assets/framework/ace/snippets/dart.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/dart.js diff --git a/assets/framework/ace/snippets/diff.js b/src/etcdkeeper/assets/static/framework/ace/snippets/diff.js similarity index 100% rename from assets/framework/ace/snippets/diff.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/diff.js diff --git a/assets/framework/ace/snippets/django.js b/src/etcdkeeper/assets/static/framework/ace/snippets/django.js similarity index 100% rename from assets/framework/ace/snippets/django.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/django.js diff --git a/assets/framework/ace/snippets/dockerfile.js b/src/etcdkeeper/assets/static/framework/ace/snippets/dockerfile.js similarity index 100% rename from assets/framework/ace/snippets/dockerfile.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/dockerfile.js diff --git a/assets/framework/ace/snippets/dot.js b/src/etcdkeeper/assets/static/framework/ace/snippets/dot.js similarity index 100% rename from assets/framework/ace/snippets/dot.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/dot.js diff --git a/assets/framework/ace/snippets/drools.js b/src/etcdkeeper/assets/static/framework/ace/snippets/drools.js similarity index 100% rename from assets/framework/ace/snippets/drools.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/drools.js diff --git a/assets/framework/ace/snippets/eiffel.js b/src/etcdkeeper/assets/static/framework/ace/snippets/eiffel.js similarity index 100% rename from assets/framework/ace/snippets/eiffel.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/eiffel.js diff --git a/assets/framework/ace/snippets/ejs.js b/src/etcdkeeper/assets/static/framework/ace/snippets/ejs.js similarity index 100% rename from assets/framework/ace/snippets/ejs.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/ejs.js diff --git a/assets/framework/ace/snippets/elixir.js b/src/etcdkeeper/assets/static/framework/ace/snippets/elixir.js similarity index 100% rename from assets/framework/ace/snippets/elixir.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/elixir.js diff --git a/assets/framework/ace/snippets/elm.js b/src/etcdkeeper/assets/static/framework/ace/snippets/elm.js similarity index 100% rename from assets/framework/ace/snippets/elm.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/elm.js diff --git a/assets/framework/ace/snippets/erlang.js b/src/etcdkeeper/assets/static/framework/ace/snippets/erlang.js similarity index 100% rename from assets/framework/ace/snippets/erlang.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/erlang.js diff --git a/assets/framework/ace/snippets/forth.js b/src/etcdkeeper/assets/static/framework/ace/snippets/forth.js similarity index 100% rename from assets/framework/ace/snippets/forth.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/forth.js diff --git a/assets/framework/ace/snippets/fortran.js b/src/etcdkeeper/assets/static/framework/ace/snippets/fortran.js similarity index 100% rename from assets/framework/ace/snippets/fortran.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/fortran.js diff --git a/assets/framework/ace/snippets/ftl.js b/src/etcdkeeper/assets/static/framework/ace/snippets/ftl.js similarity index 100% rename from assets/framework/ace/snippets/ftl.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/ftl.js diff --git a/assets/framework/ace/snippets/gcode.js b/src/etcdkeeper/assets/static/framework/ace/snippets/gcode.js similarity index 100% rename from assets/framework/ace/snippets/gcode.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/gcode.js diff --git a/assets/framework/ace/snippets/gherkin.js b/src/etcdkeeper/assets/static/framework/ace/snippets/gherkin.js similarity index 100% rename from assets/framework/ace/snippets/gherkin.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/gherkin.js diff --git a/assets/framework/ace/snippets/gitignore.js b/src/etcdkeeper/assets/static/framework/ace/snippets/gitignore.js similarity index 100% rename from assets/framework/ace/snippets/gitignore.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/gitignore.js diff --git a/assets/framework/ace/snippets/glsl.js b/src/etcdkeeper/assets/static/framework/ace/snippets/glsl.js similarity index 100% rename from assets/framework/ace/snippets/glsl.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/glsl.js diff --git a/assets/framework/ace/snippets/gobstones.js b/src/etcdkeeper/assets/static/framework/ace/snippets/gobstones.js similarity index 100% rename from assets/framework/ace/snippets/gobstones.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/gobstones.js diff --git a/assets/framework/ace/snippets/golang.js b/src/etcdkeeper/assets/static/framework/ace/snippets/golang.js similarity index 100% rename from assets/framework/ace/snippets/golang.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/golang.js diff --git a/assets/framework/ace/snippets/groovy.js b/src/etcdkeeper/assets/static/framework/ace/snippets/groovy.js similarity index 100% rename from assets/framework/ace/snippets/groovy.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/groovy.js diff --git a/assets/framework/ace/snippets/haml.js b/src/etcdkeeper/assets/static/framework/ace/snippets/haml.js similarity index 100% rename from assets/framework/ace/snippets/haml.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/haml.js diff --git a/assets/framework/ace/snippets/handlebars.js b/src/etcdkeeper/assets/static/framework/ace/snippets/handlebars.js similarity index 100% rename from assets/framework/ace/snippets/handlebars.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/handlebars.js diff --git a/assets/framework/ace/snippets/haskell.js b/src/etcdkeeper/assets/static/framework/ace/snippets/haskell.js similarity index 100% rename from assets/framework/ace/snippets/haskell.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/haskell.js diff --git a/assets/framework/ace/snippets/haskell_cabal.js b/src/etcdkeeper/assets/static/framework/ace/snippets/haskell_cabal.js similarity index 100% rename from assets/framework/ace/snippets/haskell_cabal.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/haskell_cabal.js diff --git a/assets/framework/ace/snippets/haxe.js b/src/etcdkeeper/assets/static/framework/ace/snippets/haxe.js similarity index 100% rename from assets/framework/ace/snippets/haxe.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/haxe.js diff --git a/assets/framework/ace/snippets/hjson.js b/src/etcdkeeper/assets/static/framework/ace/snippets/hjson.js similarity index 100% rename from assets/framework/ace/snippets/hjson.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/hjson.js diff --git a/assets/framework/ace/snippets/html.js b/src/etcdkeeper/assets/static/framework/ace/snippets/html.js similarity index 100% rename from assets/framework/ace/snippets/html.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/html.js diff --git a/assets/framework/ace/snippets/html_elixir.js b/src/etcdkeeper/assets/static/framework/ace/snippets/html_elixir.js similarity index 100% rename from assets/framework/ace/snippets/html_elixir.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/html_elixir.js diff --git a/assets/framework/ace/snippets/html_ruby.js b/src/etcdkeeper/assets/static/framework/ace/snippets/html_ruby.js similarity index 100% rename from assets/framework/ace/snippets/html_ruby.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/html_ruby.js diff --git a/assets/framework/ace/snippets/ini.js b/src/etcdkeeper/assets/static/framework/ace/snippets/ini.js similarity index 100% rename from assets/framework/ace/snippets/ini.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/ini.js diff --git a/assets/framework/ace/snippets/io.js b/src/etcdkeeper/assets/static/framework/ace/snippets/io.js similarity index 100% rename from assets/framework/ace/snippets/io.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/io.js diff --git a/assets/framework/ace/snippets/jack.js b/src/etcdkeeper/assets/static/framework/ace/snippets/jack.js similarity index 100% rename from assets/framework/ace/snippets/jack.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/jack.js diff --git a/assets/framework/ace/snippets/jade.js b/src/etcdkeeper/assets/static/framework/ace/snippets/jade.js similarity index 100% rename from assets/framework/ace/snippets/jade.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/jade.js diff --git a/assets/framework/ace/snippets/java.js b/src/etcdkeeper/assets/static/framework/ace/snippets/java.js similarity index 100% rename from assets/framework/ace/snippets/java.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/java.js diff --git a/assets/framework/ace/snippets/javascript.js b/src/etcdkeeper/assets/static/framework/ace/snippets/javascript.js similarity index 100% rename from assets/framework/ace/snippets/javascript.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/javascript.js diff --git a/assets/framework/ace/snippets/json.js b/src/etcdkeeper/assets/static/framework/ace/snippets/json.js similarity index 100% rename from assets/framework/ace/snippets/json.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/json.js diff --git a/assets/framework/ace/snippets/jsoniq.js b/src/etcdkeeper/assets/static/framework/ace/snippets/jsoniq.js similarity index 100% rename from assets/framework/ace/snippets/jsoniq.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/jsoniq.js diff --git a/assets/framework/ace/snippets/jsp.js b/src/etcdkeeper/assets/static/framework/ace/snippets/jsp.js similarity index 100% rename from assets/framework/ace/snippets/jsp.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/jsp.js diff --git a/assets/framework/ace/snippets/jsx.js b/src/etcdkeeper/assets/static/framework/ace/snippets/jsx.js similarity index 100% rename from assets/framework/ace/snippets/jsx.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/jsx.js diff --git a/assets/framework/ace/snippets/julia.js b/src/etcdkeeper/assets/static/framework/ace/snippets/julia.js similarity index 100% rename from assets/framework/ace/snippets/julia.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/julia.js diff --git a/assets/framework/ace/snippets/kotlin.js b/src/etcdkeeper/assets/static/framework/ace/snippets/kotlin.js similarity index 100% rename from assets/framework/ace/snippets/kotlin.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/kotlin.js diff --git a/assets/framework/ace/snippets/latex.js b/src/etcdkeeper/assets/static/framework/ace/snippets/latex.js similarity index 100% rename from assets/framework/ace/snippets/latex.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/latex.js diff --git a/assets/framework/ace/snippets/less.js b/src/etcdkeeper/assets/static/framework/ace/snippets/less.js similarity index 100% rename from assets/framework/ace/snippets/less.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/less.js diff --git a/assets/framework/ace/snippets/liquid.js b/src/etcdkeeper/assets/static/framework/ace/snippets/liquid.js similarity index 100% rename from assets/framework/ace/snippets/liquid.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/liquid.js diff --git a/assets/framework/ace/snippets/lisp.js b/src/etcdkeeper/assets/static/framework/ace/snippets/lisp.js similarity index 100% rename from assets/framework/ace/snippets/lisp.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/lisp.js diff --git a/assets/framework/ace/snippets/livescript.js b/src/etcdkeeper/assets/static/framework/ace/snippets/livescript.js similarity index 100% rename from assets/framework/ace/snippets/livescript.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/livescript.js diff --git a/assets/framework/ace/snippets/logiql.js b/src/etcdkeeper/assets/static/framework/ace/snippets/logiql.js similarity index 100% rename from assets/framework/ace/snippets/logiql.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/logiql.js diff --git a/assets/framework/ace/snippets/lsl.js b/src/etcdkeeper/assets/static/framework/ace/snippets/lsl.js similarity index 100% rename from assets/framework/ace/snippets/lsl.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/lsl.js diff --git a/assets/framework/ace/snippets/lua.js b/src/etcdkeeper/assets/static/framework/ace/snippets/lua.js similarity index 100% rename from assets/framework/ace/snippets/lua.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/lua.js diff --git a/assets/framework/ace/snippets/luapage.js b/src/etcdkeeper/assets/static/framework/ace/snippets/luapage.js similarity index 100% rename from assets/framework/ace/snippets/luapage.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/luapage.js diff --git a/assets/framework/ace/snippets/lucene.js b/src/etcdkeeper/assets/static/framework/ace/snippets/lucene.js similarity index 100% rename from assets/framework/ace/snippets/lucene.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/lucene.js diff --git a/assets/framework/ace/snippets/makefile.js b/src/etcdkeeper/assets/static/framework/ace/snippets/makefile.js similarity index 100% rename from assets/framework/ace/snippets/makefile.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/makefile.js diff --git a/assets/framework/ace/snippets/markdown.js b/src/etcdkeeper/assets/static/framework/ace/snippets/markdown.js similarity index 100% rename from assets/framework/ace/snippets/markdown.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/markdown.js diff --git a/assets/framework/ace/snippets/mask.js b/src/etcdkeeper/assets/static/framework/ace/snippets/mask.js similarity index 100% rename from assets/framework/ace/snippets/mask.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/mask.js diff --git a/assets/framework/ace/snippets/matlab.js b/src/etcdkeeper/assets/static/framework/ace/snippets/matlab.js similarity index 100% rename from assets/framework/ace/snippets/matlab.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/matlab.js diff --git a/assets/framework/ace/snippets/maze.js b/src/etcdkeeper/assets/static/framework/ace/snippets/maze.js similarity index 100% rename from assets/framework/ace/snippets/maze.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/maze.js diff --git a/assets/framework/ace/snippets/mel.js b/src/etcdkeeper/assets/static/framework/ace/snippets/mel.js similarity index 100% rename from assets/framework/ace/snippets/mel.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/mel.js diff --git a/assets/framework/ace/snippets/mushcode.js b/src/etcdkeeper/assets/static/framework/ace/snippets/mushcode.js similarity index 100% rename from assets/framework/ace/snippets/mushcode.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/mushcode.js diff --git a/assets/framework/ace/snippets/mysql.js b/src/etcdkeeper/assets/static/framework/ace/snippets/mysql.js similarity index 100% rename from assets/framework/ace/snippets/mysql.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/mysql.js diff --git a/assets/framework/ace/snippets/nix.js b/src/etcdkeeper/assets/static/framework/ace/snippets/nix.js similarity index 100% rename from assets/framework/ace/snippets/nix.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/nix.js diff --git a/assets/framework/ace/snippets/nsis.js b/src/etcdkeeper/assets/static/framework/ace/snippets/nsis.js similarity index 100% rename from assets/framework/ace/snippets/nsis.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/nsis.js diff --git a/assets/framework/ace/snippets/objectivec.js b/src/etcdkeeper/assets/static/framework/ace/snippets/objectivec.js similarity index 100% rename from assets/framework/ace/snippets/objectivec.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/objectivec.js diff --git a/assets/framework/ace/snippets/ocaml.js b/src/etcdkeeper/assets/static/framework/ace/snippets/ocaml.js similarity index 100% rename from assets/framework/ace/snippets/ocaml.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/ocaml.js diff --git a/assets/framework/ace/snippets/pascal.js b/src/etcdkeeper/assets/static/framework/ace/snippets/pascal.js similarity index 100% rename from assets/framework/ace/snippets/pascal.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/pascal.js diff --git a/assets/framework/ace/snippets/perl.js b/src/etcdkeeper/assets/static/framework/ace/snippets/perl.js similarity index 100% rename from assets/framework/ace/snippets/perl.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/perl.js diff --git a/assets/framework/ace/snippets/pgsql.js b/src/etcdkeeper/assets/static/framework/ace/snippets/pgsql.js similarity index 100% rename from assets/framework/ace/snippets/pgsql.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/pgsql.js diff --git a/assets/framework/ace/snippets/php.js b/src/etcdkeeper/assets/static/framework/ace/snippets/php.js similarity index 100% rename from assets/framework/ace/snippets/php.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/php.js diff --git a/assets/framework/ace/snippets/plain_text.js b/src/etcdkeeper/assets/static/framework/ace/snippets/plain_text.js similarity index 100% rename from assets/framework/ace/snippets/plain_text.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/plain_text.js diff --git a/assets/framework/ace/snippets/powershell.js b/src/etcdkeeper/assets/static/framework/ace/snippets/powershell.js similarity index 100% rename from assets/framework/ace/snippets/powershell.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/powershell.js diff --git a/assets/framework/ace/snippets/praat.js b/src/etcdkeeper/assets/static/framework/ace/snippets/praat.js similarity index 100% rename from assets/framework/ace/snippets/praat.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/praat.js diff --git a/assets/framework/ace/snippets/prolog.js b/src/etcdkeeper/assets/static/framework/ace/snippets/prolog.js similarity index 100% rename from assets/framework/ace/snippets/prolog.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/prolog.js diff --git a/assets/framework/ace/snippets/properties.js b/src/etcdkeeper/assets/static/framework/ace/snippets/properties.js similarity index 100% rename from assets/framework/ace/snippets/properties.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/properties.js diff --git a/assets/framework/ace/snippets/protobuf.js b/src/etcdkeeper/assets/static/framework/ace/snippets/protobuf.js similarity index 100% rename from assets/framework/ace/snippets/protobuf.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/protobuf.js diff --git a/assets/framework/ace/snippets/python.js b/src/etcdkeeper/assets/static/framework/ace/snippets/python.js similarity index 100% rename from assets/framework/ace/snippets/python.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/python.js diff --git a/assets/framework/ace/snippets/r.js b/src/etcdkeeper/assets/static/framework/ace/snippets/r.js similarity index 100% rename from assets/framework/ace/snippets/r.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/r.js diff --git a/assets/framework/ace/snippets/razor.js b/src/etcdkeeper/assets/static/framework/ace/snippets/razor.js similarity index 100% rename from assets/framework/ace/snippets/razor.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/razor.js diff --git a/assets/framework/ace/snippets/rdoc.js b/src/etcdkeeper/assets/static/framework/ace/snippets/rdoc.js similarity index 100% rename from assets/framework/ace/snippets/rdoc.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/rdoc.js diff --git a/assets/framework/ace/snippets/rhtml.js b/src/etcdkeeper/assets/static/framework/ace/snippets/rhtml.js similarity index 100% rename from assets/framework/ace/snippets/rhtml.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/rhtml.js diff --git a/assets/framework/ace/snippets/rst.js b/src/etcdkeeper/assets/static/framework/ace/snippets/rst.js similarity index 100% rename from assets/framework/ace/snippets/rst.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/rst.js diff --git a/assets/framework/ace/snippets/ruby.js b/src/etcdkeeper/assets/static/framework/ace/snippets/ruby.js similarity index 100% rename from assets/framework/ace/snippets/ruby.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/ruby.js diff --git a/assets/framework/ace/snippets/rust.js b/src/etcdkeeper/assets/static/framework/ace/snippets/rust.js similarity index 100% rename from assets/framework/ace/snippets/rust.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/rust.js diff --git a/assets/framework/ace/snippets/sass.js b/src/etcdkeeper/assets/static/framework/ace/snippets/sass.js similarity index 100% rename from assets/framework/ace/snippets/sass.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/sass.js diff --git a/assets/framework/ace/snippets/scad.js b/src/etcdkeeper/assets/static/framework/ace/snippets/scad.js similarity index 100% rename from assets/framework/ace/snippets/scad.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/scad.js diff --git a/assets/framework/ace/snippets/scala.js b/src/etcdkeeper/assets/static/framework/ace/snippets/scala.js similarity index 100% rename from assets/framework/ace/snippets/scala.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/scala.js diff --git a/assets/framework/ace/snippets/scheme.js b/src/etcdkeeper/assets/static/framework/ace/snippets/scheme.js similarity index 100% rename from assets/framework/ace/snippets/scheme.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/scheme.js diff --git a/assets/framework/ace/snippets/scss.js b/src/etcdkeeper/assets/static/framework/ace/snippets/scss.js similarity index 100% rename from assets/framework/ace/snippets/scss.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/scss.js diff --git a/assets/framework/ace/snippets/sh.js b/src/etcdkeeper/assets/static/framework/ace/snippets/sh.js similarity index 100% rename from assets/framework/ace/snippets/sh.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/sh.js diff --git a/assets/framework/ace/snippets/sjs.js b/src/etcdkeeper/assets/static/framework/ace/snippets/sjs.js similarity index 100% rename from assets/framework/ace/snippets/sjs.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/sjs.js diff --git a/assets/framework/ace/snippets/smarty.js b/src/etcdkeeper/assets/static/framework/ace/snippets/smarty.js similarity index 100% rename from assets/framework/ace/snippets/smarty.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/smarty.js diff --git a/assets/framework/ace/snippets/snippets.js b/src/etcdkeeper/assets/static/framework/ace/snippets/snippets.js similarity index 100% rename from assets/framework/ace/snippets/snippets.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/snippets.js diff --git a/assets/framework/ace/snippets/soy_template.js b/src/etcdkeeper/assets/static/framework/ace/snippets/soy_template.js similarity index 100% rename from assets/framework/ace/snippets/soy_template.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/soy_template.js diff --git a/assets/framework/ace/snippets/space.js b/src/etcdkeeper/assets/static/framework/ace/snippets/space.js similarity index 100% rename from assets/framework/ace/snippets/space.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/space.js diff --git a/assets/framework/ace/snippets/sql.js b/src/etcdkeeper/assets/static/framework/ace/snippets/sql.js similarity index 100% rename from assets/framework/ace/snippets/sql.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/sql.js diff --git a/assets/framework/ace/snippets/sqlserver.js b/src/etcdkeeper/assets/static/framework/ace/snippets/sqlserver.js similarity index 100% rename from assets/framework/ace/snippets/sqlserver.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/sqlserver.js diff --git a/assets/framework/ace/snippets/stylus.js b/src/etcdkeeper/assets/static/framework/ace/snippets/stylus.js similarity index 100% rename from assets/framework/ace/snippets/stylus.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/stylus.js diff --git a/assets/framework/ace/snippets/svg.js b/src/etcdkeeper/assets/static/framework/ace/snippets/svg.js similarity index 100% rename from assets/framework/ace/snippets/svg.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/svg.js diff --git a/assets/framework/ace/snippets/swift.js b/src/etcdkeeper/assets/static/framework/ace/snippets/swift.js similarity index 100% rename from assets/framework/ace/snippets/swift.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/swift.js diff --git a/assets/framework/ace/snippets/tcl.js b/src/etcdkeeper/assets/static/framework/ace/snippets/tcl.js similarity index 100% rename from assets/framework/ace/snippets/tcl.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/tcl.js diff --git a/assets/framework/ace/snippets/tex.js b/src/etcdkeeper/assets/static/framework/ace/snippets/tex.js similarity index 100% rename from assets/framework/ace/snippets/tex.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/tex.js diff --git a/assets/framework/ace/snippets/text.js b/src/etcdkeeper/assets/static/framework/ace/snippets/text.js similarity index 100% rename from assets/framework/ace/snippets/text.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/text.js diff --git a/assets/framework/ace/snippets/textile.js b/src/etcdkeeper/assets/static/framework/ace/snippets/textile.js similarity index 100% rename from assets/framework/ace/snippets/textile.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/textile.js diff --git a/assets/framework/ace/snippets/toml.js b/src/etcdkeeper/assets/static/framework/ace/snippets/toml.js similarity index 100% rename from assets/framework/ace/snippets/toml.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/toml.js diff --git a/assets/framework/ace/snippets/tsx.js b/src/etcdkeeper/assets/static/framework/ace/snippets/tsx.js similarity index 100% rename from assets/framework/ace/snippets/tsx.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/tsx.js diff --git a/assets/framework/ace/snippets/twig.js b/src/etcdkeeper/assets/static/framework/ace/snippets/twig.js similarity index 100% rename from assets/framework/ace/snippets/twig.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/twig.js diff --git a/assets/framework/ace/snippets/typescript.js b/src/etcdkeeper/assets/static/framework/ace/snippets/typescript.js similarity index 100% rename from assets/framework/ace/snippets/typescript.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/typescript.js diff --git a/assets/framework/ace/snippets/vala.js b/src/etcdkeeper/assets/static/framework/ace/snippets/vala.js similarity index 100% rename from assets/framework/ace/snippets/vala.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/vala.js diff --git a/assets/framework/ace/snippets/vbscript.js b/src/etcdkeeper/assets/static/framework/ace/snippets/vbscript.js similarity index 100% rename from assets/framework/ace/snippets/vbscript.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/vbscript.js diff --git a/assets/framework/ace/snippets/velocity.js b/src/etcdkeeper/assets/static/framework/ace/snippets/velocity.js similarity index 100% rename from assets/framework/ace/snippets/velocity.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/velocity.js diff --git a/assets/framework/ace/snippets/verilog.js b/src/etcdkeeper/assets/static/framework/ace/snippets/verilog.js similarity index 100% rename from assets/framework/ace/snippets/verilog.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/verilog.js diff --git a/assets/framework/ace/snippets/vhdl.js b/src/etcdkeeper/assets/static/framework/ace/snippets/vhdl.js similarity index 100% rename from assets/framework/ace/snippets/vhdl.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/vhdl.js diff --git a/assets/framework/ace/snippets/wollok.js b/src/etcdkeeper/assets/static/framework/ace/snippets/wollok.js similarity index 100% rename from assets/framework/ace/snippets/wollok.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/wollok.js diff --git a/assets/framework/ace/snippets/xml.js b/src/etcdkeeper/assets/static/framework/ace/snippets/xml.js similarity index 100% rename from assets/framework/ace/snippets/xml.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/xml.js diff --git a/assets/framework/ace/snippets/xquery.js b/src/etcdkeeper/assets/static/framework/ace/snippets/xquery.js similarity index 100% rename from assets/framework/ace/snippets/xquery.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/xquery.js diff --git a/assets/framework/ace/snippets/yaml.js b/src/etcdkeeper/assets/static/framework/ace/snippets/yaml.js similarity index 100% rename from assets/framework/ace/snippets/yaml.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/yaml.js diff --git a/assets/framework/ace/theme-ambiance.js b/src/etcdkeeper/assets/static/framework/ace/theme-ambiance.js similarity index 100% rename from assets/framework/ace/theme-ambiance.js rename to src/etcdkeeper/assets/static/framework/ace/theme-ambiance.js diff --git a/assets/framework/ace/theme-chaos.js b/src/etcdkeeper/assets/static/framework/ace/theme-chaos.js similarity index 100% rename from assets/framework/ace/theme-chaos.js rename to src/etcdkeeper/assets/static/framework/ace/theme-chaos.js diff --git a/assets/framework/ace/theme-chrome.js b/src/etcdkeeper/assets/static/framework/ace/theme-chrome.js similarity index 100% rename from assets/framework/ace/theme-chrome.js rename to src/etcdkeeper/assets/static/framework/ace/theme-chrome.js diff --git a/assets/framework/ace/theme-clouds.js b/src/etcdkeeper/assets/static/framework/ace/theme-clouds.js similarity index 100% rename from assets/framework/ace/theme-clouds.js rename to src/etcdkeeper/assets/static/framework/ace/theme-clouds.js diff --git a/assets/framework/ace/theme-clouds_midnight.js b/src/etcdkeeper/assets/static/framework/ace/theme-clouds_midnight.js similarity index 100% rename from assets/framework/ace/theme-clouds_midnight.js rename to src/etcdkeeper/assets/static/framework/ace/theme-clouds_midnight.js diff --git a/assets/framework/ace/theme-cobalt.js b/src/etcdkeeper/assets/static/framework/ace/theme-cobalt.js similarity index 100% rename from assets/framework/ace/theme-cobalt.js rename to src/etcdkeeper/assets/static/framework/ace/theme-cobalt.js diff --git a/assets/framework/ace/theme-crimson_editor.js b/src/etcdkeeper/assets/static/framework/ace/theme-crimson_editor.js similarity index 100% rename from assets/framework/ace/theme-crimson_editor.js rename to src/etcdkeeper/assets/static/framework/ace/theme-crimson_editor.js diff --git a/assets/framework/ace/theme-dawn.js b/src/etcdkeeper/assets/static/framework/ace/theme-dawn.js similarity index 100% rename from assets/framework/ace/theme-dawn.js rename to src/etcdkeeper/assets/static/framework/ace/theme-dawn.js diff --git a/assets/framework/ace/theme-dreamweaver.js b/src/etcdkeeper/assets/static/framework/ace/theme-dreamweaver.js similarity index 100% rename from assets/framework/ace/theme-dreamweaver.js rename to src/etcdkeeper/assets/static/framework/ace/theme-dreamweaver.js diff --git a/assets/framework/ace/theme-eclipse.js b/src/etcdkeeper/assets/static/framework/ace/theme-eclipse.js similarity index 100% rename from assets/framework/ace/theme-eclipse.js rename to src/etcdkeeper/assets/static/framework/ace/theme-eclipse.js diff --git a/assets/framework/ace/theme-github.js b/src/etcdkeeper/assets/static/framework/ace/theme-github.js similarity index 100% rename from assets/framework/ace/theme-github.js rename to src/etcdkeeper/assets/static/framework/ace/theme-github.js diff --git a/assets/framework/ace/theme-idle_fingers.js b/src/etcdkeeper/assets/static/framework/ace/theme-idle_fingers.js similarity index 100% rename from assets/framework/ace/theme-idle_fingers.js rename to src/etcdkeeper/assets/static/framework/ace/theme-idle_fingers.js diff --git a/assets/framework/ace/theme-iplastic.js b/src/etcdkeeper/assets/static/framework/ace/theme-iplastic.js similarity index 100% rename from assets/framework/ace/theme-iplastic.js rename to src/etcdkeeper/assets/static/framework/ace/theme-iplastic.js diff --git a/assets/framework/ace/theme-katzenmilch.js b/src/etcdkeeper/assets/static/framework/ace/theme-katzenmilch.js similarity index 100% rename from assets/framework/ace/theme-katzenmilch.js rename to src/etcdkeeper/assets/static/framework/ace/theme-katzenmilch.js diff --git a/assets/framework/ace/theme-kr_theme.js b/src/etcdkeeper/assets/static/framework/ace/theme-kr_theme.js similarity index 100% rename from assets/framework/ace/theme-kr_theme.js rename to src/etcdkeeper/assets/static/framework/ace/theme-kr_theme.js diff --git a/assets/framework/ace/theme-kuroir.js b/src/etcdkeeper/assets/static/framework/ace/theme-kuroir.js similarity index 100% rename from assets/framework/ace/theme-kuroir.js rename to src/etcdkeeper/assets/static/framework/ace/theme-kuroir.js diff --git a/assets/framework/ace/theme-merbivore.js b/src/etcdkeeper/assets/static/framework/ace/theme-merbivore.js similarity index 100% rename from assets/framework/ace/theme-merbivore.js rename to src/etcdkeeper/assets/static/framework/ace/theme-merbivore.js diff --git a/assets/framework/ace/theme-merbivore_soft.js b/src/etcdkeeper/assets/static/framework/ace/theme-merbivore_soft.js similarity index 100% rename from assets/framework/ace/theme-merbivore_soft.js rename to src/etcdkeeper/assets/static/framework/ace/theme-merbivore_soft.js diff --git a/assets/framework/ace/theme-mono_industrial.js b/src/etcdkeeper/assets/static/framework/ace/theme-mono_industrial.js similarity index 100% rename from assets/framework/ace/theme-mono_industrial.js rename to src/etcdkeeper/assets/static/framework/ace/theme-mono_industrial.js diff --git a/assets/framework/ace/theme-monokai.js b/src/etcdkeeper/assets/static/framework/ace/theme-monokai.js similarity index 100% rename from assets/framework/ace/theme-monokai.js rename to src/etcdkeeper/assets/static/framework/ace/theme-monokai.js diff --git a/assets/framework/ace/theme-pastel_on_dark.js b/src/etcdkeeper/assets/static/framework/ace/theme-pastel_on_dark.js similarity index 100% rename from assets/framework/ace/theme-pastel_on_dark.js rename to src/etcdkeeper/assets/static/framework/ace/theme-pastel_on_dark.js diff --git a/assets/framework/ace/theme-solarized_dark.js b/src/etcdkeeper/assets/static/framework/ace/theme-solarized_dark.js similarity index 100% rename from assets/framework/ace/theme-solarized_dark.js rename to src/etcdkeeper/assets/static/framework/ace/theme-solarized_dark.js diff --git a/assets/framework/ace/theme-solarized_light.js b/src/etcdkeeper/assets/static/framework/ace/theme-solarized_light.js similarity index 100% rename from assets/framework/ace/theme-solarized_light.js rename to src/etcdkeeper/assets/static/framework/ace/theme-solarized_light.js diff --git a/assets/framework/ace/theme-sqlserver.js b/src/etcdkeeper/assets/static/framework/ace/theme-sqlserver.js similarity index 100% rename from assets/framework/ace/theme-sqlserver.js rename to src/etcdkeeper/assets/static/framework/ace/theme-sqlserver.js diff --git a/assets/framework/ace/theme-terminal.js b/src/etcdkeeper/assets/static/framework/ace/theme-terminal.js similarity index 100% rename from assets/framework/ace/theme-terminal.js rename to src/etcdkeeper/assets/static/framework/ace/theme-terminal.js diff --git a/assets/framework/ace/theme-textmate.js b/src/etcdkeeper/assets/static/framework/ace/theme-textmate.js similarity index 100% rename from assets/framework/ace/theme-textmate.js rename to src/etcdkeeper/assets/static/framework/ace/theme-textmate.js diff --git a/assets/framework/ace/theme-tomorrow.js b/src/etcdkeeper/assets/static/framework/ace/theme-tomorrow.js similarity index 100% rename from assets/framework/ace/theme-tomorrow.js rename to src/etcdkeeper/assets/static/framework/ace/theme-tomorrow.js diff --git a/assets/framework/ace/theme-tomorrow_night.js b/src/etcdkeeper/assets/static/framework/ace/theme-tomorrow_night.js similarity index 100% rename from assets/framework/ace/theme-tomorrow_night.js rename to src/etcdkeeper/assets/static/framework/ace/theme-tomorrow_night.js diff --git a/assets/framework/ace/theme-tomorrow_night_blue.js b/src/etcdkeeper/assets/static/framework/ace/theme-tomorrow_night_blue.js similarity index 100% rename from assets/framework/ace/theme-tomorrow_night_blue.js rename to src/etcdkeeper/assets/static/framework/ace/theme-tomorrow_night_blue.js diff --git a/assets/framework/ace/theme-tomorrow_night_bright.js b/src/etcdkeeper/assets/static/framework/ace/theme-tomorrow_night_bright.js similarity index 100% rename from assets/framework/ace/theme-tomorrow_night_bright.js rename to src/etcdkeeper/assets/static/framework/ace/theme-tomorrow_night_bright.js diff --git a/assets/framework/ace/theme-tomorrow_night_eighties.js b/src/etcdkeeper/assets/static/framework/ace/theme-tomorrow_night_eighties.js similarity index 100% rename from assets/framework/ace/theme-tomorrow_night_eighties.js rename to src/etcdkeeper/assets/static/framework/ace/theme-tomorrow_night_eighties.js diff --git a/assets/framework/ace/theme-twilight.js b/src/etcdkeeper/assets/static/framework/ace/theme-twilight.js similarity index 100% rename from assets/framework/ace/theme-twilight.js rename to src/etcdkeeper/assets/static/framework/ace/theme-twilight.js diff --git a/assets/framework/ace/theme-vibrant_ink.js b/src/etcdkeeper/assets/static/framework/ace/theme-vibrant_ink.js similarity index 100% rename from assets/framework/ace/theme-vibrant_ink.js rename to src/etcdkeeper/assets/static/framework/ace/theme-vibrant_ink.js diff --git a/assets/framework/ace/theme-xcode.js b/src/etcdkeeper/assets/static/framework/ace/theme-xcode.js similarity index 100% rename from assets/framework/ace/theme-xcode.js rename to src/etcdkeeper/assets/static/framework/ace/theme-xcode.js diff --git a/assets/framework/ace/worker-coffee.js b/src/etcdkeeper/assets/static/framework/ace/worker-coffee.js similarity index 100% rename from assets/framework/ace/worker-coffee.js rename to src/etcdkeeper/assets/static/framework/ace/worker-coffee.js diff --git a/assets/framework/ace/worker-css.js b/src/etcdkeeper/assets/static/framework/ace/worker-css.js similarity index 100% rename from assets/framework/ace/worker-css.js rename to src/etcdkeeper/assets/static/framework/ace/worker-css.js diff --git a/assets/framework/ace/worker-html.js b/src/etcdkeeper/assets/static/framework/ace/worker-html.js similarity index 100% rename from assets/framework/ace/worker-html.js rename to src/etcdkeeper/assets/static/framework/ace/worker-html.js diff --git a/assets/framework/ace/worker-javascript.js b/src/etcdkeeper/assets/static/framework/ace/worker-javascript.js similarity index 100% rename from assets/framework/ace/worker-javascript.js rename to src/etcdkeeper/assets/static/framework/ace/worker-javascript.js diff --git a/assets/framework/ace/worker-json.js b/src/etcdkeeper/assets/static/framework/ace/worker-json.js similarity index 100% rename from assets/framework/ace/worker-json.js rename to src/etcdkeeper/assets/static/framework/ace/worker-json.js diff --git a/assets/framework/ace/worker-lua.js b/src/etcdkeeper/assets/static/framework/ace/worker-lua.js similarity index 100% rename from assets/framework/ace/worker-lua.js rename to src/etcdkeeper/assets/static/framework/ace/worker-lua.js diff --git a/assets/framework/ace/worker-php.js b/src/etcdkeeper/assets/static/framework/ace/worker-php.js similarity index 100% rename from assets/framework/ace/worker-php.js rename to src/etcdkeeper/assets/static/framework/ace/worker-php.js diff --git a/assets/framework/ace/worker-xml.js b/src/etcdkeeper/assets/static/framework/ace/worker-xml.js similarity index 100% rename from assets/framework/ace/worker-xml.js rename to src/etcdkeeper/assets/static/framework/ace/worker-xml.js diff --git a/assets/framework/ace/worker-xquery.js b/src/etcdkeeper/assets/static/framework/ace/worker-xquery.js similarity index 100% rename from assets/framework/ace/worker-xquery.js rename to src/etcdkeeper/assets/static/framework/ace/worker-xquery.js diff --git a/assets/framework/custom/css/style.css b/src/etcdkeeper/assets/static/framework/custom/css/style.css similarity index 100% rename from assets/framework/custom/css/style.css rename to src/etcdkeeper/assets/static/framework/custom/css/style.css diff --git a/assets/framework/custom/js/common.js b/src/etcdkeeper/assets/static/framework/custom/js/common.js similarity index 100% rename from assets/framework/custom/js/common.js rename to src/etcdkeeper/assets/static/framework/custom/js/common.js diff --git a/assets/framework/easyui/jquery.easyui.min.js b/src/etcdkeeper/assets/static/framework/easyui/jquery.easyui.min.js similarity index 100% rename from assets/framework/easyui/jquery.easyui.min.js rename to src/etcdkeeper/assets/static/framework/easyui/jquery.easyui.min.js diff --git a/assets/framework/easyui/jquery.min.js b/src/etcdkeeper/assets/static/framework/easyui/jquery.min.js similarity index 100% rename from assets/framework/easyui/jquery.min.js rename to src/etcdkeeper/assets/static/framework/easyui/jquery.min.js diff --git a/assets/framework/easyui/locale/easyui-lang-af.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-af.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-af.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-af.js diff --git a/assets/framework/easyui/locale/easyui-lang-am.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-am.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-am.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-am.js diff --git a/assets/framework/easyui/locale/easyui-lang-ar.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-ar.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-ar.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-ar.js diff --git a/assets/framework/easyui/locale/easyui-lang-bg.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-bg.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-bg.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-bg.js diff --git a/assets/framework/easyui/locale/easyui-lang-ca.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-ca.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-ca.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-ca.js diff --git a/assets/framework/easyui/locale/easyui-lang-cs.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-cs.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-cs.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-cs.js diff --git a/assets/framework/easyui/locale/easyui-lang-cz.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-cz.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-cz.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-cz.js diff --git a/assets/framework/easyui/locale/easyui-lang-da.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-da.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-da.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-da.js diff --git a/assets/framework/easyui/locale/easyui-lang-de.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-de.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-de.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-de.js diff --git a/assets/framework/easyui/locale/easyui-lang-el.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-el.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-el.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-el.js diff --git a/assets/framework/easyui/locale/easyui-lang-en.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-en.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-en.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-en.js diff --git a/assets/framework/easyui/locale/easyui-lang-es.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-es.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-es.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-es.js diff --git a/assets/framework/easyui/locale/easyui-lang-fr.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-fr.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-fr.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-fr.js diff --git a/assets/framework/easyui/locale/easyui-lang-it.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-it.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-it.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-it.js diff --git a/assets/framework/easyui/locale/easyui-lang-jp.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-jp.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-jp.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-jp.js diff --git a/assets/framework/easyui/locale/easyui-lang-ko.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-ko.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-ko.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-ko.js diff --git a/assets/framework/easyui/locale/easyui-lang-nl.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-nl.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-nl.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-nl.js diff --git a/assets/framework/easyui/locale/easyui-lang-pl.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-pl.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-pl.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-pl.js diff --git a/assets/framework/easyui/locale/easyui-lang-pt_BR.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-pt_BR.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-pt_BR.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-pt_BR.js diff --git a/assets/framework/easyui/locale/easyui-lang-ru.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-ru.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-ru.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-ru.js diff --git a/assets/framework/easyui/locale/easyui-lang-sv_SE.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-sv_SE.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-sv_SE.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-sv_SE.js diff --git a/assets/framework/easyui/locale/easyui-lang-tr.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-tr.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-tr.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-tr.js diff --git a/assets/framework/easyui/locale/easyui-lang-zh_CN.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-zh_CN.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-zh_CN.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-zh_CN.js diff --git a/assets/framework/easyui/locale/easyui-lang-zh_TW.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-zh_TW.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-zh_TW.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-zh_TW.js diff --git a/assets/framework/easyui/themes/bootstrap/easyui.css b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/easyui.css similarity index 100% rename from assets/framework/easyui/themes/bootstrap/easyui.css rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/easyui.css diff --git a/assets/framework/easyui/themes/bootstrap/images/accordion_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/accordion_arrows.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/accordion_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/accordion_arrows.png diff --git a/assets/framework/easyui/themes/bootstrap/images/blank.gif b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/blank.gif similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/blank.gif rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/blank.gif diff --git a/assets/framework/easyui/themes/bootstrap/images/calendar_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/calendar_arrows.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/calendar_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/calendar_arrows.png diff --git a/assets/framework/easyui/themes/bootstrap/images/combo_arrow.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/combo_arrow.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/combo_arrow.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/combo_arrow.png diff --git a/assets/framework/easyui/themes/bootstrap/images/datagrid_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/datagrid_icons.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/datagrid_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/datagrid_icons.png diff --git a/assets/framework/easyui/themes/bootstrap/images/datebox_arrow.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/datebox_arrow.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/datebox_arrow.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/datebox_arrow.png diff --git a/assets/framework/easyui/themes/bootstrap/images/layout_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/layout_arrows.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/layout_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/layout_arrows.png diff --git a/assets/framework/easyui/themes/bootstrap/images/linkbutton_bg.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/linkbutton_bg.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/linkbutton_bg.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/linkbutton_bg.png diff --git a/assets/framework/easyui/themes/bootstrap/images/loading.gif b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/loading.gif similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/loading.gif rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/loading.gif diff --git a/assets/framework/easyui/themes/bootstrap/images/menu_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/menu_arrows.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/menu_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/menu_arrows.png diff --git a/assets/framework/easyui/themes/bootstrap/images/messager_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/messager_icons.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/messager_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/messager_icons.png diff --git a/assets/framework/easyui/themes/bootstrap/images/pagination_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/pagination_icons.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/pagination_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/pagination_icons.png diff --git a/assets/framework/easyui/themes/bootstrap/images/panel_tools.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/panel_tools.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/panel_tools.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/panel_tools.png diff --git a/assets/framework/easyui/themes/bootstrap/images/searchbox_button.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/searchbox_button.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/searchbox_button.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/searchbox_button.png diff --git a/assets/framework/easyui/themes/bootstrap/images/slider_handle.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/slider_handle.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/slider_handle.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/slider_handle.png diff --git a/assets/framework/easyui/themes/bootstrap/images/spinner_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/spinner_arrows.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/spinner_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/spinner_arrows.png diff --git a/assets/framework/easyui/themes/bootstrap/images/tabs_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/tabs_icons.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/tabs_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/tabs_icons.png diff --git a/assets/framework/easyui/themes/bootstrap/images/tree_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/tree_icons.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/tree_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/tree_icons.png diff --git a/assets/framework/easyui/themes/bootstrap/images/validatebox_warning.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/validatebox_warning.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/validatebox_warning.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/validatebox_warning.png diff --git a/assets/framework/easyui/themes/default/easyui.css b/src/etcdkeeper/assets/static/framework/easyui/themes/default/easyui.css similarity index 100% rename from assets/framework/easyui/themes/default/easyui.css rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/easyui.css diff --git a/assets/framework/easyui/themes/default/images/accordion_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/accordion_arrows.png similarity index 100% rename from assets/framework/easyui/themes/default/images/accordion_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/accordion_arrows.png diff --git a/assets/framework/easyui/themes/default/images/blank.gif b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/blank.gif similarity index 100% rename from assets/framework/easyui/themes/default/images/blank.gif rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/blank.gif diff --git a/assets/framework/easyui/themes/default/images/calendar_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/calendar_arrows.png similarity index 100% rename from assets/framework/easyui/themes/default/images/calendar_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/calendar_arrows.png diff --git a/assets/framework/easyui/themes/default/images/combo_arrow.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/combo_arrow.png similarity index 100% rename from assets/framework/easyui/themes/default/images/combo_arrow.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/combo_arrow.png diff --git a/assets/framework/easyui/themes/default/images/datagrid_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/datagrid_icons.png similarity index 100% rename from assets/framework/easyui/themes/default/images/datagrid_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/datagrid_icons.png diff --git a/assets/framework/easyui/themes/default/images/datebox_arrow.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/datebox_arrow.png similarity index 100% rename from assets/framework/easyui/themes/default/images/datebox_arrow.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/datebox_arrow.png diff --git a/assets/framework/easyui/themes/default/images/layout_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/layout_arrows.png similarity index 100% rename from assets/framework/easyui/themes/default/images/layout_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/layout_arrows.png diff --git a/assets/framework/easyui/themes/default/images/linkbutton_bg.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/linkbutton_bg.png similarity index 100% rename from assets/framework/easyui/themes/default/images/linkbutton_bg.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/linkbutton_bg.png diff --git a/assets/framework/easyui/themes/default/images/loading.gif b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/loading.gif similarity index 100% rename from assets/framework/easyui/themes/default/images/loading.gif rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/loading.gif diff --git a/assets/framework/easyui/themes/default/images/menu_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/menu_arrows.png similarity index 100% rename from assets/framework/easyui/themes/default/images/menu_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/menu_arrows.png diff --git a/assets/framework/easyui/themes/default/images/messager_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/messager_icons.png similarity index 100% rename from assets/framework/easyui/themes/default/images/messager_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/messager_icons.png diff --git a/assets/framework/easyui/themes/default/images/pagination_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/pagination_icons.png similarity index 100% rename from assets/framework/easyui/themes/default/images/pagination_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/pagination_icons.png diff --git a/assets/framework/easyui/themes/default/images/panel_tools.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/panel_tools.png similarity index 100% rename from assets/framework/easyui/themes/default/images/panel_tools.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/panel_tools.png diff --git a/assets/framework/easyui/themes/default/images/searchbox_button.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/searchbox_button.png similarity index 100% rename from assets/framework/easyui/themes/default/images/searchbox_button.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/searchbox_button.png diff --git a/assets/framework/easyui/themes/default/images/slider_handle.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/slider_handle.png similarity index 100% rename from assets/framework/easyui/themes/default/images/slider_handle.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/slider_handle.png diff --git a/assets/framework/easyui/themes/default/images/spinner_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/spinner_arrows.png similarity index 100% rename from assets/framework/easyui/themes/default/images/spinner_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/spinner_arrows.png diff --git a/assets/framework/easyui/themes/default/images/tabs_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/tabs_icons.png similarity index 100% rename from assets/framework/easyui/themes/default/images/tabs_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/tabs_icons.png diff --git a/assets/framework/easyui/themes/default/images/tree_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/tree_icons.png similarity index 100% rename from assets/framework/easyui/themes/default/images/tree_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/tree_icons.png diff --git a/assets/framework/easyui/themes/default/images/validatebox_warning.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/validatebox_warning.png similarity index 100% rename from assets/framework/easyui/themes/default/images/validatebox_warning.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/validatebox_warning.png diff --git a/assets/framework/easyui/themes/icon.css b/src/etcdkeeper/assets/static/framework/easyui/themes/icon.css similarity index 100% rename from assets/framework/easyui/themes/icon.css rename to src/etcdkeeper/assets/static/framework/easyui/themes/icon.css diff --git a/assets/framework/easyui/themes/icons/back.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/back.png similarity index 100% rename from assets/framework/easyui/themes/icons/back.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/back.png diff --git a/assets/framework/easyui/themes/icons/blank.gif b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/blank.gif similarity index 100% rename from assets/framework/easyui/themes/icons/blank.gif rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/blank.gif diff --git a/assets/framework/easyui/themes/icons/cancel.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/cancel.png similarity index 100% rename from assets/framework/easyui/themes/icons/cancel.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/cancel.png diff --git a/assets/framework/easyui/themes/icons/clear.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/clear.png similarity index 100% rename from assets/framework/easyui/themes/icons/clear.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/clear.png diff --git a/assets/framework/easyui/themes/icons/cut.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/cut.png similarity index 100% rename from assets/framework/easyui/themes/icons/cut.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/cut.png diff --git a/assets/framework/easyui/themes/icons/dir.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/dir.png similarity index 100% rename from assets/framework/easyui/themes/icons/dir.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/dir.png diff --git a/assets/framework/easyui/themes/icons/edit_add.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/edit_add.png similarity index 100% rename from assets/framework/easyui/themes/icons/edit_add.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/edit_add.png diff --git a/assets/framework/easyui/themes/icons/edit_remove.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/edit_remove.png similarity index 100% rename from assets/framework/easyui/themes/icons/edit_remove.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/edit_remove.png diff --git a/assets/framework/easyui/themes/icons/export.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/export.png similarity index 100% rename from assets/framework/easyui/themes/icons/export.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/export.png diff --git a/assets/framework/easyui/themes/icons/filesave.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/filesave.png similarity index 100% rename from assets/framework/easyui/themes/icons/filesave.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/filesave.png diff --git a/assets/framework/easyui/themes/icons/filter.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/filter.png similarity index 100% rename from assets/framework/easyui/themes/icons/filter.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/filter.png diff --git a/assets/framework/easyui/themes/icons/help.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/help.png similarity index 100% rename from assets/framework/easyui/themes/icons/help.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/help.png diff --git a/assets/framework/easyui/themes/icons/import.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/import.png similarity index 100% rename from assets/framework/easyui/themes/icons/import.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/import.png diff --git a/assets/framework/easyui/themes/icons/large_chart.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/large_chart.png similarity index 100% rename from assets/framework/easyui/themes/icons/large_chart.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/large_chart.png diff --git a/assets/framework/easyui/themes/icons/large_clipart.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/large_clipart.png similarity index 100% rename from assets/framework/easyui/themes/icons/large_clipart.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/large_clipart.png diff --git a/assets/framework/easyui/themes/icons/large_picture.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/large_picture.png similarity index 100% rename from assets/framework/easyui/themes/icons/large_picture.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/large_picture.png diff --git a/assets/framework/easyui/themes/icons/large_shapes.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/large_shapes.png similarity index 100% rename from assets/framework/easyui/themes/icons/large_shapes.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/large_shapes.png diff --git a/assets/framework/easyui/themes/icons/large_smartart.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/large_smartart.png similarity index 100% rename from assets/framework/easyui/themes/icons/large_smartart.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/large_smartart.png diff --git a/assets/framework/easyui/themes/icons/lock.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/lock.png similarity index 100% rename from assets/framework/easyui/themes/icons/lock.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/lock.png diff --git a/assets/framework/easyui/themes/icons/man.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/man.png similarity index 100% rename from assets/framework/easyui/themes/icons/man.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/man.png diff --git a/assets/framework/easyui/themes/icons/mini_add.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/mini_add.png similarity index 100% rename from assets/framework/easyui/themes/icons/mini_add.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/mini_add.png diff --git a/assets/framework/easyui/themes/icons/mini_edit.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/mini_edit.png similarity index 100% rename from assets/framework/easyui/themes/icons/mini_edit.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/mini_edit.png diff --git a/assets/framework/easyui/themes/icons/mini_refresh.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/mini_refresh.png similarity index 100% rename from assets/framework/easyui/themes/icons/mini_refresh.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/mini_refresh.png diff --git a/assets/framework/easyui/themes/icons/more.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/more.png similarity index 100% rename from assets/framework/easyui/themes/icons/more.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/more.png diff --git a/assets/framework/easyui/themes/icons/no.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/no.png similarity index 100% rename from assets/framework/easyui/themes/icons/no.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/no.png diff --git a/assets/framework/easyui/themes/icons/ok.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/ok.png similarity index 100% rename from assets/framework/easyui/themes/icons/ok.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/ok.png diff --git a/assets/framework/easyui/themes/icons/path.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/path.png similarity index 100% rename from assets/framework/easyui/themes/icons/path.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/path.png diff --git a/assets/framework/easyui/themes/icons/pencil.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/pencil.png similarity index 100% rename from assets/framework/easyui/themes/icons/pencil.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/pencil.png diff --git a/assets/framework/easyui/themes/icons/print.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/print.png similarity index 100% rename from assets/framework/easyui/themes/icons/print.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/print.png diff --git a/assets/framework/easyui/themes/icons/redo.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/redo.png similarity index 100% rename from assets/framework/easyui/themes/icons/redo.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/redo.png diff --git a/assets/framework/easyui/themes/icons/reload.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/reload.png similarity index 100% rename from assets/framework/easyui/themes/icons/reload.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/reload.png diff --git a/assets/framework/easyui/themes/icons/search.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/search.png similarity index 100% rename from assets/framework/easyui/themes/icons/search.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/search.png diff --git a/assets/framework/easyui/themes/icons/server.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/server.png similarity index 100% rename from assets/framework/easyui/themes/icons/server.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/server.png diff --git a/assets/framework/easyui/themes/icons/sum.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/sum.png similarity index 100% rename from assets/framework/easyui/themes/icons/sum.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/sum.png diff --git a/assets/framework/easyui/themes/icons/text.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/text.png similarity index 100% rename from assets/framework/easyui/themes/icons/text.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/text.png diff --git a/assets/framework/easyui/themes/icons/tip.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/tip.png similarity index 100% rename from assets/framework/easyui/themes/icons/tip.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/tip.png diff --git a/assets/framework/easyui/themes/icons/undo.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/undo.png similarity index 100% rename from assets/framework/easyui/themes/icons/undo.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/undo.png diff --git a/assets/framework/easyui/themes/icons/user.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/user.png similarity index 100% rename from assets/framework/easyui/themes/icons/user.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/user.png diff --git a/assets/framework/easyui/themes/metro/easyui.css b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/easyui.css similarity index 100% rename from assets/framework/easyui/themes/metro/easyui.css rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/easyui.css diff --git a/assets/framework/easyui/themes/metro/images/accordion_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/accordion_arrows.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/accordion_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/accordion_arrows.png diff --git a/assets/framework/easyui/themes/metro/images/blank.gif b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/blank.gif similarity index 100% rename from assets/framework/easyui/themes/metro/images/blank.gif rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/blank.gif diff --git a/assets/framework/easyui/themes/metro/images/calendar_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/calendar_arrows.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/calendar_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/calendar_arrows.png diff --git a/assets/framework/easyui/themes/metro/images/combo_arrow.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/combo_arrow.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/combo_arrow.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/combo_arrow.png diff --git a/assets/framework/easyui/themes/metro/images/datagrid_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/datagrid_icons.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/datagrid_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/datagrid_icons.png diff --git a/assets/framework/easyui/themes/metro/images/datebox_arrow.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/datebox_arrow.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/datebox_arrow.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/datebox_arrow.png diff --git a/assets/framework/easyui/themes/metro/images/layout_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/layout_arrows.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/layout_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/layout_arrows.png diff --git a/assets/framework/easyui/themes/metro/images/linkbutton_bg.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/linkbutton_bg.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/linkbutton_bg.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/linkbutton_bg.png diff --git a/assets/framework/easyui/themes/metro/images/loading.gif b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/loading.gif similarity index 100% rename from assets/framework/easyui/themes/metro/images/loading.gif rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/loading.gif diff --git a/assets/framework/easyui/themes/metro/images/menu_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/menu_arrows.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/menu_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/menu_arrows.png diff --git a/assets/framework/easyui/themes/metro/images/messager_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/messager_icons.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/messager_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/messager_icons.png diff --git a/assets/framework/easyui/themes/metro/images/pagination_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/pagination_icons.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/pagination_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/pagination_icons.png diff --git a/assets/framework/easyui/themes/metro/images/panel_tools.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/panel_tools.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/panel_tools.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/panel_tools.png diff --git a/assets/framework/easyui/themes/metro/images/searchbox_button.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/searchbox_button.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/searchbox_button.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/searchbox_button.png diff --git a/assets/framework/easyui/themes/metro/images/slider_handle.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/slider_handle.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/slider_handle.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/slider_handle.png diff --git a/assets/framework/easyui/themes/metro/images/spinner_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/spinner_arrows.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/spinner_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/spinner_arrows.png diff --git a/assets/framework/easyui/themes/metro/images/tabs_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/tabs_icons.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/tabs_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/tabs_icons.png diff --git a/assets/framework/easyui/themes/metro/images/tree_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/tree_icons.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/tree_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/tree_icons.png diff --git a/assets/framework/easyui/themes/metro/images/validatebox_warning.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/validatebox_warning.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/validatebox_warning.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/validatebox_warning.png diff --git a/assets/framework/favicon.ico b/src/etcdkeeper/assets/static/framework/favicon.ico similarity index 100% rename from assets/framework/favicon.ico rename to src/etcdkeeper/assets/static/framework/favicon.ico diff --git a/assets/framework/jquery/jquery.json-2.2.js b/src/etcdkeeper/assets/static/framework/jquery/jquery.json-2.2.js similarity index 100% rename from assets/framework/jquery/jquery.json-2.2.js rename to src/etcdkeeper/assets/static/framework/jquery/jquery.json-2.2.js diff --git a/assets/framework/js.cookie-2.1.4.min.js b/src/etcdkeeper/assets/static/framework/js.cookie-2.1.4.min.js similarity index 100% rename from assets/framework/js.cookie-2.1.4.min.js rename to src/etcdkeeper/assets/static/framework/js.cookie-2.1.4.min.js diff --git a/assets/framework/logo.png b/src/etcdkeeper/assets/static/framework/logo.png similarity index 100% rename from assets/framework/logo.png rename to src/etcdkeeper/assets/static/framework/logo.png diff --git a/assets/etcdkeeper/index.html b/src/etcdkeeper/assets/templates/etcdkeeper/index.html similarity index 98% rename from assets/etcdkeeper/index.html rename to src/etcdkeeper/assets/templates/etcdkeeper/index.html index fbd7f5f..47f4cfe 100644 --- a/assets/etcdkeeper/index.html +++ b/src/etcdkeeper/assets/templates/etcdkeeper/index.html @@ -120,7 +120,7 @@

ETCD Keeper 0.7.6 -
+
text
toml
ini
@@ -146,18 +146,18 @@

min; i-- { + for _, a := range all[i] { + for _, pa := range all[i-1] { + if i == 2 { // The last is root + pa["nodes"] = append(pa["nodes"].([]map[string]interface{}), a) + pa["dir"] = true + } else { + if strings.HasPrefix(a["key"].(string), pa["key"].(string)+ek.config.separator) { + pa["nodes"] = append(pa["nodes"].([]map[string]interface{}), a) + pa["dir"] = true + } + } + } + } + } + + for _, n := range all[min] { + if n["key"] == key { + nodesSort(n) + data["node"] = n + break + } + } + } + + var dataByte []byte + var err error + if dataByte, err = json.Marshal(data); err != nil { + io.WriteString(w, err.Error()) + } else { + io.WriteString(w, string(dataByte)) + } +} + +func nodesSort(node map[string]interface{}) { + if v, ok := node["nodes"]; ok && v != nil { + a := v.([]map[string]interface{}) + if len(a) != 0 { + for i := 0; i < len(a)-1; i++ { + nodesSort(a[i]) + for j := i + 1; j < len(a); j++ { + if a[j]["key"].(string) < a[i]["key"].(string) { + a[i], a[j] = a[j], a[i] + } + } + } + nodesSort(a[len(a)-1]) + } + } +} + +func (ek *Etcdkeeper) DelV2(w http.ResponseWriter, r *http.Request) { + key := r.FormValue("key") + dir := r.FormValue("dir") + log.Println("DELETE", "v2", key) + + kapi := client.NewKeysAPI(ek.getClientV2(w, r)) + + isDir, _ := strconv.ParseBool(dir) + if isDir { + if _, err := kapi.Delete(context.Background(), key, &client.DeleteOptions{Recursive: true, Dir: true}); err != nil { + io.WriteString(w, err.Error()) + return + } + } else { + if _, err := kapi.Delete(context.Background(), key, nil); err != nil { + io.WriteString(w, err.Error()) + return + } + } + + io.WriteString(w, "ok") +} + +func (ek *Etcdkeeper) GetPathV2(w http.ResponseWriter, r *http.Request) { + ek.GetV2(w, r) +} + +func (ek *Etcdkeeper) getClientV2(w http.ResponseWriter, r *http.Request) client.Client { + sess := ek.sessmgr.SessionStart(w, r) + v := sess.Get("uinfov2") + if v != nil { + uinfo := v.(*userInfo) + c, _ := ek.newClientV2(uinfo) + return c + } + return nil +} + +func (ek *Etcdkeeper) newClientV2(uinfo *userInfo) (client.Client, error) { + cfg := client.Config{ + Endpoints: []string{uinfo.host}, + HeaderTimeoutPerRequest: time.Second * time.Duration(ek.config.connectTimeout), + } + if ek.config.useAuth { + cfg.Username = uinfo.uname + cfg.Password = uinfo.passwd + } + + c, err := client.New(cfg) + if err != nil { + return nil, err + } + return c, nil +} + +func (ek *Etcdkeeper) getPermissionPrefixV2(host, uname, key string) ([][]string, error) { + if !ek.config.useAuth { + return [][]string{{key, "p"}}, nil // No auth return all + } else { + if uname == "root" { + return [][]string{{key, "p"}}, nil + } + + if !strings.HasPrefix(host, "http://") { + host = "http://" + host + } + rootUser := ek.rootUsersV2[host] + rootCli, err := ek.newClientV2(rootUser) + if err != nil { + return nil, err + } + rootUserKapi := client.NewAuthUserAPI(rootCli) + rootRoleKapi := client.NewAuthRoleAPI(rootCli) + + if users, err := rootUserKapi.ListUsers(context.Background()); err != nil { + return nil, err + } else { + // Find user permissions + set := make(map[string]string) + for _, u := range users { + if u == uname { + user, err := rootUserKapi.GetUser(context.Background(), u) + if err != nil { + return nil, err + } + for _, r := range user.Roles { + role, err := rootRoleKapi.GetRole(context.Background(), r) + if err != nil { + return nil, err + } + for _, ks := range role.Permissions.KV.Read { + var k string + if strings.HasSuffix(ks, "*") { + k = ks[:len(ks)-1] + set[k] = "p" + } else if strings.HasSuffix(ks, "/*") { + k = ks[:len(ks)-2] + set[k] = "c" + } else { + if _, ok := set[ks]; !ok { + set[ks] = "" + } + } + } + } + break + } + } + var pers [][]string + var ks []string + for k := range set { + ks = append(ks, k) + } + sort.Strings(ks) + for _, k := range ks { + pers = append(pers, []string{k, set[k]}) + } + return pers, nil + } + } +} + +func (ek *Etcdkeeper) getInfoV2(host string) map[string]string { + if !strings.HasPrefix(host, "http://") { + host = "http://" + host + } + info := make(map[string]string) + uinfo, ok := ek.rootUsersV2[host] + if ok { + rootClient, err := ek.newClientV2(uinfo) + if err != nil { + log.Println(err) + return info + } + ver, err := rootClient.GetVersion(context.Background()) + if err != nil { + log.Fatal(err) + } + memberKapi := client.NewMembersAPI(rootClient) + member, err := memberKapi.Leader(context.Background()) + if err != nil { + log.Fatal(err) + } + info["version"] = ver.Server + info["name"] = member.Name + info["size"] = "unknow" // FIXME: How get? + } + return info +} + +func (ek *Etcdkeeper) getNode(node *client.Node, selKey string, all map[int][]map[string]interface{}, min, max int) int { + keys := strings.Split(node.Key, ek.config.separator) // /foo/bar + if len(keys) < min && strings.HasPrefix(node.Key, selKey) { + return max + } + for i := range keys { // ["", "foo", "bar"] + k := strings.Join(keys[0:i+1], ek.config.separator) + if k == "" { + continue + } + nodeMap := map[string]interface{}{"key": k, "dir": true, "nodes": make([]map[string]interface{}, 0)} + if k == node.Key { + nodeMap["value"] = node.Value + nodeMap["dir"] = node.Dir + nodeMap["ttl"] = node.TTL + nodeMap["createdIndex"] = node.CreatedIndex + nodeMap["modifiedIndex"] = node.ModifiedIndex + } + keylevel := len(strings.Split(k, ek.config.separator)) + if keylevel > max { + max = keylevel + } + + if _, ok := all[keylevel]; !ok { + all[keylevel] = make([]map[string]interface{}, 0) + } + var isExist bool + for _, n := range all[keylevel] { + if n["key"].(string) == k { + isExist = true + } + } + if !isExist { + all[keylevel] = append(all[keylevel], nodeMap) + } + } + + if len(node.Nodes) != 0 { + for _, n := range node.Nodes { + max = ek.getNode(n, selKey, all, min, max) + } + } + return max +} diff --git a/src/etcdkeeper/internal/etcdkeeper/v3.go b/src/etcdkeeper/internal/etcdkeeper/v3.go new file mode 100644 index 0000000..04aebd2 --- /dev/null +++ b/src/etcdkeeper/internal/etcdkeeper/v3.go @@ -0,0 +1,543 @@ +package etcdkeeper + +import ( + "context" + "crypto/tls" + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "strconv" + "strings" + "time" + + "github.com/coreos/etcd/clientv3" + "github.com/coreos/etcd/pkg/transport" + "google.golang.org/grpc" +) + +// v3 api +func (ek *Etcdkeeper) Connect(w http.ResponseWriter, r *http.Request) { + ek.mu.Lock() + defer ek.mu.Unlock() + sess := ek.sessmgr.SessionStart(w, r) + host := r.FormValue("host") + uname := r.FormValue("uname") + passwd := r.FormValue("passwd") + + if ek.config.useAuth { + if _, ok := ek.rootUsers[host]; !ok && uname != "root" { // no root user + b, _ := json.Marshal(map[string]interface{}{"status": "root"}) + io.WriteString(w, string(b)) + return + } + if uname == "" || passwd == "" { + b, _ := json.Marshal(map[string]interface{}{"status": "login"}) + io.WriteString(w, string(b)) + return + } + } + + if uinfo, ok := sess.Get("uinfo").(*userInfo); ok { + if host == uinfo.host && uname == uinfo.uname && passwd == uinfo.passwd { + info := ek.getInfo(host) + b, _ := json.Marshal(map[string]interface{}{"status": "running", "info": info}) + io.WriteString(w, string(b)) + return + } + } + + uinfo := &userInfo{host: host, uname: uname, passwd: passwd} + c, err := ek.newClient(uinfo) + if err != nil { + log.Println(r.Method, "v3", "connect fail.") + b, _ := json.Marshal(map[string]interface{}{"status": "error", "message": err.Error()}) + io.WriteString(w, string(b)) + return + } + defer c.Close() + _ = sess.Set("uinfo", uinfo) + + if ek.config.useAuth { + if uname == "root" { + ek.rootUsers[host] = uinfo + } + } else { + ek.rootUsers[host] = uinfo + } + log.Println(r.Method, "v3", "connect success.") + info := ek.getInfo(host) + b, _ := json.Marshal(map[string]interface{}{"status": "running", "info": info}) + io.WriteString(w, string(b)) +} + +func (ek *Etcdkeeper) Put(w http.ResponseWriter, r *http.Request) { + cli := ek.getClient(w, r) + defer cli.Close() + key := r.FormValue("key") + value := r.FormValue("value") + ttl := r.FormValue("ttl") + log.Println("PUT", "v3", key) + + var err error + data := make(map[string]interface{}) + if ttl != "" { + var sec int64 + sec, err = strconv.ParseInt(ttl, 10, 64) + if err != nil { + log.Println(err.Error()) + } + var leaseResp *clientv3.LeaseGrantResponse + leaseResp, err = cli.Grant(context.TODO(), sec) + if err == nil && leaseResp != nil { + _, err = cli.Put(context.Background(), key, value, clientv3.WithLease(leaseResp.ID)) + } + } else { + _, err = cli.Put(context.Background(), key, value) + } + if err != nil { + data["errorCode"] = 500 + data["message"] = err.Error() + } else { + if resp, err := cli.Get(context.Background(), key); err != nil { + data["errorCode"] = 500 + data["errorCode"] = err.Error() + } else { + if resp.Count > 0 { + kv := resp.Kvs[0] + node := make(map[string]interface{}) + node["key"] = string(kv.Key) + node["value"] = string(kv.Value) + node["dir"] = false + node["ttl"] = ek.getTTL(cli, kv.Lease) + node["createdIndex"] = kv.CreateRevision + node["modifiedIndex"] = kv.ModRevision + data["node"] = node + } + } + } + + var dataByte []byte + if dataByte, err = json.Marshal(data); err != nil { + io.WriteString(w, err.Error()) + } else { + io.WriteString(w, string(dataByte)) + } +} + +func (ek *Etcdkeeper) Get(w http.ResponseWriter, r *http.Request) { + data := make(map[string]interface{}) + key := r.FormValue("key") + log.Println("GET", "v3", key) + + var cli *clientv3.Client + sess := ek.sessmgr.SessionStart(w, r) + v := sess.Get("uinfo") + var uinfo *userInfo + + if v != nil { + uinfo = v.(*userInfo) + cli, _ = ek.newClient(uinfo) + defer cli.Close() + + permissions, e := ek.getPermissionPrefix(uinfo.host, uinfo.uname, key) + if e != nil { + io.WriteString(w, e.Error()) + return + } + if r.FormValue("prefix") == "true" { + pnode := make(map[string]interface{}) + pnode["key"] = key + pnode["nodes"] = make([]map[string]interface{}, 0) + for _, p := range permissions { + var ( + resp *clientv3.GetResponse + err error + ) + if p[1] != "" { + prefixKey := p[0] + if p[0] == "/" { + prefixKey = "" + } + resp, err = cli.Get(context.Background(), prefixKey, clientv3.WithPrefix()) + } else { + resp, err = cli.Get(context.Background(), p[0]) + } + if err != nil { + data["errorCode"] = 500 + data["message"] = err.Error() + } else { + for _, kv := range resp.Kvs { + node := make(map[string]interface{}) + node["key"] = string(kv.Key) + node["value"] = string(kv.Value) + node["dir"] = false + if key == string(kv.Key) { + node["ttl"] = ek.getTTL(cli, kv.Lease) + } else { + node["ttl"] = 0 + } + node["createdIndex"] = kv.CreateRevision + node["modifiedIndex"] = kv.ModRevision + nodes := pnode["nodes"].([]map[string]interface{}) + pnode["nodes"] = append(nodes, node) + } + } + } + data["node"] = pnode + } else { + if resp, err := cli.Get(context.Background(), key); err != nil { + data["errorCode"] = 500 + data["message"] = err.Error() + } else { + if resp.Count > 0 { + kv := resp.Kvs[0] + node := make(map[string]interface{}) + node["key"] = string(kv.Key) + node["value"] = string(kv.Value) + node["dir"] = false + node["ttl"] = ek.getTTL(cli, kv.Lease) + node["createdIndex"] = kv.CreateRevision + node["modifiedIndex"] = kv.ModRevision + data["node"] = node + } else { + data["errorCode"] = 500 + data["message"] = "The node does not exist." + } + } + } + } + + var dataByte []byte + var err error + if dataByte, err = json.Marshal(data); err != nil { + io.WriteString(w, err.Error()) + } else { + io.WriteString(w, string(dataByte)) + } +} + +func (ek *Etcdkeeper) GetPath(w http.ResponseWriter, r *http.Request) { + originKey := r.FormValue("key") + log.Println("GET", "v3", originKey) + var ( + data = make(map[string]interface{}) + /* + {1:["/"], 2:["/foo", "/foo2"], 3:["/foo/bar", "/foo2/bar"], 4:["/foo/bar/test"]} + */ + all = make(map[int][]map[string]interface{}) + min int + max int + //prefixKey string + ) + + var cli *clientv3.Client + sess := ek.sessmgr.SessionStart(w, r) + v := sess.Get("uinfo") + var uinfo *userInfo + if v != nil { + uinfo = v.(*userInfo) + cli, _ = ek.newClient(uinfo) + defer cli.Close() + + permissions, e := ek.getPermissionPrefix(uinfo.host, uinfo.uname, originKey) + if e != nil { + io.WriteString(w, e.Error()) + return + } + + // parent + var ( + presp *clientv3.GetResponse + err error + ) + if originKey != ek.config.separator { + presp, err = cli.Get(context.Background(), originKey) + if err != nil { + data["errorCode"] = 500 + data["message"] = err.Error() + dataByte, _ := json.Marshal(data) + io.WriteString(w, string(dataByte)) + return + } + } + if originKey == ek.config.separator { + min = 1 + //prefixKey = separator + } else { + min = len(strings.Split(originKey, ek.config.separator)) + //prefixKey = originKey + } + max = min + all[min] = []map[string]interface{}{{"key": originKey}} + if presp != nil && presp.Count != 0 { + all[min][0]["value"] = string(presp.Kvs[0].Value) + all[min][0]["ttl"] = ek.getTTL(cli, presp.Kvs[0].Lease) + all[min][0]["createdIndex"] = presp.Kvs[0].CreateRevision + all[min][0]["modifiedIndex"] = presp.Kvs[0].ModRevision + } + all[min][0]["nodes"] = make([]map[string]interface{}, 0) + + for _, p := range permissions { + key, rangeEnd := p[0], p[1] + //child + var resp *clientv3.GetResponse + if rangeEnd != "" { + resp, err = cli.Get(context.Background(), key, clientv3.WithPrefix(), clientv3.WithSort(clientv3.SortByKey, clientv3.SortAscend)) + } else { + resp, err = cli.Get(context.Background(), key, clientv3.WithSort(clientv3.SortByKey, clientv3.SortAscend)) + } + if err != nil { + data["errorCode"] = 500 + data["message"] = err.Error() + dataByte, _ := json.Marshal(data) + io.WriteString(w, string(dataByte)) + return + } + + for _, kv := range resp.Kvs { + if string(kv.Key) == ek.config.separator { + continue + } + keys := strings.Split(string(kv.Key), ek.config.separator) // /foo/bar + for i := range keys { // ["", "foo", "bar"] + k := strings.Join(keys[0:i+1], ek.config.separator) + if k == "" { + continue + } + node := map[string]interface{}{"key": k} + if node["key"].(string) == string(kv.Key) { + node["value"] = string(kv.Value) + if key == string(kv.Key) { + node["ttl"] = ek.getTTL(cli, kv.Lease) + } else { + node["ttl"] = 0 + } + node["createdIndex"] = kv.CreateRevision + node["modifiedIndex"] = kv.ModRevision + } + level := len(strings.Split(k, ek.config.separator)) + if level > max { + max = level + } + + if _, ok := all[level]; !ok { + all[level] = make([]map[string]interface{}, 0) + } + levelNodes := all[level] + var isExist bool + for _, n := range levelNodes { + if n["key"].(string) == k { + isExist = true + } + } + if !isExist { + node["nodes"] = make([]map[string]interface{}, 0) + all[level] = append(all[level], node) + } + } + } + } + + // parent-child mapping + for i := max; i > min; i-- { + for _, a := range all[i] { + for _, pa := range all[i-1] { + if i == 2 { + pa["nodes"] = append(pa["nodes"].([]map[string]interface{}), a) + pa["dir"] = true + } else { + if strings.HasPrefix(a["key"].(string), pa["key"].(string)+ek.config.separator) { + pa["nodes"] = append(pa["nodes"].([]map[string]interface{}), a) + pa["dir"] = true + } + } + } + } + } + } + data = all[min][0] + if dataByte, err := json.Marshal(map[string]interface{}{"node": data}); err != nil { + io.WriteString(w, err.Error()) + } else { + io.WriteString(w, string(dataByte)) + } +} + +func (ek *Etcdkeeper) Del(w http.ResponseWriter, r *http.Request) { + cli := ek.getClient(w, r) + defer cli.Close() + key := r.FormValue("key") + dir := r.FormValue("dir") + log.Println("DELETE", "v3", key) + + if _, err := cli.Delete(context.Background(), key); err != nil { + io.WriteString(w, err.Error()) + return + } + + if dir == "true" { + if _, err := cli.Delete(context.Background(), key+ek.config.separator, clientv3.WithPrefix()); err != nil { + io.WriteString(w, err.Error()) + return + } + } + io.WriteString(w, "ok") +} + +func (ek *Etcdkeeper) getTTL(cli *clientv3.Client, lease int64) int64 { + resp, err := cli.Lease.TimeToLive(context.Background(), clientv3.LeaseID(lease)) + if err != nil { + return 0 + } + if resp.TTL == -1 { + return 0 + } + return resp.TTL +} + +func (ek *Etcdkeeper) getClient(w http.ResponseWriter, r *http.Request) *clientv3.Client { + sess := ek.sessmgr.SessionStart(w, r) + v := sess.Get("uinfo") + if v != nil { + uinfo := v.(*userInfo) + c, _ := ek.newClient(uinfo) + return c + } + return nil +} + +func (ek *Etcdkeeper) newClient(uinfo *userInfo) (*clientv3.Client, error) { + endpoints := []string{uinfo.host} + var err error + + // use tls if usetls is true + var tlsConfig *tls.Config + if ek.config.usetls { + tlsInfo := transport.TLSInfo{ + CertFile: ek.config.cert, + KeyFile: ek.config.keyfile, + TrustedCAFile: ek.config.cacert, + } + tlsConfig, err = tlsInfo.ClientConfig() + if err != nil { + log.Println(err.Error()) + } + } + + conf := clientv3.Config{ + Endpoints: endpoints, + TLS: tlsConfig, + DialTimeout: time.Second * time.Duration(ek.config.connectTimeout), + DialOptions: []grpc.DialOption{grpc.WithBlock(), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(ek.config.grpcMaxMsgSize))}, + } + if ek.config.useAuth { + conf.Username = uinfo.uname + conf.Password = uinfo.passwd + } + + var c *clientv3.Client + c, err = clientv3.New(conf) + if err != nil { + return nil, err + } + return c, nil +} + +func (ek *Etcdkeeper) getPermissionPrefix(host, uname, key string) ([][]string, error) { + if !ek.config.useAuth { + return [][]string{{key, "p"}}, nil // No auth return all + } else { + if uname == "root" { + return [][]string{{key, "p"}}, nil + } + rootUser := ek.rootUsers[host] + rootCli, err := ek.newClient(rootUser) + if err != nil { + return nil, err + } + defer rootCli.Close() + + if resp, err := rootCli.UserList(context.Background()); err != nil { + return nil, err + } else { + // Find user permissions + set := make(map[string]string) + for _, u := range resp.Users { + if u == uname { + ur, err := rootCli.UserGet(context.Background(), u) + if err != nil { + return nil, err + } + for _, r := range ur.Roles { + rr, err := rootCli.RoleGet(context.Background(), r) + if err != nil { + return nil, err + } + for _, p := range rr.Perm { + set[string(p.Key)] = string(p.RangeEnd) + } + } + break + } + } + var pers [][]string + for k, v := range set { + pers = append(pers, []string{k, v}) + } + return pers, nil + } + } +} + +func (ek *Etcdkeeper) getInfo(host string) map[string]string { + info := make(map[string]string) + uinfo := ek.rootUsers[host] + rootClient, err := ek.newClient(uinfo) + if err != nil { + log.Println(err) + return info + } + defer rootClient.Close() + + status, err := rootClient.Status(context.Background(), host) + if err != nil { + log.Fatal(err) + } + mems, err := rootClient.MemberList(context.Background()) + if err != nil { + log.Fatal(err) + } + kb := 1024 + mb := kb * 1024 + gb := mb * 1024 + var sizeStr string + for _, m := range mems.Members { + if m.ID == status.Leader { + info["version"] = status.Version + gn, rem1 := ek.size(int(status.DbSize), gb) + mn, rem2 := ek.size(rem1, mb) + kn, bn := ek.size(rem2, kb) + if sizeStr != "" { + sizeStr += " " + } + if gn > 0 { + info["size"] = fmt.Sprintf("%dG", gn) + } else { + if mn > 0 { + info["size"] = fmt.Sprintf("%dM", mn) + } else { + if kn > 0 { + info["size"] = fmt.Sprintf("%dK", kn) + } else { + info["size"] = fmt.Sprintf("%dByte", bn) + } + } + } + info["name"] = m.GetName() + break + } + } + return info +} diff --git a/src/etcdkeeper/internal/responseWriter.go b/src/etcdkeeper/internal/responseWriter.go new file mode 100644 index 0000000..5a76189 --- /dev/null +++ b/src/etcdkeeper/internal/responseWriter.go @@ -0,0 +1,30 @@ +package internal + +import "net/http" + +//CompletableResponseWriter extend http.ResponseWriter with completed state +// a ResponseWriter is Completed when method Write or WriteHeader have been called +type CompletableResponseWriter struct { + http.ResponseWriter + done bool +} + +//New initialize CompletableResponseWriter with non completed http.ResponseWriter +func NewCompletableResponseWriter(w http.ResponseWriter) *CompletableResponseWriter { + return &CompletableResponseWriter{w, false} +} + +func (w *CompletableResponseWriter) WriteHeader(status int) { + w.done = true + w.ResponseWriter.WriteHeader(status) +} + +func (w *CompletableResponseWriter) Write(b []byte) (int, error) { + w.done = true + return w.ResponseWriter.Write(b) +} + +//IsCompleted return true if response writer write or writeheader have been called +func (w *CompletableResponseWriter) IsCompleted() bool { + return w.done +} diff --git a/src/etcdkeeper/internal/template.go b/src/etcdkeeper/internal/template.go new file mode 100644 index 0000000..eaf9176 --- /dev/null +++ b/src/etcdkeeper/internal/template.go @@ -0,0 +1,62 @@ +package internal + +import ( + "io/fs" + "log" + "net/http" + "path" + "path/filepath" + "strings" + "text/template" +) + +type templateHandler struct { + config interface{} + templates *template.Template +} + +func NewTemplateServer(rootDir fs.FS, templateData interface{}) *templateHandler { + + root := template.New("") + err := fs.WalkDir(rootDir, ".", func(currentPath string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + if !d.IsDir() { + b, err := fs.ReadFile(rootDir, currentPath) + if err != nil { + return err + } + + if len(currentPath) > 0 && !strings.HasPrefix(currentPath, "/") { + currentPath = "/" + currentPath + } + _, err = root.New(currentPath).Parse(string(b)) + if err != nil { + return err + } + log.Printf("template %q parsed\n", currentPath) + } + return nil + }) + + return &templateHandler{templateData, template.Must(root, err)} +} + +// ServeHTTP check and serve template content if exist +func (h *templateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // this clean '..' '.' and trailing slash '/' + cleanedURL := path.Clean(r.URL.Path) + + ext := path.Ext(cleanedURL) + // if no extension on name this is a folder + if ext == "" { + cleanedURL = filepath.Join(cleanedURL, "/index.html") + } + + err := h.templates.ExecuteTemplate(w, cleanedURL, h.config) + if err == nil { + log.Printf("GET : %s\n", cleanedURL) + } +} diff --git a/src/etcdkeeper/main.go b/src/etcdkeeper/main.go index d89b0f8..029494b 100644 --- a/src/etcdkeeper/main.go +++ b/src/etcdkeeper/main.go @@ -1,105 +1,81 @@ package main import ( - "context" - "crypto/tls" - "encoding/json" - "etcdkeeper/session" - _ "etcdkeeper/session/providers/memory" + "embed" + "etcdkeeper/internal" + "etcdkeeper/internal/etcdkeeper" "flag" - "fmt" - "github.com/coreos/etcd/client" - "github.com/coreos/etcd/clientv3" - "github.com/coreos/etcd/pkg/transport" - "io" + "io/fs" "log" "net/http" "os" - "path/filepath" - "sort" "strconv" - "strings" - "sync" - "time" - "google.golang.org/grpc" ) -var ( - sep = flag.String("sep", "/", "separator") - separator = "" - usetls = flag.Bool("usetls", false, "use tls") - cacert = flag.String("cacert", "", "verify certificates of TLS-enabled secure servers using this CA bundle (v3)") - cert = flag.String("cert", "", "identify secure client using this TLS certificate file (v3)") - keyfile = flag.String("key", "", "identify secure client using this TLS key file (v3)") - useAuth = flag.Bool("auth", false, "use auth") - connectTimeout = flag.Int("timeout", 5, "ETCD client connect timeout") - rootUsers = make(map[string]*userInfo) // host:rootUser - rootUesrsV2 = make(map[string]*userInfo) // host:rootUser - - sessmgr *session.Manager - mu sync.Mutex -) - -type userInfo struct { - host string - uname string - passwd string -} +//go:embed assets/* +var assets embed.FS func main() { - host := flag.String("h","0.0.0.0","host name or ip address") - port := flag.Int("p", 8080, "port") + etcdkeeperConfig := &etcdkeeper.EtcdkeeperConfig{} + etcdkeeperConfig.ParseFlag() + + host := flag.String("h", "0.0.0.0", "etcdkeeper listen hostname or ip address") + port := flag.Int("p", 8080, "etcdkeeper listen port") flag.CommandLine.Parse(os.Args[1:]) - separator = *sep + + etcdk := etcdkeeper.NewEtcdKeeper(etcdkeeperConfig) middleware := func(fns ...func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { + // avoid calling superfluous write on ResponseWriter + cw := internal.NewCompletableResponseWriter(w) + for _, fn := range fns { - fn(w, r) + if cw.IsCompleted() { + break + } + fn(cw, r) } } } // v2 //http.HandleFunc(*name, v2request) - http.HandleFunc("/v2/separator", middleware(nothing, getSeparator)) - http.HandleFunc("/v2/connect", middleware(nothing, connectV2)) - http.HandleFunc("/v2/put", middleware(nothing, putV2)) - http.HandleFunc("/v2/get", middleware(nothing, getV2)) - http.HandleFunc("/v2/delete", middleware(nothing, delV2)) + http.HandleFunc("/v2/separator", middleware(nothing, etcdk.GetSeparator)) + http.HandleFunc("/v2/connect", middleware(nothing, etcdk.ConnectV2)) + http.HandleFunc("/v2/put", middleware(nothing, etcdk.PutV2)) + http.HandleFunc("/v2/get", middleware(nothing, etcdk.GetV2)) + http.HandleFunc("/v2/delete", middleware(nothing, etcdk.DelV2)) // dirctory mode - http.HandleFunc("/v2/getpath", middleware(nothing, getPathV2)) + http.HandleFunc("/v2/getpath", middleware(nothing, etcdk.GetPathV2)) // v3 - http.HandleFunc("/v3/separator", middleware(nothing, getSeparator)) - http.HandleFunc("/v3/connect", middleware(nothing, connect)) - http.HandleFunc("/v3/put", middleware(nothing, put)) - http.HandleFunc("/v3/get", middleware(nothing, get)) - http.HandleFunc("/v3/delete", middleware(nothing, del)) + http.HandleFunc("/v3/separator", middleware(nothing, etcdk.GetSeparator)) + http.HandleFunc("/v3/connect", middleware(nothing, etcdk.Connect)) + http.HandleFunc("/v3/put", middleware(nothing, etcdk.Put)) + http.HandleFunc("/v3/get", middleware(nothing, etcdk.Get)) + http.HandleFunc("/v3/delete", middleware(nothing, etcdk.Del)) // dirctory mode - http.HandleFunc("/v3/getpath", middleware(nothing, getPath)) + http.HandleFunc("/v3/getpath", middleware(nothing, etcdk.GetPath)) - wd, err := os.Executable() + // static directory server + staticFS, err := fs.Sub(assets, "assets/static") if err != nil { - log.Fatal(err) + log.Fatalf("Fail to load static assets resource directory : %v", err) } - rootPath := filepath.Dir(wd) - - // Session management - sessmgr, err = session.NewManager("memory", "_etcdkeeper_session", 86400) + staticHandler := http.FileServer(http.FS(staticFS)) + templateFS, err := fs.Sub(assets, "assets/templates") if err != nil { - log.Fatal(err) + log.Fatalf("Fail to load templates assets resource directory : %v", err) } - time.AfterFunc(86400*time.Second, func() { - sessmgr.GC() - }) - //log.Println(http.Dir(rootPath + "/assets")) + templateHandler := internal.NewTemplateServer(templateFS, etcdkeeperConfig) - http.Handle("/", http.FileServer(http.Dir(rootPath + "/assets"))) // view static directory + http.HandleFunc("/", middleware(templateHandler.ServeHTTP, staticHandler.ServeHTTP)) + // listening log.Printf("listening on %s:%d\n", *host, *port) - err = http.ListenAndServe(*host + ":" + strconv.Itoa(*port), nil) + err = http.ListenAndServe(*host+":"+strconv.Itoa(*port), nil) if err != nil { log.Fatal(err) } @@ -108,986 +84,3 @@ func main() { func nothing(_ http.ResponseWriter, _ *http.Request) { // Nothing } - - -//func v2request(w http.ResponseWriter, r *http.Request){ -// if err := r.ParseForm(); err != nil { -// log.Println(err.Error()) -// } -// log.Println(r.Method, "v2", r.FormValue("url"), r.PostForm.Encode()) -// -// body := strings.NewReader(r.PostForm.Encode()) -// req, err := http.NewRequest(r.Method, r.Form.Get("url"), body) -// if err != nil { -// io.WriteString(w, err.Error()) -// return -// } -// req.Header.Set("Content-Type", "application/x-www-form-urlencoded") -// client := &http.Client{Timeout: 10*time.Second} // important!!! -// resp, err := client.Do(req) -// if err != nil { -// io.WriteString(w, err.Error()) -// }else { -// result, err := ioutil.ReadAll(resp.Body) -// if err != nil { -// io.WriteString(w, "Get data failed: " + err.Error()) -// } else { -// io.WriteString(w, string(result)) -// } -// } -//} - -// v2 api -func connectV2(w http.ResponseWriter, r *http.Request) { - mu.Lock() - defer mu.Unlock() - sess := sessmgr.SessionStart(w, r) - host := strings.TrimSpace(r.FormValue("host")) - uname := r.FormValue("uname") - passwd := r.FormValue("passwd") - if !strings.HasPrefix(host, "http") { - host = "http://" + host - } - - if *useAuth { - _, ok := rootUesrsV2[host] - if !ok && uname != "root" { - b, _ := json.Marshal(map[string]interface{}{"status":"root"}) - io.WriteString(w, string(b)) - return - } - if uname == "" || passwd == "" { - b, _ := json.Marshal(map[string]interface{}{"status":"login"}) - io.WriteString(w, string(b)) - return - } - } - - if uinfo, ok := sess.Get("uinfov2").(*userInfo); ok { - if host == uinfo.host && uname == uinfo.uname && passwd == uinfo.passwd { - info := getInfoV2(host) - b, _ := json.Marshal(map[string]interface{}{"status":"running", "info":info}) - io.WriteString(w, string(b)) - return - } - } - - uinfo := &userInfo{host:host, uname:uname, passwd:passwd} - _, err := newClientV2(uinfo) - if err != nil { - log.Println(r.Method, "v2", "connect fail.") - b, _ := json.Marshal(map[string]interface{}{"status":"error", "message":err.Error()}) - io.WriteString(w, string(b)) - return - } - _ = sess.Set("uinfov2", uinfo) - - if *useAuth { - if uname == "root" { - rootUesrsV2[host] = uinfo - } - } else { - rootUesrsV2[host] = uinfo - } - log.Println(r.Method, "v2", "connect success.") - info := getInfoV2(host) - b, _ := json.Marshal(map[string]interface{}{"status":"running", "info":info}) - io.WriteString(w, string(b)) -} - -func putV2(w http.ResponseWriter, r *http.Request) { - key := r.FormValue("key") - value := r.FormValue("value") - ttl := r.FormValue("ttl") - dir := r.FormValue("dir") - log.Println("PUT", "v2", key) - - kapi := client.NewKeysAPI(getClientV2(w, r)) - - var isDir bool - if dir != "" { - isDir, _ = strconv.ParseBool(dir) - } - var err error - data := make(map[string]interface{}) - if ttl != "" { - var sec int64 - sec, err = strconv.ParseInt(ttl, 10, 64) - if err != nil { - log.Println(err.Error()) - } - _, err = kapi.Set(context.Background(), key, value, &client.SetOptions{TTL:time.Duration(sec)*time.Second, Dir:isDir}) - } else { - _, err = kapi.Set(context.Background(), key, value, &client.SetOptions{Dir:isDir}) - } - if err != nil { - data["errorCode"] = 500 - data["message"] = err.Error() - } else { - if resp, err := kapi.Get(context.Background(), key, &client.GetOptions{Recursive:true, Sort:true}); err != nil { - data["errorCode"] = err.Error() - } else { - if resp.Node != nil { - node := make(map[string]interface{}) - node["key"] = resp.Node.Key - node["value"] = resp.Node.Value - node["dir"] = resp.Node.Dir - node["ttl"] = resp.Node.TTL - node["createdIndex"] = resp.Node.CreatedIndex - node["modifiedIndex"] = resp.Node.ModifiedIndex - data["node"] = node - } - } - } - - var dataByte []byte - if dataByte, err = json.Marshal(data);err != nil { - io.WriteString(w, err.Error()) - } else { - io.WriteString(w, string(dataByte)) - } -} - -func getV2(w http.ResponseWriter, r *http.Request) { - key := r.FormValue("key") - data := make(map[string]interface{}) - log.Println("GET", "v2", key) - - var cli client.Client - sess := sessmgr.SessionStart(w, r) - v := sess.Get("uinfov2") - var uinfo *userInfo - if v != nil { - uinfo = v.(*userInfo) - cli, _ = newClientV2(uinfo) - kapi := client.NewKeysAPI(cli) - - var permissions [][]string - if r.FormValue("prefix") == "true" { - var e error - permissions, e = getPermissionPrefixV2(uinfo.host, uinfo.uname, key) - if e != nil { - io.WriteString(w, e.Error()) - return - } - } else { - permissions = [][]string{{key, ""}} - } - - var ( - min, max int - ) - if key == separator { - min = 1 - } else { - min = len(strings.Split(key, separator)) - } - max = min - all := make(map[int][]map[string]interface{}) - if key == separator { - all[min] = []map[string]interface{}{{"key":key, "value":"", "dir":true, "nodes":make([]map[string]interface{}, 0)}} - } - for _, p := range permissions { - pKey, pRange := p[0], p[1] - var opt *client.GetOptions - if pRange != "" { - if pRange == "c" { - pKey += separator - } - opt = &client.GetOptions{Recursive:true, Sort:true} - } - if resp, err := kapi.Get(context.Background(), pKey, opt); err != nil { - data["errorCode"] = 500 - data["message"] = err.Error() - } else { - if resp.Node == nil { - data["errorCode"] = 500 - data["message"] = "The node does not exist." - } else { - max = getNode(resp.Node , key, all, min, max) - } - } - } - - //b, _ := json.MarshalIndent(all, "", " ") - //fmt.Println(string(b)) - - // parent-child mapping - for i := max; i > min; i-- { - for _, a := range all[i] { - for _, pa := range all[i-1] { - if i == 2 { // The last is root - pa["nodes"] = append(pa["nodes"].([]map[string]interface{}), a) - pa["dir"] = true - } else { - if strings.HasPrefix(a["key"].(string), pa["key"].(string) + separator) { - pa["nodes"] = append(pa["nodes"].([]map[string]interface{}), a) - pa["dir"] = true - } - } - } - } - } - - for _, n := range all[min] { - if n["key"] == key { - nodesSort(n) - data["node"] = n - break - } - } - } - - var dataByte []byte - var err error - if dataByte, err = json.Marshal(data);err != nil { - io.WriteString(w, err.Error()) - } else { - io.WriteString(w, string(dataByte)) - } -} - -func nodesSort(node map[string]interface{}) { - if v, ok := node["nodes"]; ok && v != nil { - a := v.([]map[string]interface{}) - if len(a) != 0 { - for i := 0; i < len(a) - 1; i++ { - nodesSort(a[i]) - for j := i + 1; j < len(a); j++ { - if a[j]["key"].(string) < a[i]["key"].(string) { - a[i], a[j] = a[j], a[i] - } - } - } - nodesSort(a[len(a) - 1]) - } - } -} - -func getNode(node *client.Node, selKey string, all map[int][]map[string]interface{}, min, max int) int { - keys := strings.Split(node.Key, separator) // /foo/bar - if len(keys) < min && strings.HasPrefix(node.Key, selKey) { - return max - } - for i := range keys { // ["", "foo", "bar"] - k := strings.Join(keys[0:i+1], separator) - if k == "" { - continue - } - nodeMap := map[string]interface{}{"key": k, "dir":true, "nodes":make([]map[string]interface{}, 0)} - if k == node.Key { - nodeMap["value"] = node.Value - nodeMap["dir"] = node.Dir - nodeMap["ttl"] = node.TTL - nodeMap["createdIndex"] = node.CreatedIndex - nodeMap["modifiedIndex"] = node.ModifiedIndex - } - keylevel := len(strings.Split(k, separator)) - if keylevel > max { - max = keylevel - } - - if _, ok := all[keylevel];!ok { - all[keylevel] = make([]map[string]interface{}, 0) - } - var isExist bool - for _, n := range all[keylevel] { - if n["key"].(string) == k { - isExist = true - } - } - if !isExist { - all[keylevel] = append(all[keylevel], nodeMap) - } - } - - if len(node.Nodes) != 0 { - for _, n := range node.Nodes { - max = getNode(n, selKey, all, min, max) - } - } - return max -} - -func delV2(w http.ResponseWriter, r *http.Request) { - key := r.FormValue("key") - dir := r.FormValue("dir") - log.Println("DELETE", "v2", key) - - kapi := client.NewKeysAPI(getClientV2(w, r)) - - isDir, _ := strconv.ParseBool(dir) - if isDir { - if _, err := kapi.Delete(context.Background(), key, &client.DeleteOptions{Recursive:true, Dir:true}); err != nil { - io.WriteString(w, err.Error()) - return - } - } else { - if _, err := kapi.Delete(context.Background(), key, nil); err != nil { - io.WriteString(w, err.Error()) - return - } - } - - io.WriteString(w, "ok") -} - -func getPathV2(w http.ResponseWriter, r *http.Request) { - getV2(w, r) -} - -func getClientV2(w http.ResponseWriter, r *http.Request) client.Client { - sess := sessmgr.SessionStart(w, r) - v := sess.Get("uinfov2") - if v != nil { - uinfo := v.(*userInfo) - c, _ := newClientV2(uinfo) - return c - } - return nil -} - -func newClientV2(uinfo *userInfo) (client.Client, error) { - cfg := client.Config{ - Endpoints: []string{uinfo.host}, - HeaderTimeoutPerRequest: time.Second * time.Duration(*connectTimeout), - } - if *useAuth { - cfg.Username = uinfo.uname - cfg.Password = uinfo.passwd - } - - c, err := client.New(cfg) - if err != nil { - return nil, err - } - return c, nil -} - -func getPermissionPrefixV2(host, uname, key string) ([][]string, error) { - if !*useAuth { - return [][]string{{key, "p"}}, nil // No auth return all - } else { - if uname == "root" { - return [][]string{{key, "p"}}, nil - } - - if !strings.HasPrefix(host, "http://") { - host = "http://" + host - } - rootUser := rootUesrsV2[host] - rootCli, err := newClientV2(rootUser) - if err != nil { - return nil, err - } - rootUserKapi := client.NewAuthUserAPI(rootCli) - rootRoleKapi := client.NewAuthRoleAPI(rootCli) - - if users, err := rootUserKapi.ListUsers(context.Background()); err != nil { - return nil, err - } else { - // Find user permissions - set := make(map[string]string) - for _, u := range users { - if u == uname { - user, err := rootUserKapi.GetUser(context.Background(), u) - if err != nil { - return nil, err - } - for _, r := range user.Roles { - role, err := rootRoleKapi.GetRole(context.Background(), r) - if err != nil { - return nil, err - } - for _, ks := range role.Permissions.KV.Read { - var k string - if strings.HasSuffix(ks, "*") { - k = ks[:len(ks) - 1] - set[k] = "p" - } else if strings.HasSuffix(ks, "/*") { - k = ks[:len(ks) - 2] - set[k] = "c" - } else { - if _, ok := set[ks]; !ok { - set[ks] = "" - } - } - } - } - break - } - } - var pers [][]string - var ks []string - for k := range set { - ks = append(ks, k) - } - sort.Strings(ks) - for _, k := range ks { - pers = append(pers, []string{k, set[k]}) - } - return pers, nil - } - } -} - -func getInfoV2(host string) map[string]string { - if !strings.HasPrefix(host, "http://") { - host = "http://" + host - } - info := make(map[string]string) - uinfo, ok := rootUesrsV2[host] - if ok { - rootClient, err := newClientV2(uinfo) - if err != nil { - log.Println(err) - return info - } - ver, err := rootClient.GetVersion(context.Background()) - if err != nil { - log.Fatal(err) - } - memberKapi := client.NewMembersAPI(rootClient) - member, err := memberKapi.Leader(context.Background()) - if err != nil { - log.Fatal(err) - } - info["version"] = ver.Server - info["name"] = member.Name - info["size"] = "unknow" // FIXME: How get? - } - return info -} - -// v3 api -func connect(w http.ResponseWriter, r *http.Request) { - mu.Lock() - defer mu.Unlock() - sess := sessmgr.SessionStart(w, r) - host := r.FormValue("host") - uname := r.FormValue("uname") - passwd := r.FormValue("passwd") - - if *useAuth { - if _, ok := rootUsers[host]; !ok && uname != "root" { // no root user - b, _ := json.Marshal(map[string]interface{}{"status":"root"}) - io.WriteString(w, string(b)) - return - } - if uname == "" || passwd == "" { - b, _ := json.Marshal(map[string]interface{}{"status":"login"}) - io.WriteString(w, string(b)) - return - } - } - - if uinfo, ok := sess.Get("uinfo").(*userInfo); ok { - if host == uinfo.host && uname == uinfo.uname && passwd == uinfo.passwd { - info := getInfo(host) - b, _ := json.Marshal(map[string]interface{}{"status":"running", "info":info}) - io.WriteString(w, string(b)) - return - } - } - - uinfo := &userInfo{host:host, uname:uname, passwd:passwd} - c, err := newClient(uinfo) - if err != nil { - log.Println(r.Method, "v3", "connect fail.") - b, _ := json.Marshal(map[string]interface{}{"status":"error", "message":err.Error()}) - io.WriteString(w, string(b)) - return - } - defer c.Close() - _ = sess.Set("uinfo", uinfo) - - if *useAuth { - if uname == "root" { - rootUsers[host] = uinfo - } - } else { - rootUsers[host] = uinfo - } - log.Println(r.Method, "v3", "connect success.") - info := getInfo(host) - b, _ := json.Marshal(map[string]interface{}{"status":"running", "info":info}) - io.WriteString(w, string(b)) -} - -func put(w http.ResponseWriter, r *http.Request) { - cli := getClient(w, r) - defer cli.Close() - key := r.FormValue("key") - value := r.FormValue("value") - ttl := r.FormValue("ttl") - log.Println("PUT", "v3", key) - - var err error - data := make(map[string]interface{}) - if ttl != "" { - var sec int64 - sec, err = strconv.ParseInt(ttl, 10, 64) - if err != nil { - log.Println(err.Error()) - } - var leaseResp *clientv3.LeaseGrantResponse - leaseResp, err = cli.Grant(context.TODO(), sec) - if err == nil && leaseResp != nil { - _, err = cli.Put(context.Background(), key, value, clientv3.WithLease(leaseResp.ID)) - } - } else { - _, err = cli.Put(context.Background(), key, value) - } - if err != nil { - data["errorCode"] = 500 - data["message"] = err.Error() - } else { - if resp, err := cli.Get(context.Background(), key);err != nil { - data["errorCode"] = 500 - data["errorCode"] = err.Error() - } else { - if resp.Count > 0 { - kv := resp.Kvs[0] - node := make(map[string]interface{}) - node["key"] = string(kv.Key) - node["value"] = string(kv.Value) - node["dir"] = false - node["ttl"] = getTTL(cli, kv.Lease) - node["createdIndex"] = kv.CreateRevision - node["modifiedIndex"] = kv.ModRevision - data["node"] = node - } - } - } - - var dataByte []byte - if dataByte, err = json.Marshal(data);err != nil { - io.WriteString(w, err.Error()) - } else { - io.WriteString(w, string(dataByte)) - } -} - -func get(w http.ResponseWriter, r *http.Request) { - data := make(map[string]interface{}) - key := r.FormValue("key") - log.Println("GET", "v3", key) - - var cli *clientv3.Client - sess := sessmgr.SessionStart(w, r) - v := sess.Get("uinfo") - var uinfo *userInfo - if v != nil { - uinfo = v.(*userInfo) - cli, _ = newClient(uinfo) - defer cli.Close() - - permissions, e := getPermissionPrefix(uinfo.host, uinfo.uname, key) - if e != nil { - io.WriteString(w, e.Error()) - return - } - if r.FormValue("prefix") == "true" { - pnode := make(map[string]interface{}) - pnode["key"] = key - pnode["nodes"] = make([]map[string]interface{}, 0) - for _, p := range permissions { - var ( - resp *clientv3.GetResponse - err error - ) - if p[1] != "" { - prefixKey := p[0] - if p[0] == "/" { - prefixKey = "" - } - resp, err = cli.Get(context.Background(), prefixKey, clientv3.WithPrefix()) - } else { - resp, err = cli.Get(context.Background(), p[0]) - } - if err != nil { - data["errorCode"] = 500 - data["message"] = err.Error() - } else { - for _, kv := range resp.Kvs { - node := make(map[string]interface{}) - node["key"] = string(kv.Key) - node["value"] = string(kv.Value) - node["dir"] = false - if key == string(kv.Key) { - node["ttl"] = getTTL(cli, kv.Lease) - } else { - node["ttl"] = 0 - } - node["createdIndex"] = kv.CreateRevision - node["modifiedIndex"] = kv.ModRevision - nodes := pnode["nodes"].([]map[string]interface{}) - pnode["nodes"] = append(nodes, node) - } - } - } - data["node"] = pnode - } else { - if resp, err := cli.Get(context.Background(), key);err != nil { - data["errorCode"] = 500 - data["message"] = err.Error() - } else { - if resp.Count > 0 { - kv := resp.Kvs[0] - node := make(map[string]interface{}) - node["key"] = string(kv.Key) - node["value"] = string(kv.Value) - node["dir"] = false - node["ttl"] = getTTL(cli, kv.Lease) - node["createdIndex"] = kv.CreateRevision - node["modifiedIndex"] = kv.ModRevision - data["node"] = node - } else { - data["errorCode"] = 500 - data["message"] = "The node does not exist." - } - } - } - } - - var dataByte []byte - var err error - if dataByte, err = json.Marshal(data);err != nil { - io.WriteString(w, err.Error()) - } else { - io.WriteString(w, string(dataByte)) - } -} - -func getPath(w http.ResponseWriter, r *http.Request) { - originKey := r.FormValue("key") - log.Println("GET", "v3", originKey) - var ( - data = make(map[string]interface{}) - /* - {1:["/"], 2:["/foo", "/foo2"], 3:["/foo/bar", "/foo2/bar"], 4:["/foo/bar/test"]} - */ - all = make(map[int][]map[string]interface{}) - min int - max int - //prefixKey string - ) - - var cli *clientv3.Client - sess := sessmgr.SessionStart(w, r) - v := sess.Get("uinfo") - var uinfo *userInfo - if v != nil { - uinfo = v.(*userInfo) - cli, _ = newClient(uinfo) - defer cli.Close() - - permissions, e := getPermissionPrefix(uinfo.host, uinfo.uname, originKey) - if e != nil { - io.WriteString(w, e.Error()) - return - } - - // parent - var ( - presp *clientv3.GetResponse - err error - ) - if originKey != separator { - presp, err = cli.Get(context.Background(), originKey) - if err != nil { - data["errorCode"] = 500 - data["message"] = err.Error() - dataByte, _ := json.Marshal(data) - io.WriteString(w, string(dataByte)) - return - } - } - if originKey == separator { - min = 1 - //prefixKey = separator - } else { - min = len(strings.Split(originKey, separator)) - //prefixKey = originKey - } - max = min - all[min] = []map[string]interface{}{{"key":originKey}} - if presp != nil && presp.Count != 0 { - all[min][0]["value"] = string(presp.Kvs[0].Value) - all[min][0]["ttl"] = getTTL(cli, presp.Kvs[0].Lease) - all[min][0]["createdIndex"] = presp.Kvs[0].CreateRevision - all[min][0]["modifiedIndex"] = presp.Kvs[0].ModRevision - } - all[min][0]["nodes"] = make([]map[string]interface{}, 0) - - for _, p := range permissions { - key, rangeEnd := p[0], p[1] - //child - var resp *clientv3.GetResponse - if rangeEnd != "" { - resp, err = cli.Get(context.Background(), key, clientv3.WithPrefix(), clientv3.WithSort(clientv3.SortByKey, clientv3.SortAscend)) - } else { - resp, err = cli.Get(context.Background(), key, clientv3.WithSort(clientv3.SortByKey, clientv3.SortAscend)) - } - if err != nil { - data["errorCode"] = 500 - data["message"] = err.Error() - dataByte, _ := json.Marshal(data) - io.WriteString(w, string(dataByte)) - return - } - - for _, kv := range resp.Kvs { - if string(kv.Key) == separator { - continue - } - keys := strings.Split(string(kv.Key), separator) // /foo/bar - for i := range keys { // ["", "foo", "bar"] - k := strings.Join(keys[0:i+1], separator) - if k == "" { - continue - } - node := map[string]interface{}{"key":k} - if node["key"].(string) == string(kv.Key) { - node["value"] = string(kv.Value) - if key == string(kv.Key) { - node["ttl"] = getTTL(cli, kv.Lease) - } else { - node["ttl"] = 0 - } - node["createdIndex"] = kv.CreateRevision - node["modifiedIndex"] = kv.ModRevision - } - level := len(strings.Split(k, separator)) - if level > max { - max = level - } - - if _, ok := all[level];!ok { - all[level] = make([]map[string]interface{}, 0) - } - levelNodes := all[level] - var isExist bool - for _, n := range levelNodes { - if n["key"].(string) == k { - isExist = true - } - } - if !isExist { - node["nodes"] = make([]map[string]interface{}, 0) - all[level] = append(all[level], node) - } - } - } - } - - // parent-child mapping - for i := max; i > min; i-- { - for _, a := range all[i] { - for _, pa := range all[i-1] { - if i == 2 { - pa["nodes"] = append(pa["nodes"].([]map[string]interface{}), a) - pa["dir"] = true - } else { - if strings.HasPrefix(a["key"].(string), pa["key"].(string) +separator) { - pa["nodes"] = append(pa["nodes"].([]map[string]interface{}), a) - pa["dir"] = true - } - } - } - } - } - } - data = all[min][0] - if dataByte, err := json.Marshal(map[string]interface{}{"node":data});err != nil { - io.WriteString(w, err.Error()) - } else { - io.WriteString(w, string(dataByte)) - } -} - -func del(w http.ResponseWriter, r *http.Request) { - cli := getClient(w, r) - defer cli.Close() - key := r.FormValue("key") - dir := r.FormValue("dir") - log.Println("DELETE", "v3", key) - - if _, err := cli.Delete(context.Background(), key);err != nil { - io.WriteString(w, err.Error()) - return - } - - if dir == "true" { - if _, err := cli.Delete(context.Background(), key +separator, clientv3.WithPrefix());err != nil { - io.WriteString(w, err.Error()) - return - } - } - io.WriteString(w, "ok") -} - -func getTTL(cli *clientv3.Client, lease int64) int64 { - resp, err := cli.Lease.TimeToLive(context.Background(), clientv3.LeaseID(lease)) - if err != nil { - return 0 - } - if resp.TTL == -1 { - return 0 - } - return resp.TTL -} - -func getSeparator(w http.ResponseWriter, _ *http.Request) { - io.WriteString(w, separator) -} - -func getClient(w http.ResponseWriter, r *http.Request) *clientv3.Client { - sess := sessmgr.SessionStart(w, r) - v := sess.Get("uinfo") - if v != nil { - uinfo := v.(*userInfo) - c, _ := newClient(uinfo) - return c - } - return nil -} - -func newClient(uinfo *userInfo) (*clientv3.Client, error) { - endpoints := []string{uinfo.host} - var err error - - // use tls if usetls is true - var tlsConfig *tls.Config - if *usetls { - tlsInfo := transport.TLSInfo{ - CertFile: *cert, - KeyFile: *keyfile, - TrustedCAFile: *cacert, - } - tlsConfig, err = tlsInfo.ClientConfig() - if err != nil { - log.Println(err.Error()) - } - } - - conf := clientv3.Config{ - Endpoints: endpoints, - DialTimeout: time.Second * time.Duration(*connectTimeout), - TLS: tlsConfig, - DialOptions: []grpc.DialOption{grpc.WithBlock()}, - } - if *useAuth { - conf.Username = uinfo.uname - conf.Password = uinfo.passwd - } - - var c *clientv3.Client - c, err = clientv3.New(conf) - if err != nil { - return nil, err - } - return c, nil -} - -func getPermissionPrefix(host, uname, key string) ([][]string, error) { - if !*useAuth { - return [][]string{{key, "p"}}, nil // No auth return all - } else { - if uname == "root" { - return [][]string{{key, "p"}}, nil - } - rootUser := rootUsers[host] - rootCli, err := newClient(rootUser) - if err != nil { - return nil, err - } - defer rootCli.Close() - - if resp, err := rootCli.UserList(context.Background()); err != nil { - return nil, err - } else { - // Find user permissions - set := make(map[string]string) - for _, u := range resp.Users { - if u == uname { - ur, err := rootCli.UserGet(context.Background(), u) - if err != nil { - return nil, err - } - for _, r := range ur.Roles { - rr, err := rootCli.RoleGet(context.Background(), r) - if err != nil { - return nil, err - } - for _, p := range rr.Perm { - set[string(p.Key)] = string(p.RangeEnd) - } - } - break - } - } - var pers [][]string - for k, v := range set { - pers = append(pers, []string{k, v}) - } - return pers, nil - } - } -} - -func getInfo(host string) map[string]string { - info := make(map[string]string) - uinfo := rootUsers[host] - rootClient, err := newClient(uinfo) - if err != nil { - log.Println(err) - return info - } - defer rootClient.Close() - - - status, err := rootClient.Status(context.Background(), host) - if err != nil { - log.Fatal(err) - } - mems, err := rootClient.MemberList(context.Background()) - if err != nil { - log.Fatal(err) - } - kb := 1024 - mb := kb*1024 - gb := mb*1024 - var sizeStr string - for _, m := range mems.Members { - if m.ID == status.Leader { - info["version"] = status.Version - gn, rem1 := size(int(status.DbSize), gb) - mn, rem2 := size(rem1, mb) - kn, bn := size(rem2, kb) - if sizeStr != "" { - sizeStr += " " - } - if gn > 0 { - info["size"] = fmt.Sprintf("%dG", gn) - } else { - if mn > 0 { - info["size"] = fmt.Sprintf("%dM", mn) - } else { - if kn > 0 { - info["size"] = fmt.Sprintf("%dK", kn) - } else { - info["size"] = fmt.Sprintf("%dByte", bn) - } - } - } - info["name"] = m.GetName() - break - } - } - return info -} - -func size(num int, unit int) (n, rem int) { - return num/unit, num - (num/unit)*unit -} \ No newline at end of file diff --git a/src/etcdkeeper/session/session.go b/src/etcdkeeper/session/session.go index 675488f..221cc17 100644 --- a/src/etcdkeeper/session/session.go +++ b/src/etcdkeeper/session/session.go @@ -41,7 +41,7 @@ func Register(name string, provide Provider) { } type Manager struct { - cookieName string //private cookiename + cookieName string // private cookiename lock sync.Mutex // protects session provider Provider maxlifetime int64