├── .gitattributes ├── .gitignore ├── .goreleaser.yml ├── .idea ├── .gitignore ├── codeStyles │ └── codeStyleConfig.xml ├── dictionaries │ └── sysadmin.xml ├── encodings.xml ├── inspectionProfiles │ ├── profiles_settings.xml │ └── wZD.xml ├── misc.xml ├── modules.xml ├── vcs.xml └── wzd.iml ├── CHANGELOG-RUS.md ├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── NOTICE ├── OPTIONS-RUS.md ├── OPTIONS.md ├── README-RUS.md ├── README.md ├── cmp.go ├── cnt.go ├── conf ├── init.d │ ├── bsd │ │ └── wzd │ └── linux │ │ └── wzd ├── logrotate.d │ └── wzd ├── nginx │ ├── localhost-ssl.conf │ └── localhost.conf ├── systemd │ └── wzd.service └── wzd │ ├── bsd │ ├── del-localhost.conf │ ├── get-localhost.conf │ ├── put-localhost.conf │ └── wzd.conf │ ├── del-localhost.conf │ ├── docker │ ├── del-localhost.conf │ ├── get-localhost.conf │ ├── put-localhost.conf │ └── wzd-docker.conf │ ├── get-localhost.conf │ ├── put-localhost.conf │ └── wzd.conf ├── db.go ├── del.go ├── file.go ├── get.go ├── go.mod ├── go.sum ├── hlp.go ├── images ├── get-put-32M.png ├── get.png ├── logo.png ├── paypal.png ├── put.png ├── reduction-full.png ├── wzd-arch.png └── wzd-scheme.png ├── init.go ├── log.go ├── main.go ├── put.go ├── range.go ├── scripts ├── docker │ └── start.sh └── postinstall.sh ├── search.go ├── sys.go ├── tree.go └── wza └── wza /.gitattributes: -------------------------------------------------------------------------------- 1 | * -text 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # This is an example goreleaser.yaml file with some sane defaults. 2 | # Make sure to check the documentation at http://goreleaser.com 3 | before: 4 | hooks: 5 | 6 | env_files: 7 | github_token: /root/projects/keys/main 8 | 9 | builds: 10 | - 11 | binary: wzd 12 | id: wzd 13 | ldflags: -s -w 14 | env: 15 | - CGO_ENABLED=0 16 | goos: 17 | - linux 18 | - freebsd 19 | - openbsd 20 | - netbsd 21 | - solaris 22 | - darwin 23 | goarch: 24 | - amd64 25 | - arm64 26 | - ppc64 27 | - mips64 28 | goarm: 29 | - 7 30 | 31 | release: 32 | draft: false 33 | 34 | archives: 35 | - 36 | id: "wzd" 37 | builds: ['wzd'] 38 | format: tar.gz 39 | name_template: "{{.ProjectName}}-{{.Version}}-{{.Os}}-{{.Arch}}" 40 | files: 41 | - LICENSE 42 | - NOTICE 43 | 44 | nfpms: 45 | - 46 | id: "wzd" 47 | builds: ['wzd'] 48 | formats: 49 | - deb 50 | - rpm 51 | vendor: "Eltaline " 52 | homepage: "https://elta.ee/" 53 | maintainer: "Andrey Kuvshinov " 54 | description: "Sharded NoSQL Compact Storage Server. Distributing any number of small and large files/values" 55 | license: "Apache 2.0" 56 | file_name_template: "{{.ProjectName}}-{{.Version}}-{{.Os}}-{{.Arch}}" 57 | 58 | bindir: /usr/sbin 59 | 60 | dependencies: 61 | - systemd 62 | - logrotate 63 | 64 | empty_folders: 65 | - /etc/wzd 66 | - /var/log/wzd 67 | - /var/lib/wzd 68 | - /usr/share/wzd 69 | 70 | contents: 71 | - src: "conf/systemd/wzd.service" 72 | dst: "/lib/systemd/system/wzd.service" 73 | 74 | - src: "conf/logrotate.d/wzd" 75 | dst: "/etc/logrotate.d/wzd" 76 | 77 | - src: "LICENSE" 78 | dst: "/usr/share/wzd/LICENSE" 79 | 80 | - src: "NOTICE" 81 | dst: "/usr/share/wzd/NOTICE" 82 | 83 | - src: "conf/wzd/wzd.conf" 84 | dst: "/etc/wzd/wzd.conf" 85 | type: config 86 | 87 | - src: "conf/wzd/get-localhost.conf" 88 | dst: "/etc/wzd/get-localhost.conf" 89 | type: config 90 | 91 | - src: "conf/wzd/put-localhost.conf" 92 | dst: "/etc/wzd/put-localhost.conf" 93 | type: config 94 | 95 | - src: "conf/wzd/del-localhost.conf" 96 | dst: "/etc/wzd/del-localhost.conf" 97 | type: config 98 | 99 | scripts: 100 | postinstall: "scripts/postinstall.sh" 101 | 102 | dockers: 103 | - 104 | goos: linux 105 | goarch: amd64 106 | goarm: '' 107 | 108 | binaries: 109 | - wzd 110 | 111 | builds: 112 | - wzd 113 | 114 | image_templates: 115 | - "eltaline/wzd:latest" 116 | - "eltaline/wzd:{{.Version}}" 117 | 118 | skip_push: false 119 | 120 | dockerfile: Dockerfile 121 | 122 | build_flag_templates: 123 | - "--label=org.label-schema.schema-version=1.0" 124 | - "--label=org.label-schema.version={{.Version}}" 125 | - "--label=org.label-schema.name={{.ProjectName}}" 126 | 127 | # If your Dockerfile copies files other than the binary itself, 128 | # you should list them here as well. 129 | # Note that goreleaser will create the same structure inside the temporary 130 | # folder, so if you add `foo/bar.json` here, on your Dockerfile you can 131 | # `COPY foo/bar.json /whatever.json`. 132 | # Also, note that the paths here are relative to the folder in which 133 | # goreleaser is being run. 134 | # This field does not support wildcards, you can add an entire folder here and use wildcards when you `COPY`/`ADD` in your Dockerfile. 135 | extra_files: 136 | - conf/wzd/docker/wzd-docker.conf 137 | - conf/wzd/docker/get-localhost.conf 138 | - conf/wzd/docker/put-localhost.conf 139 | - conf/wzd/docker/del-localhost.conf 140 | - scripts/docker/start.sh 141 | - wza/wza 142 | - LICENSE 143 | - NOTICE 144 | 145 | scoop: 146 | 147 | commit_author: 148 | name: "Andrey Kuvshinov" 149 | email: syslinux@protonmail.com 150 | homepage: "https://elta.ee" 151 | license: Apache 2.0 152 | 153 | brews: 154 | - 155 | commit_author: 156 | name: "Andrey Kuvshinov" 157 | email: syslinux@protonmail.com 158 | homepage: "https://elta.ee/" 159 | goarm: 7 160 | 161 | snapshot: 162 | name_template: "{{.Version}}-master" 163 | changelog: 164 | sort: asc 165 | filters: 166 | exclude: 167 | - README 168 | - OPTIONS 169 | - Readme 170 | - Options 171 | - Image 172 | - Script 173 | - Docker 174 | - Typo 175 | - "via upload" 176 | - goreleaser 177 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /workspace.xml -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/dictionaries/sysadmin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/wZD.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/wzd.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /CHANGELOG-RUS.md: -------------------------------------------------------------------------------- 1 | Версия: 1.2.1 2 | ======== 3 | 4 | - Обновление до Go 1.14 5 | - Обновление веб вреймфорка Iris 12.1.8 6 | - Переход к поддержке Go модулей 7 | 8 | Версия: 1.2.0 9 | ======== 10 | 11 | **Важно: несовместимости с прошлыми версиями** 12 | 13 | - **По умолчанию теперь curl -X PUT без дополнительных заголовков работает в автоматическом режиме на основе параметра fmaxsize** 14 | - **Поиск пока без репликации(в разработке)** 15 | - Убран избыточный перевод каретки `"\n"` при выводе во всех типах поиска 16 | - Убраны избыточные заголовки ```KeysAll, KeysInfoAll, KeysSearchAll, KeysCountAll``` 17 | - Убрано двойное кодирование при работе с заголовком ```WithValue```, значения кодируются только в HEX 18 | - Для заголовков ```Keys, KeysFiles, KeysArchives``` добавлен тип файла/ключа 19 | - Переименована опция `srchcache` в `searchcache`, размерность изменена на байты 20 | - Изменена лицензия проекта на Apache 2.0 лицензию 21 | 22 | Добавлено в версии 1.2.0: 23 | 24 | - **Реализован быстрый поиск, поиск был полностью переписан** 25 | - Реализован автоматический шардинг Bolt архивов в рамках директории (параметры skeyscnt, smaxsize) 26 | - Заголовок ```File``` для метода PUT 27 | - Заголовки ```Prefix, WithJoin, Sort``` 28 | - Заголовок ```Compact``` для методов PUT и DELETE 29 | - Обновлена документация 30 | 31 | Исправлено в версии 1.2.0: 32 | 33 | - Исправлена работа с заголовком ```WithValue``` 34 | 35 | Версия: 1.1.3 36 | ======== 37 | 38 | **Важно: несовместимости с прошлыми версиями** 39 | 40 | - Для использования заголовков ```Keys*``` требуется добавлять заголовок вида ```curl "-H Sea"...``` 41 | - Заголовки ```Keys...Archive``` переименованы в ```Keys...Archives``` 42 | - В docker образе параметры getkeys и getinfo по умолчанию отключены 43 | 44 | Добавлено в версии 1.1.3: 45 | 46 | - Расширенный рекурсивный поиск файлов и значений 47 | - Глобальные опции gcpercent, srchcache (настройка сборщика мусора и кеш поиска) 48 | - Заголовок ```Sea``` (требуется для работы с поиском ```Keys*```) 49 | - Заголовки ```KeysSearch*, Recursive``` (параметры getsearch, getrecursive) 50 | - Заголовки ```Expression, StopFirst``` (регулярное выражение и остановка поиска) 51 | - Заголовки ```WithValue, Expire``` (параметры getvalue, getcache) 52 | - Заголовки ```MinSize, MaxSize, MinStmp, MaxStmp, WithUrl``` 53 | - Заголовки ```Expire, SkipCache``` (Query cache поиска и пропуск кеша) 54 | - Заголовки ответа в поиске ```Hitcache, Errcache, Errmsg``` 55 | - Заголовок ```FromFile``` для методов GET и DELETE 56 | - Обновлена документация 57 | 58 | Исправлено в версии 1.1.3: 59 | 60 | - Незначительные исправления ошибок 61 | 62 | Версия: 1.1.2 63 | ======== 64 | 65 | **Важно: несовместимости с прошлыми версиями** 66 | 67 | Порядок обновления до 1.1.2 версии: 68 | 69 | - Установите wZD сервер и архиватор wZA версии 1.1.2 70 | - Перезапустите wZD сервер 71 | - Обновите Bolt архивы (требуется сделать один раз) 72 | 73 | *Повторный запуск обновления исключается самим архиватором 74 | 75 | ``` 76 | find /var/storage -type f -name '*.bolt' > /tmp/upgrade.list 77 | wza --upgrade --list=/tmp/upgrade.list 78 | ``` 79 | 80 | Добавлено в версии 1.1.2: 81 | 82 | - Архиватор wZA теперь поставляется сразу в docker образе 83 | - Заголовки Keys* KeysInfo* KeysCount* (параметры getkeys, getinfo, getcount) 84 | - Заголовки JSON, Limit, Offset (расширенное использование NoSQL составляющей) 85 | - Обновление формата Bolt архивов (для будущей поддержки WEB интерфейса и FUSE) 86 | - Поддержка UTF-8 87 | 88 | Исправлено в версии 1.1.2: 89 | 90 | - Исправление формата хранения даты с uint32 на uint64 91 | - Исправление разных ошибок 92 | - Исправление утечек памяти 93 | 94 | Версия: 1.1.1 95 | ======== 96 | 97 | Исправлено в версии 1.1.1: 98 | 99 | - Небольшие исправления 100 | - Исправление выбора алгоритма высвобождения страниц при чтении 101 | 102 | Версия: 1.1.0 103 | ======== 104 | 105 | Добавлено в версии 1.1.0: 106 | 107 | - HTTPS (параметры bindaddrssl, onlyssl, sslcrt, sslkey) 108 | - IP авторизация (параметры getallow, putallow, delallow) 109 | - Выбор алгоритма free page в BoltDB (параметр freelist) 110 | - Keepalive (параметр keepalive) 111 | - Метод POST (только бинарный протокол передачи данных) 112 | - Метод OPTIONS (параметр options) 113 | - Access-Control-Allow-Origin заголовок (параметр headorigin) 114 | - X-Frame-Options заголовок (параметр xframe) 115 | - Content-Encoding заголовок для заранее сжатых gzip файлов (automatic + параметр gzstatic) 116 | - Логирование 4xx (параметр log4xx) 117 | 118 | Исправлено в версии 1.1.0: 119 | 120 | - Применение HTTP таймаутов 121 | - Исключение возможности загрузки файлов с расширением .bolt 122 | - Исправление некоторых регулярных выражений 123 | - Возможность использовать сервер без реверсивного прокси серверов 124 | 125 | **Версия 1.0.0 устарела и была удалена из публичного доступа, потому что это был первый дизайн-релиз без важного функционала** -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Version: 1.2.1 2 | ======== 3 | 4 | - Update to Go 1.14 5 | - Update to Iris 12.1.8 6 | - Transition to Go module support 7 | 8 | Version: 1.2.0 9 | ======== 10 | 11 | **Important: incompatibilities with previous versions** 12 | 13 | - **By default, now curl -X PUT without additional headers works in automatic mode based on the fmaxsize parameter** 14 | - **Search now without replication(in development)** 15 | - Removed excessive carriage return `"\n"` when displaying in all types of search 16 | - Removed redundant headers ```KeysAll, KeysInfoAll, KeysSearchAll, KeysCountAll``` 17 | - Removed double encoding when working with the header ```WithValue```, the values are encoded only in HEX 18 | - For headers ```Keys, KeysFiles, KeysArchives``` added a type of file/key 19 | - Renamed `srchcache` option to `searchcache`, dimension changed to bytes 20 | - Project license changed to Apache License 2.0 21 | 22 | Added in version 1.2.0: 23 | 24 | - **Implemented a fast search, the search has been completely rewritten** 25 | - **Implemented automatic sharding of Bolt archives within the directory (skeyscnt, smaxsize parameters)** 26 | - Header ```File``` for PUT method 27 | - Headers ```Prefix, WithJoin, Sort``` 28 | - Header ```Compact``` for PUT and DELETE methods 29 | - Updated documentation 30 | 31 | Fixed in version 1.2.0: 32 | 33 | - Fixed work with the header ```WithValue``` 34 | 35 | Version: 1.1.3 36 | ======== 37 | 38 | **Important: incompatibilities with previous versions** 39 | 40 | - For use all ```Keys*``` headers, you need to add a header ```curl "-H Sea" ...``` 41 | - Headers ```Keys...Archive`` renamed to ```Keys...Archives``` 42 | - In the docker image, getkeys and getinfo options are disabled by default 43 | 44 | Added in version 1.1.3: 45 | 46 | - Advanced recursive search for files and values 47 | - Global options: gcpercent, srchcache (configure garbage collector and search cache) 48 | - ```Sea``` header (required to work with ```Keys*``` search) 49 | - Headers: ```KeysSearch*, Recursive``` (getsearch, getrecursive parameters) 50 | - Headers: ```Expression, StopFirst``` (regular expression and stop search) 51 | - Headers: ```WithValue, Expire``` (getvalue, getcache parameters) 52 | - Headers: ```MinSize, MaxSize, MinStmp, MaxStmp, WithUrl``` 53 | - Headers: ```Expire, SkipCache``` (Query cache search and skip cache) 54 | - Response headers: ```Hitcache, Errcache, Errmsg``` for search 55 | - ```FromFile``` header for GET and DELETE methods 56 | - Updated documentation 57 | 58 | Fixed in version 1.1.3: 59 | 60 | - Minor bug fixes 61 | 62 | Version: 1.1.2 63 | ======== 64 | 65 | **Important: incompatibilities with previous versions** 66 | 67 | The procedure for upgrading to version 1.1.2: 68 | 69 | - Install wZD server and archiver wZA version 1.1.2 70 | - Restart wZD server 71 | - Update Bolt archives (one time required) 72 | 73 | *Restarting the update is excluded by the archiver itself 74 | 75 | ```bash 76 | find /var/storage -type f -name '*.bolt'> /tmp/upgrade.list 77 | wza --upgrade --list=/tmp/upgrade.list 78 | ``` 79 | 80 | Added in version 1.1.2: 81 | 82 | - The archiver wZA now comes immediately in the docker image 83 | - Headers Keys* KeysInfo* KeysCount* (parameters getkeys, getinfo, getcount) 84 | - Headers JSON, Limit, Offset (advanced use of NoSQL component) 85 | - Updating the format of Bolt archives (for future support for WEB interface and FUSE) 86 | - Support for UTF-8 87 | 88 | Fixed in version 1.1.2: 89 | 90 | - Fixed date storage format from uint32 to uint64 91 | - Correction of various errors 92 | - Fixed memory leaks 93 | 94 | Version: 1.1.1 95 | ======== 96 | 97 | Fixed in version 1.1.1: 98 | 99 | - Small fixes 100 | - Fixed freelist select algorithm when Read 101 | 102 | Version: 1.1.0 103 | ======== 104 | 105 | Added in version 1.1.0: 106 | 107 | - HTTPS (parameters bindaddrssl, onlyssl, sslcrt, sslkey) 108 | - IP authorization (parameters getallow, putallow, delallow) 109 | - Choice of free page algorithm in BoltDB (freelist parameter) 110 | - Keepalive (keepalive parameter) 111 | - POST method (binary data protocol only) 112 | - OPTIONS method (parameter options) 113 | - Access-Control-Allow-Origin header (parameter headorigin) 114 | - X-Frame-Options header (parameter xframe) 115 | - Content-Encoding header for pre-compressed gzip files (automatic + parameter gzstatic) 116 | - Logging 4xx (parameter log4xx) 117 | 118 | Fixed in version 1.1.0: 119 | 120 | - Fixed set of HTTP timeouts 121 | - Exclusion of the ability to upload files with the extension .bolt 122 | - Fixed some regular expressions 123 | - Ability to use server without reverse proxy servers 124 | 125 | **Version 1.0.0 is deprecated and removed from public access, because this is first design-release without important features** 126 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:buster 2 | 3 | LABEL maintainer="Andrey Kuvshinov" 4 | 5 | ENV bindaddr "127.0.0.1:9699" 6 | ENV bindaddrssl "127.0.0.1:9799" 7 | ENV onlyssl false 8 | ENV readtimeout 60 9 | ENV readheadertimeout 5 10 | ENV writetimeout 60 11 | ENV idletimeout 60 12 | ENV keepalive false 13 | ENV realheader "X-Real-IP" 14 | ENV charset "UTF-8" 15 | ENV debugmode false 16 | ENV gcpercent 25 17 | ENV freelist "hashmap" 18 | ENV search true 19 | ENV searchcache 134217728 20 | ENV searchdir "/var/lib/wzd/search" 21 | ENV searchinit 4 22 | ENV searchindex "ram" 23 | ENV pidfile "/run/wzd/wzd.pid" 24 | ENV logdir "/var/log/wzd" 25 | ENV logmode 0640 26 | ENV defsleep 1 27 | ENV cmpsched true 28 | ENV cmpdir "/var/lib/wzd/compact" 29 | ENV cmptime 7 30 | ENV cmpcheck 1 31 | 32 | ENV host "localhost" 33 | ENV root "/var/storage" 34 | ENV sslcrt "" 35 | ENV sslkey "" 36 | ENV getallow "/etc/wzd/get-localhost.conf" 37 | ENV putallow "/etc/wzd/put-localhost.conf" 38 | ENV delallow "/etc/wzd/del-localhost.conf" 39 | ENV options "GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE" 40 | ENV headorigin "*" 41 | ENV xframe "sameorigin" 42 | ENV upload true 43 | ENV delete true 44 | ENV compaction true 45 | ENV writeintegrity true 46 | ENV readintegrity true 47 | ENV trytimes 5 48 | ENV opentries 5 49 | ENV locktimeout 5 50 | ENV skeyscnt 16384 51 | ENV smaxsize 536870912 52 | ENV fmaxsize 1048576 53 | ENV vmaxsize 4096 54 | ENV args false 55 | ENV getbolt false 56 | ENV getkeys false 57 | ENV getinfo false 58 | ENV getsearch false 59 | ENV getrecursive false 60 | ENV getjoin false 61 | ENV getvalue false 62 | ENV getcount false 63 | ENV getcache true 64 | ENV searchthreads 4 65 | ENV searchtimeout 10 66 | ENV nonunique false 67 | ENV cctrl 0 68 | ENV minbuffer 262144 69 | ENV lowbuffer 1048576 70 | ENV medbuffer 67108864 71 | ENV bigbuffer 536870912 72 | ENV filemode 0640 73 | ENV dirmode 0750 74 | ENV delbolt false 75 | ENV deldir false 76 | ENV gzstatic false 77 | ENV log4xx true 78 | 79 | RUN groupadd wzd 80 | RUN useradd wzd -g wzd 81 | 82 | RUN mkdir -p /etc/wzd 83 | RUN mkdir -p ${logdir} 84 | RUN mkdir -p ${searchdir} 85 | RUN mkdir -p ${cmpdir} 86 | RUN mkdir -p ${root} 87 | RUN mkdir -p `dirname ${pidfile}` 88 | 89 | RUN chown wzd.wzd ${logdir} 90 | RUN chown wzd.wzd ${searchdir} 91 | RUN chown wzd.wzd ${cmpdir} 92 | RUN chown wzd.wzd `dirname ${pidfile}` 93 | 94 | RUN apt-get update 95 | RUN apt-get -y install sed util-linux 96 | 97 | COPY wzd /usr/bin/ 98 | COPY wza/wza /usr/bin/ 99 | COPY conf/wzd/docker/wzd-docker.conf /etc/wzd/wzd.conf 100 | COPY conf/wzd/docker/get-localhost.conf /etc/wzd/get-localhost.conf 101 | COPY conf/wzd/docker/put-localhost.conf /etc/wzd/put-localhost.conf 102 | COPY conf/wzd/docker/del-localhost.conf /etc/wzd/del-localhost.conf 103 | COPY scripts/docker/start.sh / 104 | COPY LICENSE / 105 | COPY NOTICE / 106 | 107 | EXPOSE 80/tcp 108 | 109 | ENTRYPOINT ["/start.sh"] 110 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright © 2020 Andrey Kuvshinov. Contacts: 2 | Copyright © 2020 Eltaline OU. Contacts: 3 | All rights reserved. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | The wZD project contains unmodified/modified libraries imports too with 18 | separate copyright notices and license terms. Your use of the source code 19 | this libraries is subject to the terms and conditions of licenses these libraries. 20 | -------------------------------------------------------------------------------- /OPTIONS-RUS.md: -------------------------------------------------------------------------------- 1 | wZD Logo 2 | 3 | Параметры wZD: 4 | ======== 5 | 6 | Секция [command line] 7 | ------------ 8 | 9 | - -config string 10 | --config=/etc/wzd/wzd.conf (по умолчанию "/etc/wzd/wzd.conf") 11 | - -debug 12 | --debug - Режим отладки. 13 | - -help 14 | --help - Выводит помощь. 15 | - -version 16 | --version - Выводит версию. 17 | 18 | Секция [global] 19 | ------------ 20 | 21 | bindaddr 22 | - **Описание:** Основной адрес и TCP порт, значение ":9699" все адреса. 23 | - **Умолчание:** "127.0.0.1:9699" 24 | - **Тип:** string 25 | - **Секция:** [global] 26 | 27 | bindaddrssl 28 | - **Описание:** Основной SSL адрес и TCP порт, значение ":9799" все адреса. 29 | - **Умолчание:** "127.0.0.1:9799" 30 | - **Тип:** string 31 | - **Секция:** [global] 32 | 33 | onlyssl 34 | - **Описание:** Глобальное отключение для всех виртуальных хостов обычного HTTP адреса и порта. Не настраивается для виртуального хоста. 35 | - **Умолчание:** false 36 | - **Значения:** true или false 37 | - **Тип:** bool 38 | - **Секция:** [global] 39 | 40 | readtimeout 41 | - **Описание:** Задает таймаут на максимальное время передачи данных от клиента серверу. Для передачи больших файлов следует увеличить. Если wZD сервер установлен за Nginx или HAProxy, тогда можно не использовать данный таймаут, установив его в 0 (без таймаута). Не настраивается для виртуального хоста. 42 | - **Умолчание:** 60 43 | - **Значения:** 0-86400 44 | - **Тип:** int 45 | - **Секция:** [global] 46 | 47 | readheadertimeout 48 | - **Описание:** Задает таймаут на максимальное время получения заголовков от клиента. Если wZD сервер установлен за Nginx или HAProxy, тогда можно не использовать данный таймаут, установив его в 0. Если параметр = 0, тогда берется значение параметра readtimeout, если и readtimeout = 0, тогда readheadertimeout таймаут не используется (без таймаута). Не настраивается для виртуального хоста. 49 | - **Умолчание:** 5 50 | - **Значения:** 0-86400 51 | - **Тип:** int 52 | - **Секция:** [global] 53 | 54 | writetimeout 55 | - **Описание:** Задает таймаут на максимальное время передачи данных клиенту. Для передачи больших файлов следует значительно увеличить. Если wZD сервер установлен за Nginx или HAProxy, тогда можно не использовать данный таймаут установив его в 0 (без таймаута). Не настраивается для виртуального хоста. 56 | - **Умолчание:** 60 57 | - **Значения:** 0-86400 58 | - **Тип:** int 59 | - **Секция:** [global] 60 | 61 | idletimeout 62 | - **Описание:** Задает таймаут на максимальное время жизни keep alive соединений. Если параметр = 0, тогда берется значение параметра readtimeout, если и readtimeout = 0, тогда idletimeout таймаут не используется (без таймаута). Не настраивается для виртуального хоста. 63 | - **Умолчание:** 60 64 | - **Значения:** 0-86400 65 | - **Тип:** int 66 | - **Секция:** [global] 67 | 68 | keepalive 69 | - **Описание:** Глобальное включение или отключение keep alive. Не настраивается для виртуального хоста. 70 | - **Умолчание:** false 71 | - **Значения:** true или false 72 | - **Тип:** bool 73 | - **Секция:** [global] 74 | 75 | realheader 76 | - **Описание:** Заголовок реального IP адреса от реверс прокси. Не настраивается для виртуального хоста. 77 | - **Умолчание:** "X-Real-IP" 78 | - **Тип:** string 79 | - **Секция:** [global] 80 | 81 | charset 82 | - **Описание:** Кодировка на весь сервер. Не настраивается для виртуального хоста. 83 | - **Умолчание:** "UTF-8" 84 | - **Тип:** string 85 | - **Секция:** [global] 86 | 87 | debugmode 88 | - **Описание:** Режим отладки. Не настраивается для виртуального хоста. 89 | - **Умолчание:** false 90 | - **Значения:** true или false 91 | - **Тип:** bool 92 | - **Секция:** [global] 93 | 94 | gcpercent 95 | - **Описание:** Глобально задает параметр сборки мусора (проценты). -1 отключает сборщик мусора. Не настраивается для виртуального хоста. 96 | - **Умолчание:** 25 97 | - **Значения:** -1-100 98 | - **Тип:** int 99 | - **Секция:** [global] 100 | 101 | freelist 102 | - **Описание:** Глобально задает тип алгоритма высвобождения страниц в BoltDB. Алгоритм hashmap более быстрый. Не настраивается для виртуального хоста. 103 | - **Умолчание:** hashmap 104 | - **Значения:** hashmap или array 105 | - **Тип:** string 106 | - **Секция:** [global] 107 | 108 | search 109 | - **Описание:** Глобальное включение или отключение поиска. 110 | - **Умолчание:** true 111 | - **Значения:** true или false 112 | - **Тип:** bool 113 | - **Секция:** [global] 114 | 115 | searchcache 116 | - **Описание:** Глобально задает размер кеша поиска. Значения больше 1/1024 размера кеша не помещаются в кеш. Не настраивается для виртуального хоста. (байты) 117 | - **Умолчание:** 134217728 118 | - **Значения:** 33554432-2147483647 119 | - **Тип:** int 120 | - **Секция:** [global] 121 | 122 | searchdir 123 | - **Описание:** Директория базы данных поиска. Наполнение базы происходит при первом запуске сервера. Можно безопасно удалить и реинициализировать снова. 124 | - **Умолчание:** "/var/lib/wzd/search" 125 | - **Тип:** string 126 | - **Секция:** [global] 127 | 128 | searchinit 129 | - **Описание:** Количество параллельных потоков во время инициализации базы данных поиска. 130 | - **Умолчание:** 4 131 | - **Значения:** 1-256 132 | - **Тип:** int 133 | - **Секция:** [global] 134 | 135 | searchindex 136 | - **Описание:** Расположение базы данных поиска. Порядок производительности по убыванию - ram, disk, bpt. 137 | - **Умолчание:** "ram" 138 | - **Значения:** "ram" или "disk" или "bpt" 139 | - **Тип:** string 140 | - **Секция:** [global] 141 | 142 | cmpsched = true 143 | - **Описание:** Глобальное включение или отключение автоматического диспетчера компакции/дефрагментации Bolt архивов. 144 | - **Умолчание:** false 145 | - **Значения:** true или false 146 | - **Тип:** bool 147 | - **Секция:** [global] 148 | 149 | cmpdir 150 | - **Описание:** Директория технической базы данных. Технических данных мало, используется для диспетчера компакции/дефрагментации. 151 | - **Умолчание:** "/var/lib/wzd/compact" 152 | - **Тип:** string 153 | - **Секция:** [global] 154 | 155 | cmptime = 30 156 | - **Описание:** Время ожидания компакции/дефрагментации обновленных Bolt архивов (дни). 157 | - **Умолчание:** 30 158 | - **Значения:** 1-1000 159 | - **Тип:** int 160 | - **Секция:** [global] 161 | 162 | cmpcheck = 5 163 | - **Описание:** Интервал запуска автоматического диспетчера компакции/дефрагментации (секунды). 164 | - **Умолчание:** 5 165 | - **Значения:** 1-5 166 | - **Тип:** int 167 | - **Секция:** [global] 168 | 169 | pidfile 170 | - **Описание:** Путь к pid файлу. 171 | - **Умолчание:** "/run/wzd/wzd.pid" 172 | - **Тип:** string 173 | - **Секция:** [global] 174 | 175 | logdir 176 | - **Описание:** Путь к log директории. 177 | - **Умолчание:** "/var/log/wzd" 178 | - **Тип:** string 179 | - **Секция:** [global] 180 | 181 | logmode 182 | - **Описание:** Права на log файлы (маска). 183 | - **Умолчание:** 0640 184 | - **Значения:** 0600-0666 185 | - **Тип:** uint32 186 | - **Секция:** [global] 187 | 188 | defsleep 189 | - **Описание:** Пауза между попытками открытия Bolt архива (секунды). 190 | - **Умолчание:** 1 191 | - **Значения:** 1-5 192 | - **Тип:** int 193 | - **Секция:** [global] 194 | 195 | 196 | Секция [server] и подсекции [server.name] 197 | ------------ 198 | 199 | [server.name] 200 | - **Описание:** Основной внутренний идентификатор виртуального хоста. после "." можно использовать любое имя, это не доменное имя, только внутренний идентификатор. 201 | - **Тип:** string 202 | - **Секция:** [server] 203 | 204 | host 205 | - **Описание:** Имя виртуального хоста. Не поддерживается * или _ , для сведения множества виртуальных хостов на 1 виртуальный хост в wZD сервере используйте Nginx или HAProxy или любой другой реверс прокси сервер с жестко заданным "proxy_set_header Host hostname;" (на примере Nginx), где hostname = host в виртуальном хосте wZD сервера. 206 | - **Умолчание:** Обязательный параметр 207 | - **Тип:** string 208 | - **Секция:** [server.name] 209 | 210 | root 211 | - **Описание:** Корневая директория виртуального хоста. 212 | - **Умолчание:** Обязательный параметр 213 | - **Тип:** string 214 | - **Секция:** [server.name] 215 | 216 | sslcrt 217 | - **Описание:** Путь к файлу SSL сертификата для виртуального хоста. 218 | - **Умолчание:** "" 219 | - **Тип:** string 220 | - **Секция:** [server.name] 221 | 222 | sslkey 223 | - **Описание:** Путь к файлу SSL ключа для виртуального хоста. 224 | - **Умолчание:** "" 225 | - **Тип:** string 226 | - **Секция:** [server.name] 227 | 228 | getallow 229 | - **Описание:** Путь к файлу разрешенных IP адресов методов GET/HEAD/OPTIONS для виртуального хоста. Если файл не указан, всегда будет отдаваться 403 ошибка. 230 | - **Умолчание:** "" 231 | - **Тип:** string 232 | - **Секция:** [server.name] 233 | 234 | putallow 235 | - **Описание:** Путь к файлу разрешенных IP адресов для методов PUT/POST/PATCH для виртуального хоста. Если файл не указан, всегда будет отдаваться 403 ошибка. 236 | - **Умолчание:** "" 237 | - **Тип:** string 238 | - **Секция:** [server.name] 239 | 240 | delallow 241 | - **Описание:** Путь к файлу разрешенных IP адресов для метода DELETE для виртуального хоста. Если файл не указан, всегда будет отдаваться 403 ошибка. 242 | - **Умолчание:** "" 243 | - **Тип:** string 244 | - **Секция:** [server.name] 245 | 246 | options 247 | - **Описание:** Задает заголовок Access-Control-Allow-Methods с доступными методами для виртуального хоста. 248 | - **Умолчание:** "GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE" 249 | - **Тип:** string 250 | - **Секция:** [server.name] 251 | 252 | headorigin 253 | - **Описание:** Задает заголовок Access-Control-Allow-Origin. 254 | - **Умолчание:** "*" 255 | - **Тип:** string 256 | - **Секция:** [server.name] 257 | 258 | xframe 259 | - **Описание:** Задает заголовок X-Frame-Options. 260 | - **Умолчание:** "sameorigin" 261 | - **Тип:** string 262 | - **Секция:** [server.name] 263 | 264 | upload 265 | - **Описание:** Включает или отключает метод PUT для виртуального хоста. 266 | - **Умолчание:** Обязательный параметр 267 | - **Значения:** true или false 268 | - **Тип:** bool 269 | - **Секция:** [server.name] 270 | 271 | delete 272 | - **Описание:** Включает или отключает метод DELETE для виртуального хоста. 273 | - **Умолчание:** Обязательный параметр 274 | - **Значения:** true или false 275 | - **Тип:** bool 276 | - **Секция:** [server.name] 277 | 278 | compaction 279 | - **Описание:** Включает или отключает автоматическое добавление задач для диспетчера компакции/дефрагментации Bolt архивов, при обновлении или удалении файлов, или значений в Bolt архивах, если параметр cmpsched не отключен глобально. 280 | - **Умолчание:** Обязательный параметр 281 | - **Значения:** true или false 282 | - **Тип:** bool 283 | - **Секция:** [server.name] 284 | 285 | writeintegrity 286 | - **Описание:** Включает или отключает подсчет и запись контрольной суммы в бинарный заголовок во время загрузки файлов или значений через wZD сервер в Bolt архивы. Рекомендуется включить. 287 | - **Умолчание:** Обязательный параметр 288 | - **Значения:** true или false 289 | - **Тип:** bool 290 | - **Секция:** [server.name] 291 | 292 | readintegrity 293 | - **Описание:** Включает или отключает проверку контрольной суммы из бинарного заголовка во время выдачи файлов или значений клиенту сервера. Рекомендуется включить. 294 | - **Умолчание:** Обязательный параметр 295 | - **Значения:** true или false 296 | - **Тип:** bool 297 | - **Секция:** [server.name] 298 | 299 | trytimes 300 | - **Описание:** Количество попыток получения виртуальной блокировки Bolt архива, прежде чем выдать http ошибку (количество). 301 | - **Умолчание:** Обязательный параметр 302 | - **Значения:** 1-1000 303 | - **Тип:** int 304 | - **Секция:** [server.name] 305 | 306 | opentries 307 | - **Описание:** Количество попыток открытия Bolt архива, прежде чем выдать http ошибку (количество). 308 | - **Умолчание:** Обязательный параметр 309 | - **Значения:** 1-1000 310 | - **Тип:** int 311 | - **Секция:** [server.name] 312 | 313 | locktimeout 314 | - **Описание:** Максимальный таймаут для открытия Bolt архива за 1 попытку (секунды). 315 | - **Умолчание:** Обязательный параметр 316 | - **Значения:** 1-3600 317 | - **Тип:** int 318 | - **Секция:** [server.name] 319 | 320 | skeyscnt 321 | - **Описание:** Максимально допустимое количество файлов и/или ключей на один Bolt архив. Если данный параметр превышен, то файл или значение загрузится в следующий по счету Bolt архив в этой директории. Рекомендуемый размер не более 65536. 322 | - **Умолчание:** Обязательный параметр 323 | - **Значения:** 4096-131072 324 | - **Тип:** int 325 | - **Секция:** [server.name] 326 | 327 | smaxsize 328 | - **Описание:** Максимальный размер Bolt архива. Если данный параметр превышен, то файл или значение загрузится в следующий по счету Bolt архив в этой же директории. (байты) 329 | - **Умолчание:** Обязательный параметр 330 | - **Значения:** 33554432-1073741824 331 | - **Тип:** int64 332 | - **Секция:** [server.name] 333 | 334 | fmaxsize 335 | - **Описание:** Максимальный размер загружаемого файла или значения в Bolt архив. Если данный параметр превышен, то файл или значение загрузится как отдельный файл по тому же пути. Рекомендуемый размер не более 1048576 (1МБ). Не рекомендуется загружать файлы или значения в Bolt архивы размером больше 16МБ. Такие файлы или значения должны сохраняться отдельно (байты). 336 | - **Умолчание:** Обязательный параметр 337 | - **Значения:** 1-33554432 338 | - **Тип:** int64 339 | - **Секция:** [server.name] 340 | 341 | vmaxsize 342 | - **Описание:** Максимальный размер запрашиваемого файла или значения при поиске с заголовком WithValue. Если данный параметр превышен для конкретного файла или значения, то значение не отобразится в JSON документе. Рекомендуемый размер не более 4096 (4КБ). Не рекомендуется запрашивать в поиске с выдачей значения, файлы или значения размером больше 16КБ (байты). 343 | - **Умолчание:** Обязательный параметр 344 | - **Значения:** 1-262144 345 | - **Тип:** int64 346 | - **Секция:** [server.name] 347 | 348 | args 349 | - **Описание:** Если отключено, тогда query аргументы будут запрещены для GET запросов. Рекомендуется включить, если вы используете Vary заголовок на реверс прокси сервере или версионность через query аргументы. 350 | - **Умолчание:** Обязательный параметр 351 | - **Значения:** true или false 352 | - **Тип:** bool 353 | - **Секция:** [server.name] 354 | 355 | getbolt 356 | - **Описание:** Если отключено, тогда будет запрещено напрямую скачивать Bolt архивы. 357 | - **Умолчание:** Обязательный параметр 358 | - **Значения:** true или false 359 | - **Тип:** bool 360 | - **Секция:** [server.name] 361 | 362 | getkeys 363 | - **Описание:** Если отключено, тогда будет запрещено получать имена файлов или ключей (в том числе отдельных файлов) в директории. 364 | - **Умолчание:** Обязательный параметр 365 | - **Значения:** true или false 366 | - **Тип:** bool 367 | - **Секция:** [server.name] 368 | 369 | getinfo 370 | - **Описание:** Если отключено, тогда будет запрещено получать имена файлов или ключей c дополнительной информацией (в том числе отдельных файлов) в директории. 371 | - **Умолчание:** Обязательный параметр 372 | - **Значения:** true или false 373 | - **Тип:** bool 374 | - **Секция:** [server.name] 375 | 376 | getsearch 377 | - **Описание:** Если отключено, тогда будет запрещено использовать заголовки поиска KeysSearch* и заголовок Expression. 378 | - **Умолчание:** Обязательный параметр 379 | - **Значения:** true или false 380 | - **Тип:** bool 381 | - **Секция:** [server.name] 382 | 383 | getrecursive 384 | - **Описание:** Если отключено, тогда рекурсивный поиск имен файлов или ключей будет запрещен. 385 | - **Умолчание:** Обязательный параметр 386 | - **Значения:** true или false 387 | - **Тип:** bool 388 | - **Секция:** [server.name] 389 | 390 | getjoin 391 | - **Описание:** Если отключено, тогда объединение с рекурсией во время поиска имен файлов или ключей будет запрещено. 392 | - **Умолчание:** Обязательный параметр 393 | - **Значения:** true или false 394 | - **Тип:** bool 395 | - **Секция:** [server.name] 396 | 397 | getvalue 398 | - **Описание:** Если отключено, тогда будет запрещено получать значения файлов или ключей (в том числе отдельных файлов) в результатах поиска через заголовки KeysSearch*. 399 | - **Умолчание:** Обязательный параметр 400 | - **Значения:** true или false 401 | - **Тип:** bool 402 | - **Секция:** [server.name] 403 | 404 | getcount 405 | - **Описание:** Если отключено, тогда будет запрещено получать суммарное количество файлов или значений (в том числе отдельных файлов) в директории. 406 | - **Умолчание:** Обязательный параметр 407 | - **Значения:** true или false 408 | - **Тип:** bool 409 | - **Секция:** [server.name] 410 | 411 | getcache 412 | - **Описание:** Если отключено, тогда будет запрещено устанавливать время жизни результатов поиска в кеше поиска через заголовок Expire. 413 | - **Умолчание:** Обязательный параметр 414 | - **Значения:** true или false 415 | - **Тип:** bool 416 | - **Секция:** [server.name] 417 | 418 | searchthreads 419 | - **Описание:** Количество параллельных потоков поиска 420 | - **Умолчание:** Обязательный параметр 421 | - **Значения:** 1-256 422 | - **Тип:** int 423 | - **Секция:** [server.name] 424 | 425 | searchtimeout 426 | - **Описание:** Задает таймаут поиска 427 | - **Умолчание:** Обязательный параметр 428 | - **Значения:** 1-86400 429 | - **Тип:** int 430 | - **Секция:** [server.name] 431 | 432 | nonunique 433 | - **Описание:** Если включено, тогда будет возможно загрузить файл с не уникальным именем в директорию, где уже есть Bolt архив, и в этом Bolt архиве есть файл или значение с тем же именем ключа, что и загружаемый отдельный файл. В случае если этот параметр выключен, то обратное все равно возможно, то есть возможно загрузить файл или значение именно в Bolt архив, даже если уже есть отдельный файл в той же директории с тем же именем, что и имя ключа, загружаемого в Bolt архив файла или значения. 434 | - **Умолчание:** Обязательный параметр 435 | - **Значения:** true или false 436 | - **Тип:** bool 437 | - **Секция:** [server.name] 438 | 439 | cctrl 440 | - **Описание:** Устанавливает заголовок Cache-Control. 441 | - **Умолчание:** Обязательный параметр 442 | - **Значения:** 0-2147483647 443 | - **Тип:** int 444 | - **Секция:** [server.name] 445 | 446 | buffers options 447 | - **Описание:** Параметры буферов чтения и записи. Все буферы могут увеличены в несколько раз для сетей с 10-100 Gbit пропускной способностью. Каждый следующий буфер не может быть меньше или равен предыдущему буферу. Требуется увеличивать при условии, что у вас действительно есть большие файлы. В данном примере установлены минимально возможные значения у всех буферов, кроме minbuffer, его минимально возможное значение равно 4096 байт. Для большинства задач, параметров по умолчанию вполне достаточно. 448 | - **Умолчание:** Обязательный параметр 449 | - **Тип:** int 450 | - **Секция:** [global] 451 | 452 | - minbuffer = 262144 # минимальный буфер памяти, при не превышении данного значения используется буфер еще меньший. 453 | - lowbuffer = 1048576 # малый буфер памяти, при превышении данного значения используется minbuffer буфер памяти. 454 | - medbuffer = 67108864 # средний буфер памяти, при превышении данного значения используется lowbuffer буфер памяти. 455 | - bigbuffer = 536870912 # не совсем буфер памяти, при превышении данного значения используется medbuffer буфер памяти. 456 | 457 | filemode 458 | - **Описание:** Права создания Bolt архивов, файлов и виртуальных атрибутов в бинарном заголовке внутри значений в Bolt архивах. 459 | - **Умолчание:** Обязательный параметр 460 | - **Значения:** 0600-0666 461 | - **Тип:** uint32 462 | - **Секция:** [server.name] 463 | 464 | dirmode 465 | - **Описание:** Права создания директорий. 466 | - **Умолчание:** Обязательный параметр 467 | - **Значения:** 0700-0777 468 | - **Тип:** uint32 469 | - **Секция:** [server.name] 470 | 471 | delbolt 472 | - **Описание:** Если включено, тогда будет разрешено напрямую удалять Bolt архивы. 473 | - **Умолчание:** Обязательный параметр 474 | - **Значения:** true или false 475 | - **Тип:** bool 476 | - **Секция:** [server.name] 477 | 478 | deldir 479 | - **Описание:** Если включено, тогда wZD сервер будет удалять последнюю пустую директорию, при условии, что реально отсутствуют файлы, поддиректории и количество ключей в Bolt архиве = 0. Но удаляется только текущая директория, рекурсивного обхода не реализовано в целях безопасности. 480 | - **Умолчание:** Обязательный параметр 481 | - **Значения:** true или false 482 | - **Тип:** bool 483 | - **Секция:** [server.name] 484 | 485 | gzstatic 486 | - **Описание:** Если включено, тогда wZD сервер будет выдавать сжатое содержимое файлов с расширением .gz, если они существуют, при GET запросе не включающим изначально расширение gz. Устанавливает заголовок Content-Encoding: gzip, если клиент присылает Accept-Encoding c типом gzip. Content-Encoding: gzip проставляется даже без включения параметра gzstatic для соответствующих файлов. Для файлов из Bolt архивов логика такая же. Приоритет выдачи gzip файлов. 487 | - **Умолчание:** Обязательный параметр 488 | - **Значения:** true или false 489 | - **Тип:** bool 490 | - **Секция:** [server.name] 491 | 492 | log4xx 493 | - **Описание:** Если включено, тогда wZD сервер будет логировать все запросы с кодами 4XX. 494 | - **Умолчание:** Обязательный параметр 495 | - **Значения:** true или false 496 | - **Тип:** bool 497 | - **Секция:** [server.name] 498 | -------------------------------------------------------------------------------- /OPTIONS.md: -------------------------------------------------------------------------------- 1 | wZD Logo 2 | 3 | wZD Parameters: 4 | ======== 5 | 6 | Section [command line] 7 | ------------ 8 | 9 | - -config string 10 | --config=/etc/wzd/wzd.conf (default is "/etc/wzd/wzd.conf") 11 | - -debug 12 | --debug - debug mode 13 | - -help 14 | --help - displays help 15 | - -version 16 | --version - print version 17 | 18 | Section [global] 19 | ------------ 20 | 21 | bindaddr 22 | - **Description**: This is the primary address and TCP port. The value is ": 9699" for all addresses. 23 | - **Default:** "127.0.0.1:9699" 24 | - **Type:** string 25 | - **Section:** [global] 26 | 27 | bindaddrssl 28 | - **Description:** This is the primary SSL address and TCP port. The value is ": 9699" for all addresses. 29 | - **Default:** "127.0.0.1:9799" 30 | - **Type:** string 31 | - **Section:** [global] 32 | 33 | onlyssl 34 | - **Description:** This globally disables standart HTTP address and port for all virtual hosts. It is not configured for a virtual host. 35 | - **Default:** false 36 | - **Values:** true or false 37 | - **Type:** bool 38 | - **Section:** [global] 39 | 40 | readtimeout 41 | - **Description:** This sets the timeout for the maximum data transfer time from the client to the server. It should be increased to transfer large files. If the wZD server is installed behind Nginx or HAProxy, this timeout can be disabled by setting it to 0 (no timeout). It is not configured for a virtual host. 42 | - **Default:** 60 43 | - **Values:** 0-86400 44 | - **Type:** int 45 | - **Section:** [global] 46 | 47 | readheadertimeout 48 | - **Description:** This sets the timeout for the maximum time for receiving headers from the client. If the wZD server is installed behind Nginx or HAProxy, this timeout can be disabled by setting it to 0. If the parameter = 0, the readtimeout parameter is taken. If the readtimeout = 0, the readheadertimeout timeout is not used (no timeout). It is not configured for a virtual host. 49 | - **Default:** 5 50 | - **Values:** 0-86400 51 | - **Type:** int 52 | - **Section:** [global] 53 | 54 | writetimeout 55 | - **Description:** This sets the timeout for the maximum data transfer time to the client. The transfer of large files should be significantly increased. If the wZD server is installed behind Nginx or HAProxy, this timeout can be disabled by setting it to 0 (no timeout). It is not configured for a virtual host. 56 | - **Default:** 60 57 | - **Values:** 0-86400 58 | - **Type:** int 59 | - **Section:** [global] 60 | 61 | idletimeout 62 | - **Description:** This sets the timeout for the maximum lifetime of keep alive connections. If the parameter = 0, the readtimeout parameter value is taken. If the readtimeout = 0, the idletimeout timeout is not used (no timeout). It is not configured for a virtual host. 63 | - **Default:** 60 64 | - **Values:** 0-86400 65 | - **Type:** int 66 | - **Section:** [global] 67 | 68 | keepalive 69 | - **Description:** This globally enables or disables keep alive. It is not configured for a virtual host. 70 | - **Default:** false 71 | - **Values:** true or false 72 | - **Type:** bool 73 | - **Section:** [global] 74 | 75 | realheader 76 | - **Description:** This is the real IP address header from the reverse proxy. It is not configured for a virtual host. 77 | - **Default:** "X-Real-IP" 78 | - **Type:** string 79 | - **Section:** [global] 80 | 81 | charset 82 | - **Description:** This is the encoding used for the entire server. It is not configured for a virtual host. 83 | - **Default:** "UTF-8" 84 | - **Type:** string 85 | - **Section:** [global] 86 | 87 | debugmode 88 | - **Description:** This globally enables debug mode. It is not configured for a virtual host. 89 | - **Default:** false 90 | - **Values:** true or false 91 | - **Type:** bool 92 | - **Section:** [global] 93 | 94 | gcpercent 95 | - **Description:** This globally sets the garbage collection parameter (percent). -1 disables the garbage collector. It is not configured for a virtual host. 96 | - **Default:** 25 97 | - **Values:** -1-100 98 | - **Type:** int 99 | - **Section:** [global] 100 | 101 | freelist 102 | - **Description:** This globally sets the type of page release algorithm in BoltDB. The hashmap algorithm is faster. It is not configured for a virtual host. 103 | - **Default:** hashmap 104 | - **Values:** hashmap or array 105 | - **Type:** string 106 | - **Section:** [global] 107 | 108 | search 109 | - **Description:** This globally enables or disables search. 110 | - **Default:** true 111 | - **Values:** true or false 112 | - **Type:** bool 113 | - **Section:** [global] 114 | 115 | searchcache 116 | - **Description:** Globally sets the size of the search cache. Values greater than 1/1024 of the cache size do not fit into the cache. Not configured for virtual host. (bytes) 117 | - **Default:** 134217728 118 | - **Values:** 33554432-2147483647 119 | - **Type:** int 120 | - **Section:** [global] 121 | 122 | searchdir 123 | - **Description:** Directory for search database. The database is filled at the first start of the server. You can safely remove data and reinitialize again. 124 | - **Default:** "/var/lib/wzd/search" 125 | - **Type:** string 126 | - **Section:** [global] 127 | 128 | searchinit 129 | - **Description:** The number of parallel threads during initialization of the search database. 130 | - **Default:** 4 131 | - **Values:** 1-256 132 | - **Type:** int 133 | - **Section:** [global] 134 | 135 | searchindex 136 | - **Description:** Search database location. The order of performance in descending order is ram, disk, bpt. 137 | - **Default:** "ram" 138 | - **Values:** "ram" or "disk" or "bpt" 139 | - **Type:** string 140 | - **Section:** [global] 141 | 142 | cmpsched = true 143 | - **Description:** This globally enables or disables the automatic compaction/defragmentation manager for Bolt archives. 144 | - **Default:** false 145 | - **Values:** true or false 146 | - **Type:** bool 147 | - **Section:** [global] 148 | 149 | cmpdir 150 | - **Description:** This is the directory of the technical database. There is little technical data for the compaction/defragmentation manager. 151 | - **Default:** /var/lib/wzd 152 | - **Type:** string 153 | - **Section:** [global] 154 | 155 | cmptime = 30 156 | - **Description:** This is the compaction/defragmentation delay timeout for the updated Bolt archives (days). 157 | - **Default:** 30 158 | - **Values:** 1-1000 159 | - **Type:** int 160 | - **Section:** [global] 161 | 162 | cmpcheck = 5 163 | - **Description:** This is the start interval of the automatic compaction/defragmentation manager (seconds). 164 | - **Default:** 5 165 | - **Values:** 1-5 166 | - **Type:** int 167 | - **Section:** [global] 168 | 169 | pidfile 170 | - **Description:** This is the PID file path. 171 | - **Default:** "/run/wzd/wzd.pid" 172 | - **Type:** string 173 | - **Section:** [global] 174 | 175 | logdir 176 | - **Description:** This is the path to the log directory. 177 | - **Default:** "/var/log/wzd" 178 | - **Type:** string 179 | - **Section:** [global] 180 | 181 | logmode 182 | - **Description:** This sets the permissions for the log files (mask). 183 | - **Default:** 0640 184 | - **Values:** 0600-0666 185 | - **Type:** uint32 186 | - **Section:** [global] 187 | 188 | defsleep 189 | - **Description:** This sets the sleep time between attempts to open Bolt archive (seconds). 190 | - **Default:** 1 191 | - **Values:** 1-5 192 | - **Type:** int 193 | - **Section:** [global] 194 | 195 | Section [server] and subsections [server.name] 196 | ------------ 197 | 198 | [server.name] 199 | - **Description:** This is the primary internal identifier of the virtual host. After "." any name can be used. This is not a domain name, only an internal identifier. 200 | - **Type:** string 201 | - **Section:** [server] 202 | 203 | host 204 | - **Description:** This is the virtual host name. The value * or _ is not supported. To convert multiple virtual hosts to one virtual host in a wZD server, use Nginx or HAProxy, or any other reverse proxy server with a hard-set "proxy_set_header Host hostname;" (using Nginx as an example) where hostname = host in the wZD server virtual host. 205 | - **Default:** Required 206 | - **Type:** string 207 | - **Section:** [server.name] 208 | 209 | root 210 | - **Description:** This is the virtual host root directory. 211 | - **Default:** Required 212 | - **Type:** string 213 | - **Section:** [server.name] 214 | 215 | sslcrt 216 | - **Description:** This is the path to the file with SSL certificate of virtual host. 217 | - **Default:** "" 218 | - **Type:** string 219 | - **Section:** [server.name] 220 | 221 | sslkey 222 | - **Description:** This is the path to the file with SSL key of virtual host. 223 | - **Default:** "" 224 | - **Type:** string 225 | - **Section:** [server.name] 226 | 227 | getallow 228 | - **Description:** This is the path to the file of allowed IP addresses of the GET/HEAD/OPTIONS methods for the virtual host. If the file is not specified, a 403 error will always be returned. 229 | - **Default:** "" 230 | - **Type:** string 231 | - **Section:** [server.name] 232 | 233 | putallow 234 | - **Description:** This is the path to the file of allowed IP addresses of the PUT/POST/PATCH methods for the virtual host. If the file is not specified, a 403 error will always be returned. 235 | - **Default:** "" 236 | - **Type:** string 237 | - **Section:** [server.name] 238 | 239 | delallow 240 | - **Description:** This is the path to the file of allowed IP addresses of the DELETE method for the virtual host. If the file is not specified, a 403 error will always be returned. 241 | - **Default:** "" 242 | - **Type:** string 243 | - **Section:** [server.name] 244 | 245 | options 246 | - **Description:** Sets the Access-Control-Allow-Methods header with the available methods for the virtual host. 247 | - **Default:** "GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE" 248 | - **Type:** string 249 | - **Section:** [server.name] 250 | 251 | headorigin 252 | - **Description:** Sets the Access-Control-Allow-Origin header. 253 | - **Default:** "*" 254 | - **Type:** string 255 | - **Section:** [server.name] 256 | 257 | xframe 258 | - **Description:** Sets the X-Frame-Options header. 259 | - **Default:** "sameorigin" 260 | - **Type:** string 261 | - **Section:** [server.name] 262 | 263 | upload 264 | - **Description:** This enables or disables the PUT method for the virtual host. 265 | - **Default:** Required 266 | - **Values:** true or false 267 | - **Type:** bool 268 | - **Section:** [server.name] 269 | 270 | delete 271 | - **Description:** This enables or disables the DELETE method for the virtual host. 272 | - **Default:** Required 273 | - **Values:** true or false 274 | - **Type:** bool 275 | - **Section:** [server.name] 276 | 277 | compaction 278 | - **Description:** This enables or disables the automatic addition of tasks for the compaction/defragmentation manager of the Bolt archives when updating or deleting files or values in Bolt archives, if parameter cmpsched is not disabled globally. 279 | - **Default:** Required 280 | - **Values:** true or false 281 | - **Type:** bool 282 | - **Section:** [server.name] 283 | 284 | writeintegrity 285 | - **Description:** This enables or disables the calculation and recording of the checksum in the binary header while uploading files or values through the wZD server to Bolt archives. It is recommended that this be enabled. 286 | - **Default:** Required 287 | - **Values:** true or false 288 | - **Type:** bool 289 | - **Section:** [server.name] 290 | 291 | readintegrity 292 | - **Description:** This enables or disables checksum verification from the binary header during the output of files or values to the client. It is recommended that this be enabled. 293 | - **Default:** Required 294 | - **Values:** true or false 295 | - **Type:** bool 296 | - **Section:** [server.name] 297 | 298 | trytimes 299 | - **Description:** This is the number of attempts to obtain a virtual lock of the Bolt archive before returning an HTTP error (number). 300 | - **Default:** Required 301 | - **Values:** 1-1000 302 | - **Type:** int 303 | - **Section:** [server.name] 304 | 305 | opentries 306 | - **Description:** This is the number of attempts to open Bolt archive before returning an HTTP error (number). 307 | - **Default:** Required 308 | - **Values:** 1-1000 309 | - **Type:** int 310 | - **Section:** [server.name] 311 | 312 | locktimeout 313 | - **Description:** This is the maximum timeout for opening the Bolt archive in 1 attempt (seconds). 314 | - **Default:** Required 315 | - **Values:** 1-3600 316 | - **Type:** int 317 | - **Section:** [server.name] 318 | 319 | skeyscnt 320 | - **Description:** The maximum allowable number of files and/or keys per Bolt archive. If this parameter is exceeded, the file or value will be uploaded to the next Bolt archive in this directory. Recommended size not more than 65536. 321 | - **Default:** Required 322 | - **Values:** 4096-131072 323 | - **Type:** int 324 | - **Section:** [server.name] 325 | 326 | smaxsize 327 | - **Description:** The maximum size of the Bolt archive. If this parameter is exceeded, then the file or value will be loaded into the next Bolt archive in the same directory. (bytes) 328 | - **Default:** Required 329 | - **Values:** 33554432-1073741824 330 | - **Type:** int64 331 | - **Section:** [server.name] 332 | 333 | fmaxsize 334 | - **Description:** This is the maximum size of the uploaded file or value in the Bolt archive. If this parameter is exceeded, the file or value will be loaded as a separate file with the same path. The recommended size is not more than 1048576 (1 MB). It is not recommended uploading files or values to Bolt archives that are larger than 16 MB. Such files or values must be stored separately (bytes). 335 | - **Default:** Required 336 | - **Values:** 1-33554432 337 | - **Type:** int64 338 | - **Section:** [server.name] 339 | 340 | vmaxsize 341 | - **Description:** This is the maximum size of the requested file or value when searching with the WithValue header. If this parameter is exceeded for a specific file or value, the value will not be displayed in the JSON document. Recommended size no more than 4096 (4 KB). It is not recommended to request values in the search if the files or values are larger than 16 KB (bytes). 342 | - **Default:** Required 343 | - **Values:** 1-262144 344 | - **Type:** int64 345 | - **Section:** [server.name] 346 | 347 | args 348 | - **Description:** If this is disabled, query arguments will be denied for GET requests. It is recommended that this be enabled if using Vary header on a reverse proxy server, or if versioning through query arguments. 349 | - **Default:** Required 350 | - **Values:** true or false 351 | - **Type:** bool 352 | - **Section:** [server.name] 353 | 354 | getbolt 355 | - **Description:** If this is disabled, direct download of Bolt archives will be forbidden. 356 | - **Default:** Required 357 | - **Values:** true or false 358 | - **Type:** bool 359 | - **Section:** [server.name] 360 | 361 | getkeys 362 | - **Description:** If this is disabled, getting file or key names (including individual files) from the directory will be forbidden. 363 | - **Default:** Required 364 | - **Values:** true or false 365 | - **Type:** bool 366 | - **Section:** [server.name] 367 | 368 | getinfo 369 | - **Description:** If this is disabled, getting file or key names with addition information (including individual files) from the directory will be forbidden. 370 | - **Default:** Required 371 | - **Values:** true or false 372 | - **Type:** bool 373 | - **Section:** [server.name] 374 | 375 | getsearch 376 | - **Description:** If this is disabled, then it will be forbidden to use KeysSearch* and Expression search headers. 377 | - **Default:** Required 378 | - **Values:** true or false 379 | - **Type:** bool 380 | - **Section:** [server.name] 381 | 382 | getrecursive 383 | - **Description:** If this is disabled, then a recursive search for file names or keys will be prohibited. 384 | - **Default:** Required 385 | - **Values:** true or false 386 | - **Type:** bool 387 | - **Section:** [server.name] 388 | 389 | getjoin 390 | - **Description:** If disabled, then joining with recursion during the search for file names or keys will be prohibited. 391 | - **Default:** Required 392 | - **Values:** true or false 393 | - **Type:** bool 394 | - **Section:** [server.name] 395 | 396 | getvalue 397 | - **Description:** If this is disabled, then it will be forbidden to get the values of files or keys (including individual files) in the search results through the KeysSearch* headers with WithValue header. 398 | - **Default:** Required 399 | - **Values:** true or false 400 | - **Type:** bool 401 | - **Section:** [server.name] 402 | 403 | getcount 404 | - **Description:** If this is disabled, getting a count of the total number of files or values (including individual files) in the directory will be forbidden. 405 | - **Default:** Required 406 | - **Values:** true or false 407 | - **Type:** bool 408 | - **Section:** [server.name] 409 | 410 | getcache 411 | - **Description:** If this is disabled, then it will be forbidden to set the lifetime of search results in the search cache through the Expire header. 412 | - **Default:** Required 413 | - **Values:** true or false 414 | - **Type:** bool 415 | - **Section:** [server.name] 416 | 417 | searchthreads 418 | - **Description:** Number of parallel search threads 419 | - **Default:** Required 420 | - **Values:** 1-256 421 | - **Type:** int 422 | - **Section:** [server.name] 423 | 424 | searchtimeout 425 | - **Description:** This sets search timeout 426 | - **Default:** Required 427 | - **Values:** 1-86400 428 | - **Type:** int 429 | - **Section:** [server.name] 430 | 431 | nonunique 432 | - **Description:** If this is enabled, it is then possible to upload a file with a non-unique name to a directory where there is already a Bolt archive, and where the Bolt archive includes a file or value with the same key name as the separate file to be uploaded. If this parameter is turned off, the reverse will be possible. That is, it will be possible to upload a file or value to the Bolt archive, even if there is already a separate file in the same directory with the same name as the name of the key loaded into the Bolt file archive or value. 433 | - **Default:** Required 434 | - **Values:** true or false 435 | - **Type:** bool 436 | - **Section:** [server.name] 437 | 438 | cctrl 439 | - **Description:** This sets the Cache-Control header. 440 | - **Default:** Required 441 | - **Values:** 0-2147483647 442 | - **Type:** int 443 | - **Section:** [server.name] 444 | 445 | buffers options 446 | - **Description:** This controls the read and write buffers. All buffers can be increased several times for networks with 10-100 Gbit bandwidth. Each subsequent buffer cannot be less than or equal to the previous one. Increasing the buffer is required for very large files. Here, the minimum possible values are set for all buffers except minbuffer, which has a minimum possible value of 4096 bytes. The default options are sufficient for most tasks. 447 | - **Default:** Required 448 | - **Type:** int 449 | - **Section:** [global] 450 | 451 | - minbuffer = 262144 # -- minimum memory buffer. If this value is not exceeded, an even smaller buffer is used. 452 | - lowbuffer = 1048576 # -- small memory buffer. If this value is exceeded, the minbuffer memory buffer is used. 453 | - medbuffer = 67108864 # -- medium memory buffer. If this value is exceeded, the lowbuffer memory buffer is used. 454 | - bigbuffer = 536870912 # -- not quite a memory buffer. When this value is exceeded, the medbuffer memory buffer is used. 455 | 456 | filemode 457 | - **Description:** This sets the permissions to create Bolt archives, files and virtual attributes in a binary header inside values in Bolt archives. 458 | - **Default:** Required 459 | - **Values:** 0600-0666 460 | - **Type:** uint32 461 | - **Section:** [server.name] 462 | 463 | dirmode 464 | - **Description:** This sets directory creation permissions. 465 | - **Default:** Required 466 | - **Values:** 0700-0777 467 | - **Type:** uint32 468 | - **Section:** [server.name] 469 | 470 | delbolt 471 | - **Description:** If this is enabled, direct deletion of Bolt archives will be allowed. 472 | - **Default:** Required 473 | - **Values:** true or false 474 | - **Type:** bool 475 | - **Section:** [server.name] 476 | 477 | deldir 478 | - **Description:** If this is enabled, then the wZD server will delete the last empty directory, provided there really are no files or subdirectories and the number of keys in the Bolt archive = 0. Only the current directory is deleted. A recursive traversal is not implemented for security reasons. 479 | - **Default:** Required 480 | - **Values:** true or false 481 | - **Type:** bool 482 | - **Section:** [server.name] 483 | 484 | gzstatic 485 | - **Description:** If this is enabled, then the wZD server will output the compressed contents of files with the .gz extension, if they exist, with a GET request that does not initially include the gz extension. Sets the Content-Encoding: gzip header if the client sends Accept-Encoding header with the gzip type. Content-Encoding: gzip is affixed even without the gzstatic option enabled for the corresponding files. For files from Bolt archives, the logic is the same. Priority of issuing gzip files. 486 | - **Default:** Required 487 | - **Values:** true or false 488 | - **Type:** bool 489 | - **Section:** [server.name] 490 | 491 | log4xx 492 | - **Description:** If this is enabled, then the wZD server will log all requests with 4XX codes. 493 | - **Default:** Required 494 | - **Values:** true or false 495 | - **Type:** bool 496 | - **Section:** [server.name] 497 | -------------------------------------------------------------------------------- /README-RUS.md: -------------------------------------------------------------------------------- 1 | wZD Logo 2 | 3 | wZD это сервер написанный на языке Go, использующий модифицированную версию BoltDB баз, 4 | как бекенд для сохранения и раздачи любого количества маленьких и больших файлов, NoSQL ключей/значений, 5 | в компактном виде внутри микро Bolt баз данных(архивов) с распределением файлов или значений в BoltDB базах в зависимости от количества директорий и поддиректорий, и общей структуры директорий. При помощи wZD можно навсегда решить проблему большого количества файлов на любой POSIX совместимой файловой системе, в том числе кластерной. Внешне он работает как обычный WebDAV сервер. 6 | 7 | ...И миллиарды файлов больше не будут проблемой. 8 | 9 | wZD Scheme 10 | 11 | Архитектура 12 | ======== 13 | 14 | wZD Arch 15 | 16 | Текущая стабильная версия: 1.2.1 17 | ======== 18 | 19 | - Обновление до Go 1.14 20 | - Обновление веб вреймфорка Iris 12.1.8 21 | - Переход к поддержке Go модулей 22 | 23 | Возможности 24 | ======== 25 | 26 | - Многопоточность 27 | - Мультисерверность для отказоустойчивости и балансировки нагрузки 28 | - Полноценный поиск файлов и значений 29 | - Поддержка HTTPS и IP авторизации 30 | - Поддерживаемые HTTP методы: GET, HEAD, OPTIONS, PUT, POST и DELETE 31 | - Управление поведением при чтении и записи через клиентские заголовки 32 | - Поддержка гибко настраиваемых виртуальных хостов 33 | - Линейное масштабирование чтения и записи при использовании кластерных файловых систем 34 | - Эффективные методы чтения и записи данных 35 | - Поддержка CRC целостности данных при записи/чтении 36 | - Поддержка заголовков Range и Accept-Ranges, If-None-Match и If-Modified-Since 37 | - Позволяет хранить и раздавать в 10000 раз больше файлов, чем есть inodes на любой POSIX совместимой файловой системе, зависит от планирования структуры директорий 38 | - Поддержка добавления, обновления, удаления файлов и значений, и отложенной компакции/дефрагментации Bolt архивов 39 | - Позволяет использовать сервер как NoSQL базу с легким шардингом на базе структуры директорий 40 | - Bolt архивы поддерживают выборочное чтение определенного количества байт из значения 41 | - Легкий шардинг данных по тысячам/миллионам Bolt архивам на базе структуры директорий 42 | - Поддержка смешанного режима, большие файлы могут сохраняться отдельно от Bolt архивов 43 | - Полу-динамические буферы для минимального потребления памяти и оптимальной настройки сетевой производительности 44 | - В дополнение предлагается многопоточный архиватор wZA для миграции файлов без остановки сервиса 45 | 46 | Несовместимости 47 | ======== 48 | 49 | - Не поддерживается Multipart 50 | - Пока нет нативного протокола и драйверов для разных языков программирования 51 | - Пока нет возможности прозрачно примонтировать структуру как файловую систему через WebDAV или FUSE 52 | - Сервер не поддерживает рекурсивное удаление директорий в целях безопасности 53 | - Сервер не позволяет загружать файлы в корневую директорию виртуального хоста (касается только Bolt архивов) 54 | - В директориях и поддиректориях виртуальных хостов не допускается чужих файлов с расширением .bolt 55 | - Нельзя просто так взять и перенести диски с данными из Little Endian системы в Big Endian систему и наоборот 56 | 57 | Multipart не будет поддерживаться, так как требуется строгая запись конкретного количества данных, чтобы не образовывались недозагруженные файлы и не возникали другие проблемы 58 | 59 | Используйте только бинарный протокол передачи данных для записи файлов или значений 60 | 61 | Требования 62 | ======== 63 | 64 | - Операционные системы: Linux, BSD, Solaris и OSX 65 | - Архитектуры: AMD64, ARM64, PPC64 и MIPS64, проверялась только AMD64 66 | - Поддерживаемый порядок байт: Little или Big Endian 67 | - Любая POSIX совместимая файловая система с полноценной поддержкой блокировок (предпочтительна кластерная MooseFS) 68 | 69 | Рекомендации 70 | ======== 71 | 72 | - Рекомендуется загружать большие файлы напрямую в wZD сервер, минуя реверс прокси серверы 73 | 74 | Реальное применение 75 | ======== 76 | 77 | Наш кластер имеет порядка 250,000,000 маленьких картинок и 15,000,000 директорий на раздельных SATA дисках. Используемая кластерная файловая система MooseFS. Она неплохо работает с таким количеством файлов, но при этом ее Master серверы потребляют по 75 гигабайт оперативной памяти, и так как происходят частые дампы большого количества метаданных, то это плохо сказывается на SSD дисках. Еще соответственно есть ограничение примерно в 1 миллиард файлов в самой MooseFS при 1-ой реплике каждого файла. 78 | 79 | В среднем у нас в разрозненной структуре директорий, где хранятся от 10 до 1000 файлов в большинстве директорий, после установки wZD и архивирования файлов в Bolt архивы, получилось примерно в 25 раз меньше файлов, около 10,000,000. При правильном планировании структуры директорий можно было бы добиться и меньшего количества файлов, но какая структура уже была, такая пока и осталась. Этим мы добились очень большой экономии inodes, низкого потребления памяти кластерной ФС, значительного ускорения работы самой MooseFS, и сокращения реально занимаемого места на кластерной ФС MooseFS. Дело в том, что MooseFS выделяет под каждый файл блок всегда размером 64KB, то есть если у вас файл имеет размер 3KB, то выделено все равно будет 64KB. 80 | 81 | Многопоточный wZA архиватор уже протестирован на реальных данных. 82 | 83 | Наш кластер (10 серверов) это Origin сервер, установленный за CDN сетью и обслуживаемый всего 2-мя wZD серверами. 84 | 85 |

86 | 87 |

88 | 89 | Смешанное применение 90 | ======== 91 | 92 | wZD разрабатывался как сервер для смешанного применения. Можно записывать не только обычные файлы, но даже прегенерированные html или json документы, и даже просто использовать как шардинговую NoSQL базу состоящую из большого количества маленьких BoltDB баз данных, а весь шардинг вести через структуру директорий и поддиректорий. 93 | 94 | Тесты производительности 95 | ======== 96 | 97 | **Тестирование показывает разницу чтения/записи между работой с обычными файлами и Bolt архивами. Параметры writeintegrity и readintegrity включены, то есть при записи или чтении файлов, в Bolt архивах используется CRC.** 98 | 99 | **Важно: время в тестах указано за полноценные GET/PUT запросы, в эти миллисекунды включены полная запись или чтение файлов HTTP клиентом.** 100 | 101 | Тесты проводились на SSD дисках, так как на SATA дисках тесты получаются не очень объективными, не видно четкой разницы между работой с Bolt архивами и обычными файлами. 102 | 103 | В тесте участвуют файлы 32KB, 256KB, 1024KB, 4096KB, 32768KB. 104 | 105 | - GET 1000 файлов и GET 1000 файлов из 1000 Bolt архивов 106 | 107 | 108 | 109 | - PUT 1000 файлов и PUT 1000 файлов в 1000 Bolt архивов 110 | 111 | 112 | 113 | Как видно по графикам, разница практически не значительная. 114 | 115 | Ниже предлагается более наглядный тест с файлами размером в 32 мегабайта. В этом случае запись в Bolt архивы становится более медленной, по сравнению с записью обычных файлов. Хотя это как считать, запись 32MB за 250ms в общем-то достаточно быстро. Чтение таких файлов работает вполне быстро и если требуется хранить файлы больших размеров именно в Bolt архивах, и скорость записи не очень критична, тогда вполне допускается такое использование, но не рекомендуется, и не более чем 32MB на 1 загруженный файл. 116 | 117 | - GET 32M 1000 файлов и файлов из Bolt архивов и PUT 32M 1000 файлов и файлов в Bolt архивы 118 | 119 | 120 | 121 | Документация 122 | ======== 123 | 124 | Установка 125 | -------- 126 | 127 | Установка пакетов/бинарников 128 | -------- 129 | 130 | - Скачать 131 | 132 | ``` 133 | systemctl enable wzd && systemctl start wzd 134 | ``` 135 | 136 | Установка docker образа 137 | -------- 138 | 139 | - **Docker образ автоматически рекурсивно меняет UID и GID в примонтированном /var/storage** 140 | 141 | ```bash 142 | docker run -d --restart=always -e bindaddr=127.0.0.1:9699 -e host=localhost -e root=/var/storage \ 143 | -v /var/storage:/var/storage --name wzd -p 9699:9699 eltaline/wzd 144 | ``` 145 | 146 | Более расширенный вариант: 147 | 148 | ```bash 149 | docker run -d --restart=always -e bindaddr=127.0.0.1:9699 -e host=localhost -e root=/var/storage \ 150 | -e upload=true -e delete=true -e compaction=true -e search=true -e fmaxsize=1048576 \ 151 | -e writeintegrity=true -e readintegrity=true -e args=false \ 152 | -e getbolt=false -e getkeys=true -e getinfo=true -e getsearch=true \ 153 | -e getrecursive=true -e getjoin=true -e getvalue=true -e getcount=true -e getcache=true \ 154 | -e nonunique=false -e cctrl=2592000 -e delbolt=false -e deldir=false \ 155 | -v /var/storage:/var/storage --name wzd -p 9699:9699 eltaline/wzd 156 | ``` 157 | 158 | Все ENV параметры по умолчанию можно посмотреть тут: Dockerfile 159 | 160 | - Включите ротацию на хост системе для контейнеров: 161 | 162 | Положите в /etc/logrotate.d/wzd 163 | 164 | ``` 165 | /var/lib/docker/containers/*/*.log { 166 | rotate 7 167 | daily 168 | compress 169 | missingok 170 | delaycompress 171 | copytruncate 172 | } 173 | ``` 174 | 175 | Настройка и использование wZD сервера 176 | -------- 177 | 178 | **Если вы устанавливаете wZD из пакетов deb или rpm, или из бинарников, то параметры upload и delete выключены по умолчанию в конфигурационном файле /etc/wzd/wzd.conf, в виртуальном хосте localhost, в целях безопасности.** 179 | 180 | В большинстве случаев достаточно воспользоваться конфигурационным файлом по умолчанию. Полное описание всех параметров продукта доступно здесь: Опции 181 | 182 | Основные методы 183 | -------- 184 | 185 | Скачивание файла (приоритетно скачивается существующий обычный файл, а не тот, что в Bolt архиве) 186 | 187 | ```bash 188 | curl -o test.jpg http://localhost/test/test.jpg 189 | ``` 190 | 191 | Скачивание файла (принудительно) 192 | 193 | ```bash 194 | curl -o test.jpg -H "FromFile: 1" http://localhost/test/test.jpg 195 | ``` 196 | 197 | Скачивание файла из Bolt архива (принудительно) 198 | 199 | ```bash 200 | curl -o test.jpg -H "FromArchive: 1" http://localhost/test/test.jpg 201 | ``` 202 | 203 | Скачивание целого Bolt архива из директории (если серверный параметр getbolt = true) 204 | 205 | ```bash 206 | curl -o test.bolt http://localhost/test/test.bolt 207 | ``` 208 | 209 | Загрузка файла в директорию в автоматическом режиме на основе параметра fmaxsize 210 | 211 | ```bash 212 | curl -X PUT --data-binary @test.jpg http://localhost/test/test.jpg 213 | ``` 214 | 215 | Загрузка файла в обычный файл 216 | 217 | ```bash 218 | curl -X PUT -H "File: 1" --data-binary @test.jpg http://localhost/test/test.jpg 219 | ``` 220 | 221 | Загрузка файла в Bolt архив (если не превышен серверный параметр fmaxsize) 222 | 223 | ```bash 224 | curl -X PUT -H "Archive: 1" --data-binary @test.jpg http://localhost/test/test.jpg 225 | ``` 226 | 227 | Удаление файла (приоритетно удалится обычный файл, если файл существует, а не файл в bolt архиве) 228 | 229 | ```bash 230 | curl -X DELETE http://localhost/test/test.jpg 231 | ``` 232 | 233 | Удаление файла (принудительно) 234 | 235 | ```bash 236 | curl -X DELETE -H "FromFile: 1" http://localhost/test/test.jpg 237 | ``` 238 | 239 | Удаление файла из Bolt архива (принудительно) 240 | 241 | ```bash 242 | curl -X DELETE -H "FromArchive: 1" http://localhost/test/test.jpg 243 | ``` 244 | 245 | Удаление целого Bolt архива из директории (если серверный параметр delbolt = true) 246 | 247 | ```bash 248 | curl -X DELETE http://localhost/test/test.bolt 249 | ``` 250 | 251 | Поиск 252 | -------- 253 | 254 | Получение списка всех имен файлов из директории и архива (если серверный параметр getkeys = true) 255 | 256 | ```bash 257 | curl -H "Sea: 1" -H "Keys: 1" http://localhost/test 258 | ``` 259 | 260 | Получение списка всех имен файлов только из директории (если серверный параметр getkeys = true) 261 | 262 | ```bash 263 | curl -H "Sea: 1" -H "KeysFiles: 1" http://localhost/test 264 | ``` 265 | 266 | Получение списка всех имен файлов только из архива (если серверный параметр getkeys = true) 267 | 268 | ```bash 269 | curl -H "Sea: 1" -H "KeysArchives: 1" http://localhost/test 270 | ``` 271 | 272 | Получение списка всех имен файлов из директории и архива с их размерами и датами (если серверный параметр getinfo = true) 273 | 274 | ```bash 275 | curl -H "Sea: 1" -H "KeysInfo: 1" http://localhost/test 276 | ``` 277 | 278 | Получение списка всех имен файлов только из директории с их размерами и датами (если серверный параметр getinfo = true) 279 | 280 | ```bash 281 | curl -H "Sea: 1" -H "KeysInfoFiles: 1" http://localhost/test 282 | ``` 283 | 284 | Получение списка всех имен файлов только из архива с их размерами и датами (если серверный параметр getinfo = true) 285 | 286 | ```bash 287 | curl -H "Sea: 1" -H "KeysInfoArchives: 1" http://localhost/test 288 | ``` 289 | 290 | Получение списка всех имен файлов из директории и архива с их размерами и датами (если серверный параметр getsearch = true) 291 | 292 | ```bash 293 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "Expression: (\.jpg$)" http://localhost/test 294 | ``` 295 | 296 | Получение списка всех имен файлов только из директории с их размерами и датами (если серверный параметр getsearch = true) 297 | 298 | ```bash 299 | curl -H "Sea: 1" -H "KeysSearchFiles: 1" -H "Expression: (\.jpg$)" http://localhost/test 300 | ``` 301 | 302 | Получение списка всех имен файлов только из архива с их размерами и датами (если серверный параметр getsearch = true) 303 | 304 | ```bash 305 | curl -H "Sea: 1" -H "KeysSearchArchives: 1" -H "Expression: (\.jpg$)" http://localhost/test 306 | ``` 307 | 308 | Получение подсчета всех файлов из директории и архива (если серверный параметр getcount = true) 309 | 310 | ```bash 311 | curl -H "Sea: 1" -H "KeysCount: 1" http://localhost/test 312 | ``` 313 | 314 | Получение подсчета всех файлов только из директории (если серверный параметр getcount = true) 315 | 316 | ```bash 317 | curl -H "Sea: 1" -H "KeysCountFiles: 1" http://localhost/test 318 | ``` 319 | 320 | Получение подсчета всех файлов только из архива (если серверный параметр getcount = true) 321 | 322 | ```bash 323 | curl -H "Sea: 1" -H "KeysCountArchives: 1" http://localhost/test 324 | ``` 325 | 326 | Расширенный поиск 327 | -------- 328 | 329 | - **Заголовки ```Keys, KeysInfo``` так же поддерживают все заголовки поиска, кроме заголовка ```WithValue```** 330 | - **Заголовки ```KeysCount``` так же поддерживают все заголовки поиска, кроме заголовков ```Limit, Offset, WithValue```** 331 | - **Заголовок ```WithJoin``` не работает с заголовком ```Recursive```, но позволяет задавать рекурсию по каждой директории** 332 | - **Заголовок ```WithValue``` доступен только при условии использования заголовков ```KeysSearch``` и ```JSON``` вместе** 333 | - **Заголовок ```Prefix```, если используется вместе с заголовком ```Expression```, тогда поиск по регулярному выражению не должен включать в себя префикс** 334 | - **Заголовок ```Recursive``` поддерживает максимальную глубину рекурсии равную 3** 335 | - **Заголовок ```Offset``` работает только в однопоточном режиме** 336 | - **Заголовок ```Expire``` устанавливает время жизни один раз для конкретного запроса, повторно выдача происходит уже из кеша и время жизни у результата в кеше не обновляется** 337 | - **Совместное использование заголовков ```Expire``` и ```SkipCache``` принудительно обновляет результат и время жизни в кеше** 338 | - **При использовании заголовка ```WithValue```, значения в JSON кодируются в HEX** 339 | 340 | Поиск по регулярному выражению (если серверный параметр getsearch = true) 341 | 342 | ```bash 343 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "Expression: (\.jpg$)" http://localhost/test 344 | ``` 345 | 346 | Рекурсивный поиск (если серверный параметр getrecursive = true) 347 | 348 | ```bash 349 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "Recursive: 3" http://localhost/test 350 | ``` 351 | 352 | Поиск с сохранением результата в серверный кеш на 120 секунд (если серверный параметр getcache = true) 353 | 354 | ```bash 355 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "Expire: 120" http://localhost/test 356 | ``` 357 | 358 | Поиск с пропуском результата из серверного кеша 359 | 360 | ```bash 361 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "SkipCache: 1" http://localhost/test 362 | ``` 363 | 364 | Поиск с пропуском результата из серверного кеша и сменой значения в серверном кеше c временем жизни в 120 секунд (если серверный параметр getcache = true) 365 | 366 | ```bash 367 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "Expire: 120" -H "SkipCache: 1" http://localhost/test 368 | ``` 369 | 370 | Поиск с установленным лимитом и сдвигом 371 | 372 | ```bash 373 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "Limit: 25" -H "Offset: 100" http://localhost/test 374 | ``` 375 | 376 | Поиск с добавлением URL виртуального хоста к именам ключей 377 | 378 | ```bash 379 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "WithUrl: 1" http://localhost/test 380 | ``` 381 | 382 | Поиск при условии ограничений размеров. ```WithValue: 1``` Если любое значение превысит серверный параметр vmaxsize, то оно не будет включено в выдачу, но ключ в выдаче будет присутствовать 383 | 384 | ```bash 385 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "MinSize: 512" -H "MaxSize: 1024" -H "WithUrl: 1" -H "WithValue: 1" http://localhost/test 386 | ``` 387 | 388 | Поиск при условии соблюдения интервала даты файлов. ```WithValue: 1``` Если любое значение превысит серверный параметр vmaxsize, то оно не будет включено в выдачу, но ключ в выдаче будет присутствовать 389 | 390 | ```bash 391 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "MinStmp: 1570798400" -H "MaxStmp: 1580798400" -H "WithUrl: 1" -H "WithValue: 1" http://localhost/test 392 | ``` 393 | 394 | Поиск по префиксу 395 | 396 | ```bash 397 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "Prefix: file_" http://localhost/test 398 | ``` 399 | 400 | Поиск по префиксу и регулярному выражению c реверсивной сортировкой 401 | 402 | ```bash 403 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "Prefix: file_" -H "Expression: 10.jpg" -H "Sort: 1" http://localhost/test 404 | ``` 405 | 406 | Поиск с объединением по префиксу и регулярному выражению с указанием глубины рекурсии 407 | 408 | ```bash 409 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "WithJoin: mydir9:1 mydir2:3 mydir1/subdir4:2 mydir7/subdir1/subdir2:0" -H "Prefix: file_" -H "Expression: 10.jpg" http://localhost/ 410 | ``` 411 | 412 | Поиск до первого совпадения 413 | 414 | ```bash 415 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "Expression: 10.jpg" -H "StopFirst: 1" http://localhost/test 416 | ``` 417 | 418 | Без комментариев 419 | 420 | ```bash 421 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "Recursive: 3" -H "Expression: (\.jpg$)" -H "MinSize: 512" -H "MaxSize: 1024" -H "MinStmp: 1570798400" -H "MaxStmp: 1580798400" -H "Limit: 25" -H "Offset: 50" -H "WithUrl: 1" -H "WithValue: 1" -H "Sort: 1" -H "Expire: 3600" http://localhost/test 422 | ``` 423 | 424 | Миграция данных в 3 шага без остановки сервиса 425 | -------- 426 | 427 | Данный сервер был разработан не только с целью использования с нуля, но и на текущих реальных продакшн системах. Для этого в дополнение с wZD сервером предлагается архиватор wZA. 428 | 429 | Архиватор позволяет без удаления, с удалением, сконвертировать текущие файлы в Bolt архивы, и так же распаковать обратно. Поддерживает перезапись и другие функции. 430 | 431 | **Архиватор, насколько это возможно, максимально безопасен, не поддерживает рекурсивного обхода, только работу по заранее подготовленному списку файлов или Bolt архивов, обеспечивает повторное считывание файла после упаковки и проверку CRC контрольной суммы на лету.** 432 | 433 | Руководство по миграции данных: 434 | 435 | Путь, который подлежит миграции: /var/storage. Здесь лежат jpg файлы в 1-м миллионе поддиректорий различной глубины, которые надо упаковать в Bolt архивы, но только те файлы, которые размером не больше 1 МБ. 436 | 437 | - У вас должен уже быть настроен wZD сервер с виртуальным хостом на корневую директорию /var/storage 438 | 439 | - Выполните рекурсивный поиск: 440 | 441 | ```bash 442 | find /var/storage -type f -name '*.jpg' -not -name '*.bolt' > /tmp/migration.list 443 | ``` 444 | 445 | - Запустите архивацию без удаления текущих файлов 446 | 447 | ```bash 448 | wza --pack --fmaxsize=1048576 --list=/tmp/migration.list 449 | ``` 450 | 451 | - Запустите удаление старых файлов с проверкой ключей в Bolt архивах, без удаления файлов, которые размером больше чем fmaxsize 452 | 453 | ```bash 454 | wza --pack --delete --fmaxsize=1048576 --list=/tmp/migration.list 455 | ``` 456 | 457 | Можно совместить в одну операцию, сразу с удалением старых файлов. 458 | Архиватор пропускает не регулярные файлы и не позволит заархивировать Bolt архив в Bolt архив. 459 | Архиватор не удалит файл пока контрольная сумма считываемого файла не совпадет после архивации с вновь считанным файлом из Bolt архива, если это конечно принудительно не отключить. 460 | Пока работает wZD сервер, он отдает данные из обычных файлов, если находит их. Это приоритетно. 461 | 462 | В данном руководстве приведен пример с однопоточным запуском, остановкой при любой ошибке с любым файлом, в том числе если в заранее подготовленный список попали не регулярные файлы или же файлы вдруг были удалены другим процессом, а в списке они остались. Для игнорирования уже несуществующих в реальности файлов из списка требуется добавить параметр --ignore-not. То же верно и при распаковке. 463 | 464 | Повторный запуск архивации без параметра --overwrite не перезапишет файлы в Bolt архивах. То же верно и при распаковке. 465 | 466 | Архиватор поддерживает многопоточный вариант --threads= и так же другие опции. 467 | В многопоточном варианте автоматически применяется параметр --ignore, чтобы не останавливать работающие потоки при возникновении любых ошибок. При ошибке и включенном --delete параметре, исходный файл не удалится. 468 | 469 | Полное описание всех параметров продукта доступно здесь: Опции 470 | 471 | Примечания и Q&A 472 | ======== 473 | 474 | - По своей сути снаружи данный сервер выглядит как обычный WebDAV сервер для пользователя или разработчика 475 | 476 | - Сервер работает только с одним системным пользователем. UID и GID для любых создаваемых файлов и Bolt архивов берутся при запуске сервера из-под текущего пользователя или из systemd стартового скрипта 477 | 478 | - Сервер автоматически создает директории и Bolt архивы во время загрузки файлов или значений. Просто загрузите файл по нужному пути 479 | 480 | - Bolt архивы именуются автоматически по названию директории в которой они создаются 481 | 482 | - Эффективное уменьшение количества файлов в рамках инстанса с данными зависит от выбранной структуры директорий и планируемого количества загрузки файлов в эти директории 483 | 484 | - Не рекомендуется загружать 100000+ файлов в одну директорию (один Bolt архив), это будет оверхед. По возможности правильно планируйте структуру директорий 485 | 486 | - Не рекомендуется загружать в Bolt архивы файлы или значения размером больше 16МБ. По умолчанию параметр fmaxsize = 1048576 байт 487 | 488 | - При превышении параметра fmaxsize, даже при установленном клиентском заголовке "Archive: 1" данные загрузятся в отдельный обычный файл без уведомления. Максимально возможный размер параметра fmaxsize = 33554432 байт 489 | 490 | - Если в виртуальном хосте включен параметр nonunique = true, это означает, что wZD сервер разрешит загрузку отдельных файлов с одинаковым именем, даже если Bolt архив в этой директории уже содержит данные с тем же именем ключа, что и загружаемый файл. 491 | 492 | - Несмотря на то, что параметр nonunique = false выключен в виртуальном хосте, wZD сервер загрузит файл или значение в новый Bolt архив, даже если имя ключа совпадает с уже существующим именем файла в этой директории. Это требуется для безостановочной работы сервиса и работы в смешанном режиме во время миграции данных в Bolt архивы, в том числе при безостановочном добавлении новых файлов через PUT метод или их удалении через DELETE метод 493 | 494 | - При использовании параметров writeintegrity=true и readintegrity=true загружаемый или скачиваемый файл, или значение полностью записывается в оперативную память. Но не более 32 МБ на 1 запрос, при максимально выставленном параметре fmaxsize. Настоятельно рекомендуется включить данные параметры в true. Данные параметры влияют только на файлы или значения в Bolt архивах 495 | 496 | - Если вы забыли включить параметр writeintegrity=true и загрузили много файлов или значений в Bolt архивы, то в таком случае для них не была подсчитана контрольная сумма, но теперь вы хотите все-таки, чтобы контрольная сумма была подсчитана и записана, тогда можно воспользоваться wZA архиватором, чтобы распаковать все текущие Bolt архивы и перепаковать их снова, но уже не отключая в самом архиваторе запись CRC суммы. В дальнейшем в архиваторе будет сделана поддержка подсчета и записи контрольной суммы для файлов или значений в текущих Bolt архивах без операций распаковки и упаковки, в том случае если контрольной суммы у значений в Bolt архивах не было изначально 497 | 498 | - Если контрольная сумма у файлов или значений не была подсчитана и записана в следствие установленного параметра writeintegrity=false, то при включенном параметре readintegrity=true все будет работать, но при скачивании контрольная сумма проверяться конечно не будет 499 | 500 | - Сервер не позволяет загружать файлы в корневую директорию виртуального хоста. Это запрещено только при попытке загрузить файл или значение в корень виртуального хоста при установленном заголовке "Archive: 1". Обычные файлы без упаковки можно загружать в корень виртуального хоста 501 | 502 | - Сервер использует расширенную версию BoltDB, текущим разработчиком wZD. Добавлены функции GetLimit(), GetOffset(), GetRange(). Это позволяет считывать по-байтово из файлов или значений столько данных сколько нужно, например при использовании заголовков "Range: bytes=..." , If-None-Match, If-Modified-Since или методов HEAD и OPTIONS, что позволяет так же значительно экономить ресурсы дисковой подсистемы, чем просто так считывался бы весь файл или значение при использовании штатной функции Get() 503 | 504 | - Сервер не создает никаких временных файлов во время своей работы, и при этом потребляет мало оперативной памяти. Передача больших файлов осуществляется через настраиваемые полу-динамические буферы малого размера на лету. В wZD не используются простые функции ctx.SendFile() и ctx.ServeContent() 505 | 506 | - Некоторые параметры по желанию сообщества могут быть перенесены из секции [global] в секцию [server] 507 | - По желанию сообщества, так же может быть добавлен новый расширенный функционал. Пользуйтесь feature request 508 | 509 | ТуДу 510 | ======== 511 | 512 | - Разработка собственного репликатора и дистрибьютора + гео для возможности использования в больших системах без кластерных ФС 513 | - Возможность полного реверсивного восстановления метаданных при их полной утере(в случае использования дистрибьютора) 514 | - ~~Поддержка HTTPS протокола, возможно будет поддерживаться только в будущем дистрибьюторе~~ (Сделано в обычной версии) 515 | - Нативный протокол для возможности использования постоянных сетевых соединений и драйверы к разным языкам программирования 516 | - ~~Расширенные возможности использования NoSQL составляющей~~ (Сделано) 517 | - Реализация фонового подсчета контрольных сумм для отдельных больших файлов 518 | - Периодическая проверка контрольных сумм в фоновом режиме для защиты от bit rot 519 | - FUSE и/или WebDAV Mount, возможно будет реализована полноценная поддержка, включая запись 520 | - ~~Отказ от SQLite в пользу более простого решения (отказ от CGO).~~ (Сделано) 521 | - Компрессии разных типов (gzip, zstd, snappy) для файлов или значений внутри Bolt архивов и для обычных файлов 522 | - Шифрование разных типов для файлов или значений внутри Bolt архивов и для обычных файлов 523 | - Отложенная серверная видео конвертация, в том числе и на GPU 524 | 525 | Параметры 526 | ======== 527 | 528 | Полное описание всех параметров продукта доступно здесь: Опции 529 | 530 | HTTP Core 531 | ======== 532 | 533 | Использует Iris в качестве http ядра 534 | 535 | Гарантии 536 | ======== 537 | 538 | Никакие гарантии на данное программное обеспечение не предоставляются. Просьба сначала тестировать. 539 | 540 | Контакты 541 | ======== 542 | 543 | - Сайт компании: Eltaline 544 | 545 | ``` 546 | Copyright © 2020 Andrey Kuvshinov. Contacts: 547 | Copyright © 2020 Eltaline OU. Contacts: 548 | All rights reserved. 549 | ``` 550 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | wZD Logo 2 | 3 | Документация на русском: https://github.com/eltaline/wzd/blob/master/README-RUS.md 4 | 5 | wZD is a server written in Go language that uses a modified version of the BoltDB database as a backend for saving and distributing any number of small and large files, NoSQL keys/values, in a compact form inside micro Bolt databases (archives), with distribution of files and values in BoltDB databases depending on the number of directories or subdirectories and the general structure of the directories. Using wZD can permanently solve the problem of a large number of files on any POSIX compatible file system, including a clustered one. Outwardly it works like a regular WebDAV server. 6 | 7 | ...and billions of files will no longer be a problem. 8 | 9 | wZD Scheme 10 | 11 | Architecture 12 | ======== 13 | 14 | wZD Arch 15 | 16 | Current stable version: 1.2.1 17 | ======== 18 | 19 | - Update to Go 1.14 20 | - Update to Iris 12.1.8 21 | - Transition to Go module support 22 | 23 | Features 24 | ======== 25 | 26 | - Multi threading 27 | - Multi servers for fault tolerance and load balancing 28 | - Complete file and value search 29 | - Supports HTTPS and IP authorization 30 | - Supported HTTP methods: GET, HEAD, OPTIONS, PUT, POST and DELETE 31 | - Manage read and write behavior through client headers 32 | - Support for customizable virtual hosts 33 | - Linear scaling of read and write using clustered file systems 34 | - Effective methods of reading and writing data 35 | - Supports CRC data integrity when writing or reading 36 | - Support for Range and Accept-Ranges, If-None-Match and If-Modified-Since headers 37 | - Store and share 10,000 times more files than there are inodes on any POSIX compatible file system, depending on the directory structure 38 | - Support for adding, updating, deleting files and values, and delayed compaction/defragmentation of Bolt archives 39 | - Allows the server to be used as a NoSQL database, with easy sharding based on the directory structure 40 | - Bolt archives support for selective reading of a certain number of bytes from a value 41 | - Easy sharding of data over thousands or millions of Bolt archives based on the directory structure 42 | - Mixed mode support, with ability to save large files separately from Bolt archives 43 | - Semi-dynamic buffers for minimal memory consumption and optimal network performance tuning 44 | - Includes multi threaded wZA archiver for migrating files without stopping the service 45 | 46 | Incompatibilities 47 | ======== 48 | 49 | - Multipart is not supported 50 | - There is no native protocol and drivers for different programming languages 51 | - There is no way to transparently mount the structure as a file system via WebDAV or FUSE 52 | - For security reasons, the server does not support recursive deletion of directories 53 | - The server does not allow uploading files to the root directory of the virtual host (applies only to Bolt archives) 54 | - Directories and subdirectories of virtual hosts do not allow other people's files with the .bolt extension 55 | - Data disks cannot simply be transferred from the Little Endian system to the Big Endian system, or vice versa 56 | 57 | Multipart will not be supported, since a strict record of a specific amount of data is required so that underloaded files do not form and other problems arise. 58 | 59 | Use only binary data transfer protocol to write files or values. 60 | 61 | Requirements 62 | ======== 63 | 64 | - Operating Systems: Linux, BSD, Solaris, OSX 65 | - Architectures: amd64, arm64, ppc64 and mips64, with only amd64 tested 66 | - Supported Byte Order: Little or Big Endian 67 | - Any POSIX compatible file system with full locking support (preferred clustered MooseFS) 68 | 69 | Recommendations 70 | ======== 71 | 72 | - It is recommended to upload large files directly to the wZD server, bypassing reverse proxy servers 73 | 74 | Real application 75 | ======== 76 | 77 | Our cluster used has about 250,000,000 small pictures and 15,000,000 directories on separate SATA drives. It utilizes the MooseFS cluster file system. This works well with so many files, but at the same time, its Master servers consume 75 gigabytes of RAM, and since frequent dumps of a large amount of metadata occur, this is bad for SSD disks. Accordingly, there is also a limit of about 1 billion files in MooseFS itself with the one replica of each file. 78 | 79 | With a fragmented directory structure, an average of 10 to 1000 files are stored in most directories. After installing wZD and archiving the files in Bolt archives, it turned out about 25 times less files, about 10,000,000. With proper planning of the structure, a smaller number of files could have been achieved, but this is not possible if the already existing structure remains unchanged. Proper planning would result in very large inodes savings, low memory consumption of the cluster FS, significant acceleration of the MooseFS operation itself, and a reduction in the actual space occupied on the MooseFS cluster FS. The fact is, MooseFS always allocates a block of 64 KB for each file, that is, even if a file has a size of 3 KB, will still be allocated 64 KB. 80 | 81 | The multi threaded wZA archiver has already been tested on real data. 82 | 83 | Our cluster used (10 servers) is an Origin server installed behind a CDN network and served by only 2 wZD servers. 84 | 85 |

86 | 87 |

88 | 89 | Mixed use 90 | ======== 91 | 92 | The wZD server was designed for mixed use. One can write not only ordinary files, but even html or json generated documents, and one can even simply use NoSQL as a sharding database consisting of a large number of small BoltDB databases, and carry out all sharding through the structure of directories and subdirectories. 93 | 94 | Performance tests 95 | ======== 96 | 97 | **Testing shows the read or write difference between working with regular files and with Bolt archives. The writeintegrity and readintegrity options are enabled; that is, when writing or reading files in Bolt archives, CRC is used.** 98 | 99 | **Important: The time in the tests is indicated for full GET or PUT requests, and the full write or read of HTTP files by the client is included in these milliseconds.** 100 | 101 | Tests were carried out on SSD disks, since on SATA disks the tests are not very objective, and there is no clear difference between working with Bolt archives and ordinary files. 102 | 103 | The test involved 32 KB, 256 KB, 1024 KB, 4096 KB, and 32768 KB files. 104 | 105 | - GET 1000 files and GET 1000 files from 1000 Bolt archives 106 | 107 | 108 | 109 | - PUT 1000 files and PUT 1000 files in 1000 Bolt archives 110 | 111 | 112 | 113 | As can be seen from the graphs, the difference is practically insignificant. 114 | 115 | Below is a more visual test done with files of 32 megabytes in size. In this case, writing to Bolt archives becomes slower compared to writing to regular files. Although this is a count, writing 32 MB for 250ms is generally quite fast. Reading such files works quite quickly, and if one wants to store large files in Bolt archives, and the write speed is not critical, such use is allowed but not recommended, and not more than 32 MB per uploaded file. 116 | 117 | GET 32M 1000 files and files from Bolt archives and PUT 32M 1000 files and files in Bolt archives 118 | 119 | 120 | 121 | Documentation 122 | ======== 123 | 124 | Installation 125 | -------- 126 | 127 | Install packages or binaries 128 | -------- 129 | 130 | - Download 131 | 132 | ``` 133 | systemctl enable wzd && systemctl start wzd 134 | ``` 135 | 136 | Install docker image 137 | -------- 138 | 139 | - **Docker image automatically recursively change UID and GID in mounted /var/storage** 140 | 141 | ```bash 142 | docker run -d --restart=always -e bindaddr=127.0.0.1:9699 -e host=localhost -e root=/var/storage \ 143 | -v /var/storage:/var/storage --name wzd -p 9699:9699 eltaline/wzd 144 | ``` 145 | 146 | More advanced option: 147 | 148 | ```bash 149 | docker run -d --restart=always -e bindaddr=127.0.0.1:9699 -e host=localhost -e root=/var/storage \ 150 | -e upload=true -e delete=true -e compaction=true -e search=true -e fmaxsize=1048576 \ 151 | -e writeintegrity=true -e readintegrity=true -e args=false \ 152 | -e getbolt=false -e getkeys=true -e getinfo=true -e getsearch=true \ 153 | -e getrecursive=true -e getjoin=true -e getvalue=true -e getcount=true -e getcache=true \ 154 | -e nonunique=false -e cctrl=2592000 -e delbolt=false -e deldir=false \ 155 | -v /var/storage:/var/storage --name wzd -p 9699:9699 eltaline/wzd 156 | ``` 157 | 158 | All ENV default parameters can be viewed here: Dockerfile 159 | 160 | - Enable rotation on the host system for containers: 161 | 162 | Put in /etc/logrotate.d/wzd: 163 | 164 | ``` 165 | /var/lib/docker/containers/*/*.log { 166 | rotate 7 167 | daily 168 | compress 169 | missingok 170 | delaycompress 171 | copytruncate 172 | } 173 | ``` 174 | 175 | Configuring and using wZD server 176 | -------- 177 | 178 | **For security reasons, if wZD is installed from deb or rpm packages, or from binaries, upload and delete options are disabled by default in the configuration file /etc/wzd/wzd.conf in the localhost virtual host.** 179 | 180 | In most cases it is enough to use the default configuration file. A full description of all product parameters is available here: Options 181 | 182 | General methods 183 | -------- 184 | 185 | Downloading file (the existing normal file is downloaded first and not the one in the Bolt archive) 186 | 187 | ```bash 188 | curl -o test.jpg http://localhost/test/test.jpg 189 | ``` 190 | 191 | Downloading file from the file (forced) 192 | 193 | ```bash 194 | curl -o test.jpg -H "FromFile: 1" http://localhost/test/test.jpg 195 | ``` 196 | 197 | Downloading file from the Bolt archive (forced) 198 | 199 | ```bash 200 | curl -o test.jpg -H "FromArchive: 1" http://localhost/test/test.jpg 201 | ``` 202 | 203 | Downloading the whole Bolt archive from the directory (if the server parameter getbolt = true) 204 | 205 | ```bash 206 | curl -o test.bolt http://localhost/test/test.bolt 207 | ``` 208 | 209 | Uploading file to the directory depending on the fmaxszie parameter 210 | 211 | ```bash 212 | curl -X PUT --data-binary @test.jpg http://localhost/test/test.jpg 213 | ``` 214 | 215 | Uploading file to the regular file 216 | 217 | ```bash 218 | curl -X PUT -H "File: 1" --data-binary @test.jpg http://localhost/test/test.jpg 219 | ``` 220 | 221 | Uploading file to the Bolt archive (if the server parameter fmaxsize is not exceeded) 222 | 223 | ```bash 224 | curl -X PUT -H "Archive: 1" --data-binary @test.jpg http://localhost/test/test.jpg 225 | ``` 226 | 227 | Deleting file (a regular file is deleted first, if it exists, and not the file in the bolt archive) 228 | 229 | ```bash 230 | curl -X DELETE http://localhost/test/test.jpg 231 | ``` 232 | 233 | Deleting file (forced) 234 | 235 | ```bash 236 | curl -X DELETE -H "FromFile: 1" http://localhost/test/test.jpg 237 | ``` 238 | 239 | Deleting file from the Bolt archive (forced) 240 | 241 | ```bash 242 | curl -X DELETE -H "FromArchive: 1" http://localhost/test/test.jpg 243 | ``` 244 | 245 | Deleting the whole Bolt archive from the directory (if the server parameter delbolt = true) 246 | 247 | ```bash 248 | curl -X DELETE http://localhost/test/test.bolt 249 | ``` 250 | 251 | Search 252 | -------- 253 | 254 | Getting list of all file names from directory and archive (if the server parameter getkeys = true) 255 | 256 | ```bash 257 | curl -H "Sea: 1" -H "Keys: 1" http://localhost/test 258 | ``` 259 | 260 | Getting list of all file names only from the directory (if the server parameter getkeys = true) 261 | 262 | ```bash 263 | curl -H "Sea: 1" -H "KeysFiles: 1" http://localhost/test 264 | 265 | ``` 266 | Getting list of all file names only from the archive (if the server parameter getkeys = true) 267 | 268 | ```bash 269 | curl -H "Sea: 1" -H "KeysArchives: 1" http://localhost/test 270 | ``` 271 | 272 | Getting list of all file names from the directory and archive with their sizes and dates (if the server parameter getinfo = true) 273 | 274 | ```bash 275 | curl -H "Sea: 1" -H "KeysInfo: 1" http://localhost/test 276 | ``` 277 | 278 | Getting list of all file names only from the directory with their sizes and dates (if the server parameter getinfo = true) 279 | 280 | ```bash 281 | curl -H "Sea: 1" -H "KeysInfoFiles: 1" http://localhost/test 282 | 283 | ``` 284 | Getting list of all file names only from the archive with their sizes and dates (if the server parameter getinfo = true) 285 | 286 | ```bash 287 | curl -H "Sea: 1" -H "KeysInfoArchives: 1" http://localhost/test 288 | ``` 289 | 290 | Getting list of all file names from the directory and archive with their sizes and dates (if the server parameter getsearch = true) 291 | 292 | ```bash 293 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "Expression: (\.jpg$)" http://localhost/test 294 | ``` 295 | 296 | Getting list of all file names only from the directory with their sizes and dates (if the server parameter getsearch = true) 297 | 298 | ```bash 299 | curl -H "Sea: 1" -H "KeysSearchFiles: 1" -H "Expression: (\.jpg$)" http://localhost/test 300 | 301 | ``` 302 | Getting list of all file names only from the archive with their sizes and dates (if the server parameter getsearch = true) 303 | 304 | ```bash 305 | curl -H "Sea: 1" -H "KeysSearchArchives: 1" -H "Expression: (\.jpg$)" http://localhost/test 306 | ``` 307 | 308 | Getting count number of all files from the directory and archive (if the server parameter getcount = true) 309 | 310 | ```bash 311 | curl -H "Sea: 1" -H "KeysCount: 1" http://localhost/test 312 | ``` 313 | 314 | Getting count number of all files only from the directory (if the server parameter getcount = true) 315 | 316 | ```bash 317 | curl -H "Sea: 1" -H "KeysCountFiles: 1" http://localhost/test 318 | ``` 319 | 320 | Getting count number of all files only from the archive (if the server parameter getcount = true) 321 | 322 | ```bash 323 | curl -H "Sea: 1" -H "KeysCountArchives: 1" http://localhost/test 324 | ``` 325 | 326 | Advanced search 327 | -------- 328 | 329 | - **```Keys, KeysInfo``` headers also support all search headers except the ```WithValue``` header** 330 | - **```KeysCount``` headers also support all search headers except ```Limit, Offset, WithValue``` headers** 331 | - **```WithJoin``` header does not work with ```Recursive``` header, but allows you to set recursion for each directory** 332 | - **```WithValue``` header is only available if you use ```KeysSearch*``` and ```JSON``` headers together** 333 | - **```Prefix``` header, if used together with ```Expression``` header, then the regular expression search should not include the prefix** 334 | - **```Recursive``` header supports a maximum recursion depth of 3** 335 | - **```Offset``` header only works in single-threaded mode** 336 | - **```Expire``` header sets the lifetime once for a particular request. Other same particular request returns result from the cache and the lifetime for the result in the cache is not updated** 337 | - **Using ```Expire``` and ```SkipCache``` headers together will force updates the result and lifetime in the cache** 338 | - **When using header ```WithValue``` values are encoded by HEX** 339 | 340 | Regex search (if server parameter getsearch = true) 341 | 342 | ```bash 343 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "Expression: (\.jpg$)" http://localhost/test 344 | ```` 345 | 346 | Recursive search (if server parameter getrecursive = true) 347 | 348 | ```bash 349 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "Recursive: 3" http://localhost/test 350 | ```` 351 | 352 | Search with saving the result to the server cache for 120 seconds (if the server parameter getcache = true) 353 | 354 | ```bash 355 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "Expire: 120" http://localhost/test 356 | ```` 357 | 358 | Search with a skip result from the server cache 359 | 360 | ```bash 361 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "SkipCache: 1" http://localhost/test 362 | ```` 363 | 364 | Search with skipping the result from the server cache and changing the value in the server cache with setting new lifetime of 120 seconds (if the server parameter getcache = true) 365 | 366 | ```bash 367 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "Expire: 120" -H "SkipCache: 1" http://localhost/test 368 | ```` 369 | 370 | Search with limit and offset 371 | 372 | ```bash 373 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "Limit: 25" -H "Offset: 100" http://localhost/test 374 | ```` 375 | 376 | Search with adding the virtual host URL to the key names 377 | 378 | ```bash 379 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "WithUrl: 1" http://localhost/test 380 | ```` 381 | 382 | Search with size limits. ```WithValue: 1``` If any value exceeds the server parameter vmaxsize, then value will not be included in the output, but the key in the output will be present 383 | 384 | ```bash 385 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "MinSize: 512" -H "MaxSize: 1024" -H "WithUrl: 1" -H "WithValue: 1" http://localhost/test 386 | ```` 387 | 388 | Search with timestamp date interval. ```WithValue: 1``` If any value exceeds the server parameter vmaxsize, then value will not be included in the output, but the key in the output will be present 389 | 390 | ```bash 391 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "MinStmp: 1570798400" -H "MaxStmp: 1580798400" -H "WithUrl: 1" -H "WithValue: 1" http://localhost/test 392 | ``` 393 | 394 | Search with prefix 395 | 396 | ```bash 397 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "Prefix: file_" http://localhost/test 398 | ``` 399 | 400 | Search by prefix and regular expression with reverse sorting 401 | 402 | ```bash 403 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "Prefix: file_" -H "Expression: 10.jpg" -H "Sort: 1" http://localhost/test 404 | ``` 405 | 406 | Search with prefix and regular expression with join and with depths of recursion 407 | 408 | ```bash 409 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "WithJoin: mydir9:1 mydir2:3 mydir1/subdir4:2 mydir7/subdir1/subdir2:0" -H "Prefix: file_" -H "Expression: 10.jpg" http://localhost/ 410 | ``` 411 | 412 | Search before the first match 413 | 414 | ```bash 415 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "Expression: 10.jpg" -H "StopFirst: 1" http://localhost/test 416 | ``` 417 | 418 | No comments 419 | 420 | ```bash 421 | curl -H "Sea: 1" -H "KeysSearch: 1" -H "JSON: 1" -H "Recursive: 3" -H "Expression: (\.jpg$)" -H "MinSize: 512" -H "MaxSize: 1024" -H "MinStmp: 1570798400" -H "MaxStmp: 1580798400" -H "Limit: 25 " -H "Offset: 50" -H "WithUrl: 1" -H "WithValue: 1" -H "Sort: 1" -H "Expire: 3600" http://localhost/test 422 | ``` 423 | 424 | Data migration in 3 steps without stopping the service 425 | -------- 426 | 427 | This server was developed not only for use from scratch but also for use on current real production systems. To do this, the wZA archiver is proposed for use with the wZD server. 428 | 429 | The archiver allows for converting current files to Bolt archives without deletion and with deletion, and allows for unpacking them back. It supports overwrite and other functions. 430 | 431 | **The archiver, as far as possible, is safe. It does not support a recursive traversal, only works on a pre-prepared list of files or Bolt archives, and provides for repeated reading of the file after packing and CRC verification of the checksum on the fly.** 432 | 433 | Data migration guide: 434 | 435 | The path to be migrated: /var/storage. Here are jpg files in the 1 million subdirectories of various depths that need to be archived in Bolt archives, but only those files that are no larger than 1 MB. 436 | 437 | - wZD server should be configured with a virtual host and with a root in the /var/storage directory 438 | 439 | - Perform a recursive search: 440 | 441 | ```bash 442 | find /var/storage -type f -name '*.jpg' -not -name '*.bolt' > /tmp/migration.list 443 | ``` 444 | 445 | - Start archiving without deleting current files: 446 | 447 | ```bash 448 | wza --pack --fmaxsize=1048576 --list=/tmp/migration.list 449 | ``` 450 | 451 | - Start deleting old files with key checking in Bolt archives without deleting files that are larger than fmaxsize: 452 | 453 | ```bash 454 | wza --pack --delete --fmaxsize=1048576 --list=/tmp/migration.list 455 | ``` 456 | 457 | This can be combined into one operation with the removal of the old files. 458 | The archiver skips non-regular files and will not allow archiving of the Bolt archive to the Bolt archive. 459 | The archiver will not delete the file until the checksum of the file being read coincides, after archiving, with the newly read file from the Bolt archive, unless of course that is forced to disable. 460 | While the wZD server is running, it returns data from regular files, if it finds them. This is a priority. 461 | 462 | This guide provides an example of a single-threaded start, stopping at any error with any file, even if non-regular files were included in the prepared list or files were suddenly deleted by another process but remained in the list. To ignore already existing files from the list, the --ignore-not option must be added. The same is true when unpacking. 463 | 464 | Restarting archiving without the --overwrite option will not overwrite files in Bolt archives. The same is true when unpacking. 465 | 466 | The archiver also supports the multi threaded version --threads= and other options. 467 | In the multi threaded version, the --ignore option is automatically applied so as not to stop running threads when any errors occur. In case of an error with the --delete option turned on, the source file will not be deleted. 468 | 469 | A full description of all product parameters is available here: Options 470 | 471 | Notes and Q&A 472 | ======== 473 | 474 | - Outwardly, at its core, this server looks like a regular WebDAV server for a user or developer 475 | 476 | - The server works with only one system user. UID and GID for any Bolt archives and files are taken at server startup from the current user or from the systemd startup script 477 | 478 | - The server automatically creates directories and Bolt archives during the upload files or values. Just upload the file to the desired path 479 | 480 | - Bolt archives are automatically named with the name of the directory in which they are created 481 | 482 | - Effective reduction of the number of files within the data instance depends on the selected directory structure, and the planned number of file uploads in these directories 483 | 484 | - It is not recommended uploading 100,000+ files to one directory (one Bolt archive); this would be a large overhead. If possible, plan your directory structure correctly 485 | 486 | - It is not recommended uploading files or values larger than 16 MB to Bolt archives. By default, the parameter fmaxsize = 1048576 bytes 487 | 488 | - If the fmaxsize parameter is exceeded, even with the "Archive: 1" client header set, the data will be loaded into a separate regular file without notification. The maximum possible size of the parameter is fmaxsize = 33554432 bytes 489 | 490 | - If the nonunique = true parameter is turned on in the virtual host, this means that the wZD server will allow uploading of individual files with the same name, even if the Bolt archive in this directory already contains data with the same key name as the uploaded file 491 | 492 | - Despite the fact that the nonunique = false parameter is disabled in the virtual host, the wZD server will upload the file or value to the new Bolt archive, even if the key name matches the already existing file name in this directory. This is required for non-stop operation of the service and working in mixed mode during data migration to Bolt archives, including when adding new files non-stop through the PUT method or deleting them through the DELETE method 493 | 494 | - When using the writeintegrity = true and readintegrity = true parameters, the downloaded file or value is completely written to RAM, but no more than 32 MB per request, with the maximum parameter fmaxsize set. It is highly recommended that these options be enabled as true. These parameters affect only files or values in Bolt archives 495 | 496 | - If the writeintegrity = true parameter has not been enabled, and a lot of files or values have been uploaded to the Bolt archives, then the checksum will not have been calculated for them. In this case, for the checksum to be calculated and recorded, wZA archiver can be used to unpack all current Bolt archives and repack them again, but without disabling the CRC amount record in the archiver itself. In the future, the archiver will support the calculation and recording of the checksum for files or values in the current Bolt archives without unpacking and packing operations, if the values in the Bolt archives did not initially have a checksum 497 | 498 | - If the checksum of the files or values has not been calculated and recorded as a result of the writeintegrity = false parameter set, then with the readintegrity = true parameter enabled, everything will work, but the checksum will not be checked when downloading 499 | 500 | - The server does not allow uploading files to the root directory of the virtual host. This is prohibited only when trying to upload a file or value to the root of the virtual host with the "Archive: 1" header set. Regular files without packing can be uploaded to the root of the virtual host 501 | 502 | - The server uses an extended version of BoltDB by the current developer of wZD. Added functions are GetLimit(), GetOffset(), GetRange(). This allows as much data to be read as is needed by a byte from files or values, for example, using the headers "Range: bytes = ...", If-None-Match, If-Modified-Since, or the HEAD and OPTIONS methods, which allows the same significant saving of disk subsystem resources as simply reading the entire file or value using the standard Get() function 503 | 504 | - The server does not create any temporary files during its operation, and at the same time it consumes little RAM. Large files are transferred through customizable, semi-dynamic, small-sized buffers on the fly. The wZD server does not use the simple function ctx.SendFile() or ctx.ServeContent() 505 | 506 | - At the request of the community, some parameters can be transferred from the [global] section to the [server] section 507 | - At the request of the community, new advanced functionality can also be added. Use the feature request 508 | 509 | ToDo 510 | ======== 511 | 512 | - Development of own replicator and distributor with a geo for possible use in large systems without cluster FS 513 | - The ability to fully reverse restore metadata when it is completely lost (if using a distributor) 514 | - Native protocol for the possibility of using permanent network connections and drivers for different programming languages 515 | - ~~Support for HTTPS protocol, it may be supported only in the future distributor~~ (Completed in standart version) 516 | - ~~Advanced features for using NoSQL component~~ (Completed) 517 | - Implementing background calculate checksums for single large files 518 | - Periodic checksum checks in the background to protect against bit rot 519 | - FUSE and / or WebDAV Mount, full support may be implemented, including write support 520 | - ~~Abandoning of SQLite in favor of a simpler solution (abandoning CGO)~~ (Completed) 521 | - Different types of compression (gzip, zstd, snappy) for files or values inside Bolt archives and for ordinary files 522 | - Different types of encryption for files or values inside Bolt archives and for regular files 523 | - Server-side delayed video conversion, including on GPU 524 | 525 | Parameters 526 | ======== 527 | 528 | A full description of all product parameters is available here: Options 529 | 530 | HTTP Core 531 | ======== 532 | 533 | Uses Iris as server http core 534 | 535 | Guarantees 536 | ======== 537 | 538 | No warranty is provided for this software. Please test first 539 | 540 | Contacts 541 | ======== 542 | 543 | - Company website: Eltaline 544 | 545 | ``` 546 | Copyright © 2020 Andrey Kuvshinov. Contacts: 547 | Copyright © 2020 Eltaline OU. Contacts: 548 | All rights reserved. 549 | ``` 550 | -------------------------------------------------------------------------------- /cmp.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright © 2020 Andrey Kuvshinov. Contacts: 4 | Copyright © 2020 Eltaline OU. Contacts: 5 | All rights reserved. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | The wZD project contains unmodified/modified libraries imports too with 20 | separate copyright notices and license terms. Your use of the source code 21 | this libraries is subject to the terms and conditions of licenses these libraries. 22 | 23 | */ 24 | 25 | package main 26 | 27 | import ( 28 | "bytes" 29 | "encoding/gob" 30 | "github.com/eltaline/mmutex" 31 | "github.com/eltaline/nutsdb" 32 | "os" 33 | "time" 34 | ) 35 | 36 | // CMPScheduler : Compaction/Defragmentation scheduler 37 | func CMPScheduler(keymutex *mmutex.Mutex, cdb *nutsdb.DB) { 38 | 39 | // Variables 40 | 41 | opentries := 30 42 | trytimes := 30 43 | timeout := time.Duration(60) * time.Second 44 | 45 | // Struct 46 | 47 | type Paths struct { 48 | Key []byte 49 | Path string 50 | } 51 | 52 | var rpth []Paths 53 | var r Paths 54 | 55 | // Loggers 56 | 57 | appLogger, applogfile := AppLogger() 58 | defer applogfile.Close() 59 | 60 | // Shutdown 61 | 62 | if shutdown { 63 | return 64 | } 65 | 66 | past := time.Now().Add(time.Duration(-24*cmptime) * time.Hour) 67 | 68 | cerr := cdb.View(func(tx *nutsdb.Tx) error { 69 | 70 | var err error 71 | 72 | var entries nutsdb.Entries 73 | 74 | entries, err = tx.GetAll(cmpbucket) 75 | 76 | if entries == nil { 77 | return nil 78 | } 79 | 80 | if err != nil { 81 | return err 82 | } 83 | 84 | for _, entry := range entries { 85 | 86 | if shutdown { 87 | break 88 | } 89 | 90 | var ev Compact 91 | 92 | dec := gob.NewDecoder(bytes.NewReader(entry.Value)) 93 | err := dec.Decode(&ev) 94 | if err != nil { 95 | 96 | appLogger.Errorf("| Gob decode from compaction db error | %v", err) 97 | 98 | r.Key = entry.Key 99 | r.Path = ev.Path 100 | 101 | rpth = append(rpth, r) 102 | 103 | continue 104 | 105 | } 106 | 107 | diff := ev.Time.Sub(past) 108 | 109 | if diff < 0 { 110 | 111 | r.Key = entry.Key 112 | r.Path = ev.Path 113 | 114 | rpth = append(rpth, r) 115 | 116 | } 117 | 118 | } 119 | 120 | return nil 121 | 122 | }) 123 | 124 | if cerr != nil { 125 | appLogger.Errorf("| Work with compaction db error | %v", cerr) 126 | } 127 | 128 | for _, dbf := range rpth { 129 | 130 | if shutdown { 131 | break 132 | } 133 | 134 | var err error 135 | 136 | key := false 137 | 138 | sdbf := string(dbf.Key) 139 | 140 | for i := 0; i < trytimes; i++ { 141 | 142 | if key = keymutex.TryLock(dbf.Path); key { 143 | break 144 | } 145 | 146 | time.Sleep(defsleep) 147 | 148 | } 149 | 150 | if key { 151 | 152 | if !FileExists(dbf.Path) { 153 | 154 | appLogger.Errorf("| Can`t open db for compaction error | DB [%s] | %v", dbf.Path, err) 155 | err = NDBDelete(cdb, cmpbucket, dbf.Key) 156 | if err != nil { 157 | appLogger.Errorf("| Delete compaction task error | DB Key [%s] | %v", sdbf, err) 158 | } 159 | 160 | keymutex.UnLock(dbf.Path) 161 | continue 162 | 163 | } 164 | 165 | infile, err := os.Stat(dbf.Path) 166 | if err != nil { 167 | 168 | appLogger.Errorf("| Can`t stat file error | File [%s] | %v", dbf.Path, err) 169 | err = NDBDelete(cdb, cmpbucket, dbf.Key) 170 | if err != nil { 171 | appLogger.Errorf("| Delete compaction task error | DB Key [%s] | %v", sdbf, err) 172 | } 173 | 174 | keymutex.UnLock(dbf.Path) 175 | continue 176 | 177 | } 178 | 179 | filemode := infile.Mode() 180 | 181 | db, err := BoltOpenWrite(dbf.Path, filemode, timeout, opentries, freelist) 182 | if err != nil { 183 | 184 | appLogger.Errorf("| Can`t open db for compaction error | DB [%s] | %v", dbf.Path, err) 185 | err = NDBDelete(cdb, cmpbucket, dbf.Key) 186 | if err != nil { 187 | appLogger.Errorf("| Delete compaction task error | DB Key [%s] | %v", sdbf, err) 188 | } 189 | 190 | keymutex.UnLock(dbf.Path) 191 | continue 192 | 193 | } 194 | 195 | err = db.CompactQuietly() 196 | if err != nil { 197 | appLogger.Errorf("| Scheduled compaction task error | DB [%s] | %v", dbf.Path, err) 198 | 199 | err = NDBDelete(cdb, cmpbucket, dbf.Key) 200 | if err != nil { 201 | appLogger.Errorf("| Delete compaction task error | DB Key [%s] | %v", sdbf, err) 202 | } 203 | 204 | db.Close() 205 | keymutex.UnLock(dbf.Path) 206 | continue 207 | 208 | } 209 | 210 | err = os.Chmod(dbf.Path, filemode) 211 | if err != nil { 212 | appLogger.Errorf("Can`t chmod db error | DB [%s] | %v", dbf.Path, err) 213 | 214 | err = NDBDelete(cdb, cmpbucket, dbf.Key) 215 | if err != nil { 216 | appLogger.Errorf("| Delete compaction task error | DB Key [%s] | %v", sdbf, err) 217 | } 218 | 219 | db.Close() 220 | keymutex.UnLock(dbf.Path) 221 | continue 222 | 223 | } 224 | 225 | err = NDBDelete(cdb, cmpbucket, dbf.Key) 226 | if err != nil { 227 | appLogger.Errorf("| Delete compaction task error | DB Key [%s] | %v", sdbf, err) 228 | db.Close() 229 | keymutex.UnLock(dbf.Path) 230 | continue 231 | } 232 | 233 | db.Close() 234 | keymutex.UnLock(dbf.Path) 235 | 236 | } else { 237 | 238 | appLogger.Errorf("| Timeout mmutex lock error | DB [%s]", dbf.Path) 239 | continue 240 | } 241 | 242 | } 243 | 244 | } 245 | -------------------------------------------------------------------------------- /cnt.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright © 2020 Andrey Kuvshinov. Contacts: 4 | Copyright © 2020 Eltaline OU. Contacts: 5 | All rights reserved. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | The wZD project contains unmodified/modified libraries imports too with 20 | separate copyright notices and license terms. Your use of the source code 21 | this libraries is subject to the terms and conditions of licenses these libraries. 22 | 23 | */ 24 | 25 | package main 26 | 27 | import ( 28 | "mime" 29 | "net/http" 30 | "path/filepath" 31 | ) 32 | 33 | // Working helpers 34 | 35 | // ContentType : get a content type of requested file/value 36 | func ContentType(filename string, filesize int64, contbuffer []byte, csizebuffer int) (conttype string, err error) { 37 | 38 | conttype = mime.TypeByExtension(filepath.Ext(filename)) 39 | 40 | if conttype == "" && filesize >= 512 { 41 | 42 | conttype = http.DetectContentType(contbuffer[:csizebuffer]) 43 | return conttype, err 44 | 45 | } 46 | 47 | return conttype, err 48 | 49 | } 50 | -------------------------------------------------------------------------------- /conf/init.d/bsd/wzd: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # PROVIDE: wzd 3 | # REQUIRE: networking syslog 4 | 5 | . /etc/rc.subr 6 | 7 | name="wzd" 8 | rcvar="wzd_enable" 9 | 10 | command="/usr/local/wzd/sbin/wzd --config=/usr/local/wzd/etc/wzd.conf" 11 | 12 | wzd_user="wzd" 13 | 14 | wzd_cnfdir="/usr/local/wzd/etc" 15 | wzd_libdir="/usr/local/wzd/lib" 16 | wzd_logdir="/usr/local/wzd/log" 17 | 18 | pidfile="${wzd_libdir}/${name}.pid" 19 | 20 | start_precmd="wzd_precmd" 21 | start_cmd="/usr/sbin/daemon -f -u $wzd_user $command" 22 | gracefulstop_cmd="wzd_gracefulstop" 23 | 24 | load_rc_config $name 25 | : ${wzd_enable:=no} 26 | 27 | wzd_check() 28 | { 29 | 30 | if [ -z `getent group wzd` ]; then 31 | pw groupadd wzd 32 | fi 33 | 34 | if [ -z `getent passwd wzd` ]; then 35 | pw useradd wzd -g wzd 36 | fi 37 | 38 | if [ ! -d ${wzd_cnfdir} ] ; then 39 | install -d -o wzd -g wzd -m 755 ${wzd_cnfdir} 40 | fi 41 | 42 | if [ ! -d ${wzd_libdir} ] ; then 43 | install -d -o wzd -g wzd -m 755 ${wzd_libdir} 44 | fi 45 | 46 | if [ ! -d ${wzd_logdir} ] ; then 47 | install -d -o wzd -g wzd -m 755 ${wzd_logdir} 48 | fi 49 | 50 | } 51 | 52 | wzd_precmd() 53 | { 54 | 55 | wzd_check 56 | 57 | } 58 | 59 | wzd_gracefulstop() 60 | { 61 | echo "Performing a graceful stop:" 62 | sig_stop="TERM" 63 | run_rc_command ${rc_prefix}stop || return 1 64 | } 65 | 66 | run_rc_command "$1" 67 | -------------------------------------------------------------------------------- /conf/init.d/linux/wzd: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ### BEGIN INIT INFO 3 | # Provides: wzd 4 | # Required-Start: $syslog $local_fs $remote_fs $network 5 | # Required-Stop: $syslog $local_fs $remote_fs 6 | # Default-Start: 2 3 4 5 7 | # Default-Stop: 0 1 6 8 | # Short-Description: wZD Service 9 | ### END INIT INFO 10 | 11 | . /lib/lsb/init-functions 12 | DAEMON=wzd 13 | USER="wzd" 14 | GROUP="wzd" 15 | DBDIR="/var/lib/wzd" 16 | LOGDIR="/var/log/wzd" 17 | RUNDIR="/run/wzd" 18 | PIDFILE=/run/wzd/wzd.pid 19 | DESC="wZD Service" 20 | start() { 21 | 22 | if [ -z `getent group wzd` ]; then 23 | groupadd wzd 24 | fi 25 | 26 | if [ -z `getent passwd wzd` ]; then 27 | useradd wzd -g wzd 28 | fi 29 | 30 | test -d $DBDIR || install --mode=755 --owner=wzd --group=wzd --directory $DBDIR 31 | test -d $LOGDIR || install --mode=755 --owner=wzd --group=wzd --directory $LOGDIR 32 | test -d $RUNDIR || install --mode=755 --owner=wzd --group=wzd --directory $RUNDIR 33 | 34 | log_daemon_msg "Starting $DESC\n" 35 | start-stop-daemon -q -b -u $USER -g $GROUP -p $PIDFILE --start --exec /usr/sbin/wzd 36 | if [ $? -ne 0 ]; then 37 | log_failure_msg "Failed" 38 | exit 1 39 | fi 40 | if [ $? -eq 0 ]; then 41 | log_success_msg "wZD Started" 42 | fi 43 | exit 0 44 | 45 | } 46 | 47 | stop() { 48 | 49 | log_daemon_msg "Stopping $DESC\n" 50 | start-stop-daemon -q -p $PIDFILE --stop 51 | if [ $? -ne 0 ]; then 52 | log_failure_msg "Failed" 53 | exit 1 54 | fi 55 | if [ $? -eq 0 ]; then 56 | log_success_msg "wZD Stopped" 57 | fi 58 | 59 | } 60 | 61 | force_reload() { 62 | 63 | stop 64 | start 65 | 66 | } 67 | 68 | case "$1" in 69 | 70 | start) 71 | start 72 | ;; 73 | stop) 74 | stop 75 | ;; 76 | force-reload) 77 | force_reload 78 | ;; 79 | restart) 80 | stop 81 | start 82 | ;; 83 | 84 | *) 85 | echo "$Usage: $DAEMON {start|stop|force-reload|restart}" 86 | exit 2 87 | 88 | esac 89 | -------------------------------------------------------------------------------- /conf/logrotate.d/wzd: -------------------------------------------------------------------------------- 1 | /var/log/wzd/*.log { 2 | rotate 7 3 | daily 4 | compress 5 | missingok 6 | delaycompress 7 | copytruncate 8 | } 9 | -------------------------------------------------------------------------------- /conf/nginx/localhost-ssl.conf: -------------------------------------------------------------------------------- 1 | upstream wzd { 2 | server 127.0.0.1:9699; 3 | } 4 | 5 | #upstream wzd-ssl { 6 | # server 127.0.0.1:9799; 7 | #} 8 | 9 | server { 10 | 11 | listen 443 ssl http2; 12 | 13 | ssl_certificate /etc/ssl/example.com/fullchain.pem; 14 | ssl_trusted_certificate /etc/ssl/example.com/chain.pem; 15 | ssl_certificate_key /etc/ssl/example.com/privkey.pem; 16 | 17 | ssl_session_timeout 60m; 18 | ssl_session_cache shared:SSL:64m; 19 | 20 | ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3; 21 | 22 | server_name example.com; 23 | 24 | resolver 127.0.0.1 8.8.8.8 8.8.4.4; 25 | 26 | error_log /var/log/nginx/example.com.error.wzd.log; 27 | 28 | proxy_connect_timeout 5; 29 | proxy_send_timeout 60; 30 | proxy_read_timeout 60; 31 | lingering_time 30; 32 | lingering_timeout 5; 33 | client_header_timeout 5; 34 | client_body_timeout 60; 35 | send_timeout 60; 36 | keepalive_timeout 60; 37 | reset_timedout_connection on; 38 | 39 | # add_header Access-Control-Allow-Origin '*' always; 40 | # add_header Access-Control-Allow-Methods 'GET, HEAD, OPTIONS, PUT, DELETE' always; 41 | 42 | rewrite ^/(.*)/$ /$1 permanent; 43 | 44 | # if ($request_method = 'OPTIONS') { 45 | # return 200; 46 | # } 47 | 48 | # if ($request_method = 'PUT') { 49 | # return 403; 50 | # } 51 | 52 | # if ($request_method = 'POST') { 53 | # return 403; 54 | # } 55 | 56 | # if ($request_method = 'PATCH') { 57 | # return 403; 58 | # } 59 | 60 | # if ($request_method = 'DELETE') { 61 | # return 403; 62 | # } 63 | 64 | client_max_body_size 512m; 65 | 66 | location / { 67 | 68 | access_log /var/log/nginx/example.com.access.wzd.log; 69 | 70 | proxy_set_header Host localhost; 71 | proxy_pass http://wzd; 72 | # proxy_pass https://wzd; 73 | proxy_set_header X-Real-IP $remote_addr; 74 | proxy_set_header X-Forwarded-Proto http; 75 | # proxy_set_header X-Forwarded-Proto https; 76 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 77 | 78 | } 79 | 80 | } -------------------------------------------------------------------------------- /conf/nginx/localhost.conf: -------------------------------------------------------------------------------- 1 | upstream wzd { 2 | server 127.0.0.1:9699; 3 | } 4 | 5 | server { 6 | 7 | listen 80; 8 | 9 | server_name _; 10 | 11 | error_log /var/log/nginx/localhost.error.wzd.log; 12 | 13 | proxy_connect_timeout 5; 14 | proxy_send_timeout 60; 15 | proxy_read_timeout 60; 16 | lingering_time 30; 17 | lingering_timeout 5; 18 | client_header_timeout 5; 19 | client_body_timeout 60; 20 | send_timeout 60; 21 | keepalive_timeout 60; 22 | reset_timedout_connection on; 23 | 24 | # add_header Access-Control-Allow-Origin '*' always; 25 | add_header Access-Control-Allow-Methods 'GET, HEAD, OPTIONS, PUT, DELETE' always; 26 | 27 | rewrite ^/(.*)/$ /$1 permanent; 28 | 29 | if ($request_method = 'OPTIONS') { 30 | return 200; 31 | } 32 | 33 | # if ($request_method = 'PUT') { 34 | # return 403; 35 | # } 36 | 37 | # if ($request_method = 'POST') { 38 | # return 403; 39 | # } 40 | 41 | # if ($request_method = 'PATCH') { 42 | # return 403; 43 | # } 44 | 45 | # if ($request_method = 'DELETE') { 46 | # return 403; 47 | # } 48 | 49 | client_max_body_size 512m; 50 | 51 | location / { 52 | 53 | access_log /var/log/nginx/localhost.access.wzd.log; 54 | 55 | proxy_set_header Host localhost; 56 | proxy_pass http://wzd; 57 | proxy_set_header X-Real-IP $remote_addr; 58 | proxy_set_header X-Forwarded-Proto http; 59 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 60 | 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /conf/systemd/wzd.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=wZD Service 3 | 4 | [Service] 5 | Type=simple 6 | User=wzd 7 | Group=wzd 8 | PIDFile=/run/wzd/wzd.pid 9 | RuntimeDirectory=wzd 10 | TimeoutStopSec=300 11 | 12 | ExecStart=/usr/sbin/wzd 13 | ExecStop=/bin/kill -s TERM $MAINPID 14 | 15 | Restart=always 16 | RestartSec=60s 17 | 18 | StandardOutput=syslog 19 | StandardError=syslog 20 | 21 | LimitNOFILE=131072 22 | 23 | [Install] 24 | WantedBy=multi-user.target 25 | Alias=wzd.service 26 | -------------------------------------------------------------------------------- /conf/wzd/bsd/del-localhost.conf: -------------------------------------------------------------------------------- 1 | 127.0.0.0/8 2 | 192.168.0.0/16 3 | 172.16.0.0/12 4 | 10.0.0.0/8 5 | -------------------------------------------------------------------------------- /conf/wzd/bsd/get-localhost.conf: -------------------------------------------------------------------------------- 1 | 0.0.0.0/0 2 | -------------------------------------------------------------------------------- /conf/wzd/bsd/put-localhost.conf: -------------------------------------------------------------------------------- 1 | 127.0.0.0/8 2 | 192.168.0.0/16 3 | 172.16.0.0/12 4 | 10.0.0.0/8 5 | -------------------------------------------------------------------------------- /conf/wzd/bsd/wzd.conf: -------------------------------------------------------------------------------- 1 | [global] 2 | 3 | bindaddr = "127.0.0.1:9699" 4 | bindaddrssl = "127.0.0.1:9799" 5 | onlyssl = false 6 | readtimeout = 60 7 | readheadertimeout = 5 8 | writetimeout = 60 9 | idletimeout = 60 10 | keepalive = false 11 | 12 | realheader = "X-Real-IP" 13 | charset = "UTF-8" 14 | 15 | debugmode = false 16 | 17 | gcpercent = 25 18 | 19 | freelist = "hashmap" 20 | 21 | search = true 22 | searchcache = 134217728 23 | searchdir = "/usr/local/wzd/lib/search" 24 | searchinit = 4 25 | searchindex = "ram" 26 | 27 | pidfile = "/usr/local/wzd/lib/wzd.pid" 28 | 29 | logdir = "/usr/local/wzd/log" 30 | logmode = 0640 31 | 32 | defsleep = 1 33 | 34 | cmpsched = true 35 | cmpdir = "/usr/local/wzd/lib/compact" 36 | 37 | cmptime = 7 38 | cmpcheck = 1 39 | 40 | [server] 41 | 42 | [server.hub] 43 | host = "localhost" 44 | root = "/path/to/root" 45 | sslcrt = "" 46 | sslkey = "" 47 | getallow = "/usr/local/wzd/etc/get-localhost.conf" 48 | putallow = "/usr/local/wzd/etc/put-localhost.conf" 49 | delallow = "/usr/local/wzd/etc/del-localhost.conf" 50 | options = "GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE" 51 | headorigin = "*" 52 | xframe = "sameorigin" 53 | upload = false 54 | delete = false 55 | compaction = true 56 | writeintegrity = true 57 | readintegrity = true 58 | trytimes = 5 59 | opentries = 5 60 | locktimeout = 5 61 | skeyscnt = 16384 62 | smaxsize = 536870912 63 | fmaxsize = 1048576 64 | vmaxsize = 4096 65 | args = false 66 | getbolt = false 67 | getkeys = false 68 | getinfo = false 69 | getsearch = false 70 | getrecursive = false 71 | getjoin = false 72 | getvalue = false 73 | getcount = false 74 | getcache = true 75 | searchthreads = 4 76 | searchtimeout = 10 77 | nonunique = false 78 | cctrl = 0 79 | minbuffer = 262144 80 | lowbuffer = 1048576 81 | medbuffer = 67108864 82 | bigbuffer = 536870912 83 | filemode = 0640 84 | dirmode = 0750 85 | delbolt = false 86 | deldir = false 87 | gzstatic = false 88 | log4xx = true 89 | 90 | [end] -------------------------------------------------------------------------------- /conf/wzd/del-localhost.conf: -------------------------------------------------------------------------------- 1 | 127.0.0.0/8 2 | 192.168.0.0/16 3 | 172.16.0.0/12 4 | 10.0.0.0/8 5 | -------------------------------------------------------------------------------- /conf/wzd/docker/del-localhost.conf: -------------------------------------------------------------------------------- 1 | 0.0.0.0/0 2 | -------------------------------------------------------------------------------- /conf/wzd/docker/get-localhost.conf: -------------------------------------------------------------------------------- 1 | 0.0.0.0/0 2 | -------------------------------------------------------------------------------- /conf/wzd/docker/put-localhost.conf: -------------------------------------------------------------------------------- 1 | 0.0.0.0/0 2 | -------------------------------------------------------------------------------- /conf/wzd/docker/wzd-docker.conf: -------------------------------------------------------------------------------- 1 | [global] 2 | 3 | bindaddr = "var_bindaddr" 4 | bindaddrssl = "var_bindaddrssl" 5 | onlyssl = var_onlyssl 6 | readtimeout = var_readtimeout 7 | readheadertimeout = var_readheadertimeout 8 | writetimeout = var_writetimeout 9 | idletimeout = var_idletimeout 10 | keepalive = var_keepalive 11 | 12 | realheader = "var_realheader" 13 | charset = "var_charset" 14 | 15 | debugmode = var_debugmode 16 | 17 | gcpercent = var_gcpercent 18 | 19 | freelist = "var_freelist" 20 | 21 | search = var_search 22 | searchcache = var_searchcache 23 | searchdir = "var_searchdir" 24 | searchinit = var_searchinit 25 | searchindex = "var_searchindex" 26 | 27 | pidfile = "var_pidfile" 28 | 29 | logdir = "var_logdir" 30 | logmode = var_logmode 31 | 32 | defsleep = var_defsleep 33 | 34 | cmpsched = var_cmpsched 35 | cmpdir = "var_cmpdir" 36 | 37 | cmptime = var_cmptime 38 | cmpcheck = var_cmpcheck 39 | 40 | [server] 41 | 42 | [server.hub] 43 | host = "var_host" 44 | root = "var_root" 45 | sslcrt = "var_sslcrt" 46 | sslkey = "var_sslkey" 47 | getallow = "var_getallow" 48 | putallow = "var_putallow" 49 | delallow = "var_delallow" 50 | options = "var_options" 51 | headorigin = "var_headorigin" 52 | xframe = "var_xframe" 53 | upload = var_upload 54 | delete = var_delete 55 | compaction = var_compaction 56 | writeintegrity = var_writeintegrity 57 | readintegrity = var_readintegrity 58 | trytimes = var_trytimes 59 | opentries = var_opentries 60 | locktimeout = var_locktimeout 61 | skeyscnt = var_skeyscnt 62 | smaxsize = var_smaxsize 63 | fmaxsize = var_fmaxsize 64 | vmaxsize = var_vmaxsize 65 | args = var_args 66 | getbolt = var_getbolt 67 | getkeys = var_getkeys 68 | getinfo = var_getinfo 69 | getsearch = var_getsearch 70 | getrecursive = var_getrecursive 71 | getjoin = var_getjoin 72 | getvalue = var_getvalue 73 | getcount = var_getcount 74 | getcache = var_getcache 75 | searchthreads = var_searchthreads 76 | searchtimeout = var_searchtimeout 77 | nonunique = var_nonunique 78 | cctrl = var_cctrl 79 | minbuffer = var_minbuffer 80 | lowbuffer = var_lowbuffer 81 | medbuffer = var_medbuffer 82 | bigbuffer = var_bigbuffer 83 | filemode = var_filemode 84 | dirmode = var_dirmode 85 | delbolt = var_delbolt 86 | deldir = var_deldir 87 | gzstatic = var_gzstatic 88 | log4xx = var_log4xx 89 | 90 | [end] -------------------------------------------------------------------------------- /conf/wzd/get-localhost.conf: -------------------------------------------------------------------------------- 1 | 0.0.0.0/0 2 | -------------------------------------------------------------------------------- /conf/wzd/put-localhost.conf: -------------------------------------------------------------------------------- 1 | 127.0.0.0/8 2 | 192.168.0.0/16 3 | 172.16.0.0/12 4 | 10.0.0.0/8 5 | -------------------------------------------------------------------------------- /conf/wzd/wzd.conf: -------------------------------------------------------------------------------- 1 | [global] 2 | 3 | bindaddr = "127.0.0.1:9699" 4 | bindaddrssl = "127.0.0.1:9799" 5 | onlyssl = false 6 | readtimeout = 60 7 | readheadertimeout = 5 8 | writetimeout = 60 9 | idletimeout = 60 10 | keepalive = false 11 | 12 | realheader = "X-Real-IP" 13 | charset = "UTF-8" 14 | 15 | debugmode = false 16 | 17 | gcpercent = 25 18 | 19 | freelist = "hashmap" 20 | 21 | search = true 22 | searchcache = 134217728 23 | searchdir = "/var/lib/wzd/search" 24 | searchinit = 4 25 | searchindex = "ram" 26 | 27 | pidfile = "/run/wzd/wzd.pid" 28 | 29 | logdir = "/var/log/wzd" 30 | logmode = 0640 31 | 32 | defsleep = 1 33 | 34 | cmpsched = true 35 | cmpdir = "/var/lib/wzd/compact" 36 | cmptime = 7 37 | cmpcheck = 1 38 | 39 | [server] 40 | 41 | [server.hub] 42 | host = "localhost" 43 | root = "/path/to/root" 44 | sslcrt = "" 45 | sslkey = "" 46 | getallow = "/etc/wzd/get-localhost.conf" 47 | putallow = "/etc/wzd/put-localhost.conf" 48 | delallow = "/etc/wzd/del-localhost.conf" 49 | options = "GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE" 50 | headorigin = "*" 51 | xframe = "sameorigin" 52 | upload = false 53 | delete = false 54 | compaction = true 55 | writeintegrity = true 56 | readintegrity = true 57 | trytimes = 5 58 | opentries = 5 59 | locktimeout = 5 60 | skeyscnt = 16384 61 | smaxsize = 536870912 62 | fmaxsize = 1048576 63 | vmaxsize = 4096 64 | args = false 65 | getbolt = false 66 | getkeys = false 67 | getinfo = false 68 | getsearch = false 69 | getrecursive = false 70 | getjoin = false 71 | getvalue = false 72 | getcount = false 73 | getcache = true 74 | searchthreads = 4 75 | searchtimeout = 10 76 | nonunique = false 77 | cctrl = 0 78 | minbuffer = 262144 79 | lowbuffer = 1048576 80 | medbuffer = 67108864 81 | bigbuffer = 536870912 82 | filemode = 0640 83 | dirmode = 0750 84 | delbolt = false 85 | deldir = false 86 | gzstatic = false 87 | log4xx = true 88 | 89 | [end] -------------------------------------------------------------------------------- /db.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright © 2020 Andrey Kuvshinov. Contacts: 4 | Copyright © 2020 Eltaline OU. Contacts: 5 | All rights reserved. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | The wZD project contains unmodified/modified libraries imports too with 20 | separate copyright notices and license terms. Your use of the source code 21 | this libraries is subject to the terms and conditions of licenses these libraries. 22 | 23 | */ 24 | 25 | package main 26 | 27 | import ( 28 | "bytes" 29 | "errors" 30 | "github.com/eltaline/bolt" 31 | "github.com/eltaline/nutsdb" 32 | "io/ioutil" 33 | "os" 34 | "time" 35 | ) 36 | 37 | // DB Helpers 38 | 39 | // BoltDB Handlers 40 | 41 | // BoltOpenWrite : open BoltDB for write operations 42 | func BoltOpenWrite(dbpath string, fmode os.FileMode, timeout time.Duration, opentries int, freelist string) (*bolt.DB, error) { 43 | 44 | i := 0 45 | 46 | flist := bolt.FreelistMapType 47 | 48 | switch { 49 | case freelist == "hashmap": 50 | flist = bolt.FreelistMapType 51 | case freelist == "array": 52 | flist = bolt.FreelistArrayType 53 | } 54 | 55 | for { 56 | 57 | i++ 58 | 59 | db, err := bolt.Open(dbpath, fmode, &bolt.Options{Timeout: timeout, FreelistType: flist}) 60 | if err == nil { 61 | return db, err 62 | } 63 | 64 | if i >= opentries { 65 | return db, err 66 | } 67 | 68 | time.Sleep(defsleep) 69 | 70 | } 71 | 72 | } 73 | 74 | // BoltOpenRead : open BoltDB for readonly operations 75 | func BoltOpenRead(dbpath string, fmode os.FileMode, timeout time.Duration, opentries int, freelist string) (*bolt.DB, error) { 76 | 77 | i := 0 78 | 79 | flist := bolt.FreelistMapType 80 | 81 | switch { 82 | case freelist == "hashmap": 83 | flist = bolt.FreelistMapType 84 | case freelist == "array": 85 | flist = bolt.FreelistArrayType 86 | } 87 | 88 | for { 89 | 90 | i++ 91 | 92 | db, err := bolt.Open(dbpath, fmode, &bolt.Options{Timeout: timeout, ReadOnly: true, FreelistType: flist}) 93 | if err == nil { 94 | return db, err 95 | } 96 | 97 | if i >= opentries { 98 | return db, err 99 | } 100 | 101 | time.Sleep(defsleep) 102 | 103 | } 104 | 105 | } 106 | 107 | // NDBInsert : NutsDB insert key function 108 | func NDBInsert(db *nutsdb.DB, bucket string, key []byte, value []byte, ttl uint32) error { 109 | 110 | nerr := db.Update(func(tx *nutsdb.Tx) error { 111 | 112 | err := tx.Put(bucket, key, value, ttl) 113 | if err != nil { 114 | return err 115 | } 116 | 117 | return nil 118 | 119 | }) 120 | 121 | if nerr != nil { 122 | return nerr 123 | } 124 | 125 | return nil 126 | 127 | } 128 | 129 | // NDBDelete : NutsDB delete key function 130 | func NDBDelete(db *nutsdb.DB, bucket string, key []byte) error { 131 | 132 | nerr := db.Update(func(tx *nutsdb.Tx) error { 133 | 134 | err := tx.Delete(bucket, key) 135 | if err != nil { 136 | return err 137 | } 138 | 139 | return nil 140 | 141 | }) 142 | 143 | if nerr != nil { 144 | return nerr 145 | } 146 | 147 | return nil 148 | 149 | } 150 | 151 | // NDBMerge : NutsDB merge compaction function 152 | func NDBMerge(db *nutsdb.DB, dir string) error { 153 | 154 | var err error 155 | 156 | err = db.Merge() 157 | if err != nil { 158 | return err 159 | } 160 | 161 | segs, err := ioutil.ReadDir(dir) 162 | if err != nil { 163 | return err 164 | } 165 | 166 | for _, seg := range segs { 167 | 168 | sn := seg.Name() 169 | ss := seg.Size() 170 | 171 | fn := dir + "/" + sn 172 | 173 | emptyBuffer := make([]byte, ss) 174 | 175 | segmentBuffer, err := ioutil.ReadFile(fn) 176 | if err != nil { 177 | return err 178 | } 179 | 180 | if segmentBuffer != nil { 181 | 182 | if bytes.Equal(emptyBuffer, segmentBuffer) { 183 | err = RemoveSegment(fn) 184 | if err != nil { 185 | return err 186 | } 187 | } 188 | 189 | } 190 | 191 | } 192 | 193 | return nil 194 | 195 | } 196 | 197 | // DBGetVal : get value of requested key from bucket 198 | func DBGetVal(db *bolt.DB, bucket string, key []byte) (data []byte, err error) { 199 | 200 | err = db.View(func(tx *bolt.Tx) error { 201 | 202 | verr := errors.New("bucket not exists") 203 | 204 | b := tx.Bucket([]byte(bucket)) 205 | if b != nil { 206 | 207 | val := b.GetOffset(key, 36) 208 | if val != nil { 209 | data = val 210 | } 211 | 212 | } else { 213 | return verr 214 | } 215 | 216 | return nil 217 | 218 | }) 219 | 220 | return data, err 221 | 222 | } 223 | 224 | // KeyExists : check existence of requested key in index/size/time bucket 225 | func KeyExists(db *bolt.DB, xbucket string, file string) (data string, err error) { 226 | 227 | err = db.View(func(tx *bolt.Tx) error { 228 | 229 | verr := errors.New("index/size/time bucket not exists") 230 | 231 | b := tx.Bucket([]byte(xbucket)) 232 | if b != nil { 233 | 234 | val := b.Get([]byte(file)) 235 | if val != nil { 236 | data = string(val) 237 | } 238 | 239 | } else { 240 | return verr 241 | } 242 | 243 | return nil 244 | 245 | }) 246 | 247 | return data, err 248 | 249 | } 250 | 251 | // KeyGetVal : get value of requested key in index/size/time bucket 252 | func KeyGetVal(db *bolt.DB, xbucket string, key []byte) (data []byte, err error) { 253 | 254 | err = db.View(func(tx *bolt.Tx) error { 255 | 256 | verr := errors.New("index/size/time bucket not exists") 257 | 258 | b := tx.Bucket([]byte(xbucket)) 259 | if b != nil { 260 | 261 | val := b.Get(key) 262 | if val != nil { 263 | data = val 264 | } 265 | 266 | } else { 267 | return verr 268 | } 269 | 270 | return nil 271 | 272 | }) 273 | 274 | return data, err 275 | 276 | } 277 | 278 | // KeysCount : count keys in an index bucket for requested directory 279 | func KeysCount(db *bolt.DB, ibucket string) (cnt int, err error) { 280 | 281 | cnt = 1 282 | 283 | var sts bolt.BucketStats 284 | 285 | err = db.View(func(tx *bolt.Tx) error { 286 | 287 | verr := errors.New("index bucket not exists") 288 | 289 | b := tx.Bucket([]byte(ibucket)) 290 | if b != nil { 291 | sts = b.Stats() 292 | cnt = sts.KeyN 293 | } else { 294 | return verr 295 | } 296 | 297 | return nil 298 | 299 | }) 300 | 301 | return cnt, err 302 | 303 | } 304 | 305 | // KeysCountBucket : count keys in a requested bucket 306 | func KeysCountBucket(db *bolt.DB, bucket string) (cnt int, err error) { 307 | 308 | cnt = 1 309 | 310 | var sts bolt.BucketStats 311 | 312 | err = db.View(func(tx *bolt.Tx) error { 313 | 314 | verr := errors.New("bucket not exists") 315 | 316 | b := tx.Bucket([]byte(bucket)) 317 | if b != nil { 318 | sts = b.Stats() 319 | cnt = sts.KeyN 320 | } else { 321 | return verr 322 | } 323 | 324 | return nil 325 | 326 | }) 327 | 328 | return cnt, err 329 | 330 | } 331 | 332 | // BucketCount : get count of buckets from count bucket in requested directory 333 | func BucketCount(db *bolt.DB, cbucket string) (cnt uint64, err error) { 334 | 335 | cnt = uint64(0) 336 | 337 | err = db.View(func(tx *bolt.Tx) error { 338 | 339 | verr := errors.New("count bucket not exists") 340 | 341 | b := tx.Bucket([]byte(cbucket)) 342 | if b != nil { 343 | 344 | val := b.Get([]byte("counter")) 345 | if val != nil { 346 | cnt = Endian.Uint64(val) 347 | } 348 | 349 | } else { 350 | return verr 351 | } 352 | 353 | return nil 354 | 355 | }) 356 | 357 | return cnt, err 358 | 359 | } 360 | 361 | // BucketStats : get current size of requested bucket 362 | func BucketStats(db *bolt.DB, bucket string) (cnt int, err error) { 363 | 364 | cnt = 0 365 | 366 | var sts bolt.BucketStats 367 | 368 | err = db.View(func(tx *bolt.Tx) error { 369 | 370 | verr := errors.New("bucket not exists") 371 | 372 | b := tx.Bucket([]byte(bucket)) 373 | if b != nil { 374 | sts = b.Stats() 375 | cnt = sts.LeafInuse 376 | } else { 377 | return verr 378 | } 379 | 380 | return nil 381 | 382 | }) 383 | 384 | return cnt, err 385 | 386 | } 387 | -------------------------------------------------------------------------------- /file.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright © 2020 Andrey Kuvshinov. Contacts: 4 | Copyright © 2020 Eltaline OU. Contacts: 5 | All rights reserved. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | The wZD project contains unmodified/modified libraries imports too with 20 | separate copyright notices and license terms. Your use of the source code 21 | this libraries is subject to the terms and conditions of licenses these libraries. 22 | 23 | */ 24 | 25 | package main 26 | 27 | import ( 28 | "io" 29 | "os" 30 | "path/filepath" 31 | ) 32 | 33 | // File Helpers 34 | 35 | // FileExists : check existence of requested file 36 | func FileExists(filename string) bool { 37 | 38 | if fi, err := os.Stat(filename); err == nil { 39 | 40 | if fi.Mode().IsRegular() { 41 | return true 42 | } 43 | 44 | } 45 | 46 | return false 47 | 48 | } 49 | 50 | // FileOrLinkExists : check existence of requested file or symlink 51 | func FileOrLinkExists(filename string) bool { 52 | 53 | if fi, err := os.Stat(filename); err == nil { 54 | 55 | if fi.Mode().IsRegular() { 56 | return true 57 | } 58 | 59 | } 60 | 61 | if _, err := filepath.EvalSymlinks(filename); err == nil { 62 | return true 63 | } 64 | 65 | return false 66 | 67 | } 68 | 69 | // DirExists : check existence of requested directory 70 | func DirExists(filename string) bool { 71 | 72 | if fi, err := os.Stat(filename); err == nil { 73 | 74 | if fi.Mode().IsDir() { 75 | return true 76 | } 77 | 78 | } 79 | 80 | return false 81 | 82 | } 83 | 84 | // IsEmptyDir : check on empty directory 85 | func IsEmptyDir(directory string) (bool, error) { 86 | 87 | dir, err := os.Open(directory) 88 | if err != nil { 89 | return false, err 90 | } 91 | defer dir.Close() 92 | 93 | _, err = dir.Readdir(1) 94 | if err != nil { 95 | 96 | if err == io.EOF { 97 | return true, nil 98 | } 99 | 100 | return false, err 101 | 102 | } 103 | 104 | return false, nil 105 | 106 | } 107 | 108 | // RemoveFile : remove requested file and/or empty dir 109 | func RemoveFile(filename string, directory string, deldir bool) error { 110 | 111 | err := os.Remove(filename) 112 | if err != nil { 113 | return err 114 | } 115 | 116 | if deldir { 117 | 118 | dir, err := os.Open(directory) 119 | if err != nil { 120 | return err 121 | } 122 | defer dir.Close() 123 | 124 | _, err = dir.Readdir(1) 125 | if err != nil { 126 | if err == io.EOF { 127 | err = os.Remove(directory) 128 | if err != nil { 129 | return err 130 | } 131 | 132 | return err 133 | 134 | } 135 | 136 | return err 137 | 138 | } 139 | 140 | } 141 | 142 | return err 143 | 144 | } 145 | 146 | // RemoveFileDB : remove requested BoltDB file and/or empty dir 147 | func RemoveFileDB(filename string, directory string, deldir bool) error { 148 | 149 | err := os.Remove(filename) 150 | if err != nil { 151 | return err 152 | } 153 | 154 | if deldir { 155 | 156 | dir, err := os.Open(directory) 157 | if err != nil { 158 | return err 159 | } 160 | defer dir.Close() 161 | 162 | _, err = dir.Readdir(1) 163 | if err != nil { 164 | if err == io.EOF { 165 | err = os.Remove(directory) 166 | if err != nil { 167 | return err 168 | } 169 | 170 | return err 171 | 172 | } 173 | 174 | return err 175 | 176 | } 177 | 178 | } 179 | 180 | return err 181 | 182 | } 183 | 184 | // RemoveSegment : remove NutsDB segment file if empty 185 | func RemoveSegment(filename string) error { 186 | 187 | err := os.Remove(filename) 188 | if err != nil { 189 | return err 190 | } 191 | 192 | return nil 193 | 194 | } 195 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module wzd 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/ajg/form v1.5.1 // indirect 7 | github.com/cespare/xxhash v1.1.0 // indirect 8 | github.com/coocood/freecache v1.2.0 9 | github.com/eltaline/bolt v0.0.0-20200118182801-950b1520db1a 10 | github.com/eltaline/cwalk v0.0.0-20191125092548-dd7f505d2f66 11 | github.com/eltaline/gron v0.0.0-20160621042432-e78485adab46 12 | github.com/eltaline/mmutex v0.0.0-20210624131550-0cd205386e7d 13 | github.com/eltaline/nutsdb v0.5.1-0.20200518094744-ed31df68a8a9 14 | github.com/eltaline/toml v0.3.1 15 | github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 // indirect 16 | github.com/google/go-querystring v1.0.0 // indirect 17 | github.com/hashicorp/go-immutable-radix v1.3.1 18 | github.com/imkira/go-interpol v1.1.0 // indirect 19 | github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect 20 | github.com/kataras/golog v0.1.7 21 | github.com/kataras/iris/v12 v12.1.8 22 | github.com/klauspost/compress v1.10.5 // indirect 23 | github.com/mattn/go-colorable v0.1.6 // indirect 24 | github.com/moul/http2curl v1.0.0 // indirect 25 | github.com/onsi/ginkgo v1.12.1 // indirect 26 | github.com/onsi/gomega v1.10.0 // indirect 27 | github.com/pieterclaerhout/go-waitgroup v1.0.7 28 | github.com/roylee0704/gron v0.0.0-20160621042432-e78485adab46 // indirect 29 | github.com/sergi/go-diff v1.1.0 // indirect 30 | github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect 31 | github.com/smartystreets/goconvey v1.6.4 // indirect 32 | github.com/valyala/fasthttp v1.12.0 // indirect 33 | github.com/xeipuuv/gojsonschema v1.2.0 // indirect 34 | github.com/xujiajun/nutsdb v0.5.0 // indirect 35 | github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 // indirect 36 | github.com/yudai/gojsondiff v1.0.0 // indirect 37 | github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect 38 | github.com/yudai/pp v2.0.1+incompatible // indirect 39 | golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce 40 | ) 41 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= 2 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 3 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 4 | github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c= 5 | github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= 6 | github.com/CloudyKit/jet/v3 v3.0.0 h1:1PwO5w5VCtlUUl+KTOBsTGZlhjWkcybsGaAau52tOy8= 7 | github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= 8 | github.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc= 9 | github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= 10 | github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= 11 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 12 | github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398 h1:WDC6ySpJzbxGWFh4aMxFFC28wwGp5pEuoTtvA4q/qQ4= 13 | github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= 14 | github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= 15 | github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= 16 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 17 | github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible h1:Ppm0npCCsmuR9oQaBtRuZcmILVE74aXE+AmrJj8L2ns= 18 | github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= 19 | github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0= 20 | github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE= 21 | github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= 22 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 23 | github.com/coocood/freecache v1.1.0 h1:ENiHOsWdj1BrrlPwblhbn4GdAsMymK3pZORJ+bJGAjA= 24 | github.com/coocood/freecache v1.1.0/go.mod h1:ePwxCDzOYvARfHdr1pByNct1at3CoKnsipOHwKlNbzI= 25 | github.com/coocood/freecache v1.2.0 h1:p8RhjN6Y4DRBIMzdRlm1y+M7h7YJxye3lGW8/VvzCz0= 26 | github.com/coocood/freecache v1.2.0/go.mod h1:OKrEjkGVoxZhyWAJoeFi5BMLUJm2Tit0kpGkIr7NGYY= 27 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 28 | github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= 29 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 30 | github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= 31 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 32 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 33 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 34 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 35 | github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= 36 | github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= 37 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 38 | github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o= 39 | github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= 40 | github.com/eltaline/bolt v0.0.0-20200118182801-950b1520db1a h1:KrRnVWQo2T0DFDB51QMTgsUoIOgR29QiE2P8bfVdlFM= 41 | github.com/eltaline/bolt v0.0.0-20200118182801-950b1520db1a/go.mod h1:KV21k3rtQTbxW6yuFW+V42qzswPLh2dcXA8UbRQSAVE= 42 | github.com/eltaline/cwalk v0.0.0-20191125092548-dd7f505d2f66 h1:mNFe+DsLY7vNYpYV4cT9rYqiBmLzTO4b/3n7hXdnzPY= 43 | github.com/eltaline/cwalk v0.0.0-20191125092548-dd7f505d2f66/go.mod h1:25eeXNUN78N4EtRQJFTxaCsr+GOBctXcqLiNP2Pdmes= 44 | github.com/eltaline/gron v0.0.0-20160621042432-e78485adab46 h1:V2wnUaQJIuAI/rjnd8UShMKch5bolYleiInMppFxCf8= 45 | github.com/eltaline/gron v0.0.0-20160621042432-e78485adab46/go.mod h1:BZBh2a0bCl2Ds8OYwAD173GsZrr49w1YxELRN4Dw5QU= 46 | github.com/eltaline/mmutex v0.0.0-20200516113347-4a6ee28318a9 h1:iRZFBE3KjWheKoYETnNlA8hYRn9J0Aakr1ntSOKyGxw= 47 | github.com/eltaline/mmutex v0.0.0-20200516113347-4a6ee28318a9/go.mod h1:7LE3NVoiYw0Zby2UhitpfIfeF3MazquuzPjT7q4T9Dg= 48 | github.com/eltaline/mmutex v0.0.0-20210624131550-0cd205386e7d h1:OTukNqQO8sKPMVHOVtcuAJ5D0qudI46q9jAFl0Yw5jE= 49 | github.com/eltaline/mmutex v0.0.0-20210624131550-0cd205386e7d/go.mod h1:cS8orNzYn+vncz6iN5B94+ErBPqlrsHZ1uwCFRB7+ZQ= 50 | github.com/eltaline/nutsdb v0.5.1-0.20200518094744-ed31df68a8a9 h1:ufVCQDYBpT40wzuIUKnmz13RVXjqKRpGR22bhlTvi9A= 51 | github.com/eltaline/nutsdb v0.5.1-0.20200518094744-ed31df68a8a9/go.mod h1:rBImhhmcpf21PLNOdkdAMe0g3kyfXaaPuID9oxCR+6Y= 52 | github.com/eltaline/toml v0.3.1 h1:ww2IODjJahBmlJ2GEHpONlZQCgxXzgwVSg/MVDhA0/A= 53 | github.com/eltaline/toml v0.3.1/go.mod h1:ykmr1lXTBF+D107RP4qh9XTXVDe0c17PMWhPbNHU5RA= 54 | github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= 55 | github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 h1:DddqAaWDpywytcG8w/qoQ5sAN8X12d3Z3koB0C3Rxsc= 56 | github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= 57 | github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= 58 | github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= 59 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= 60 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 61 | github.com/gavv/httpexpect v2.0.0+incompatible h1:1X9kcRshkSKEjNJJxX9Y9mQ5BRfbxU5kORdjhlA1yX8= 62 | github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= 63 | github.com/go-check/check v0.0.0-20180628173108-788fd7840127 h1:0gkP6mzaMqkmpcJYCFOLkIBwI7xFExG03bbkOkCvUPI= 64 | github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= 65 | github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= 66 | github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= 67 | github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= 68 | github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= 69 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 70 | github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= 71 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 72 | github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= 73 | github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= 74 | github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= 75 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 76 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 77 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 78 | github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= 79 | github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 80 | github.com/hashicorp/go-immutable-radix v1.2.0 h1:l6UW37iCXwZkZoAbEYnptSHVE/cQ5bOTPYG5W3vf9+8= 81 | github.com/hashicorp/go-immutable-radix v1.2.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 82 | github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= 83 | github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 84 | github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM= 85 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 86 | github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 87 | github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= 88 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 89 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 90 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 91 | github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk= 92 | github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= 93 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 94 | github.com/iris-contrib/blackfriday v2.0.0+incompatible h1:o5sHQHHm0ToHUlAJSTjW9UWicjJSDDauOOQ2AHuIVp4= 95 | github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= 96 | github.com/iris-contrib/go.uuid v2.0.0+incompatible h1:XZubAYg61/JwnJNbZilGjf3b3pB80+OQg2qf6c8BfWE= 97 | github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= 98 | github.com/iris-contrib/jade v1.1.3 h1:p7J/50I0cjo0wq/VWVCDFd8taPJbuFC+bq23SniRFX0= 99 | github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk= 100 | github.com/iris-contrib/pongo2 v0.0.1 h1:zGP7pW51oi5eQZMIlGA3I+FHY9/HOQWDB+572yin0to= 101 | github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g= 102 | github.com/iris-contrib/schema v0.0.1 h1:10g/WnoRR+U+XXHWKBHeNy/+tZmM2kcAVGLOsz+yaDA= 103 | github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= 104 | github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= 105 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 106 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 107 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 108 | github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= 109 | github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= 110 | github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8= 111 | github.com/kataras/golog v0.0.15 h1:NPpVJt7Usc741IahumyieIj6j4q0WbwCq+YaLcBWTqc= 112 | github.com/kataras/golog v0.0.15/go.mod h1:lMz0gaBYitlMKvVn2Ykw0WpRoqHrq1kjWId/iLrH67I= 113 | github.com/kataras/golog v0.1.7 h1:0TY5tHn5L5DlRIikepcaRR/6oInIr9AiWsxzt0vvlBE= 114 | github.com/kataras/golog v0.1.7/go.mod h1:jOSQ+C5fUqsNSwurB/oAHq1IFSb0KI3l6GMa7xB6dZA= 115 | github.com/kataras/iris/v12 v12.1.8 h1:O3gJasjm7ZxpxwTH8tApZsvf274scSGQAUpNe47c37U= 116 | github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE= 117 | github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE= 118 | github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro= 119 | github.com/kataras/pio v0.0.6 h1:Fk21upgXuB6VKHuH/sY13g6fzA5Gn+rKjV0ReXST484= 120 | github.com/kataras/pio v0.0.6/go.mod h1:NFfMp2kVP1rmV4N6gH6qgWpuoDKlrOeYi3VrAIWCGsE= 121 | github.com/kataras/pio v0.0.10 h1:b0qtPUqOpM2O+bqa5wr2O6dN4cQNwSmFd6HQqgVae0g= 122 | github.com/kataras/pio v0.0.10/go.mod h1:gS3ui9xSD+lAUpbYnjOGiQyY7sUMJO+EHpiRzhtZ5no= 123 | github.com/kataras/sitemap v0.0.5 h1:4HCONX5RLgVy6G4RkYOV3vKNcma9p236LdGOipJsaFE= 124 | github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8= 125 | github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= 126 | github.com/klauspost/compress v1.10.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= 127 | github.com/klauspost/compress v1.10.5 h1:7q6vHIqubShURwQz8cQK6yIe/xC3IF0Vm7TGfqjewrc= 128 | github.com/klauspost/compress v1.10.5/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= 129 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 130 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 131 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 132 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 133 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 134 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 135 | github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= 136 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 137 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 138 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 139 | github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= 140 | github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= 141 | github.com/microcosm-cc/bluemonday v1.0.2 h1:5lPfLTTAvAbtS0VqT+94yOtFnGfUWYyx0+iToC3Os3s= 142 | github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= 143 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 144 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 145 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= 146 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 147 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= 148 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 149 | github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs= 150 | github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= 151 | github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= 152 | github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= 153 | github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= 154 | github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= 155 | github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= 156 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 157 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 158 | github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ= 159 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 160 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 161 | github.com/onsi/gomega v1.10.0 h1:Gwkk+PTu/nfOwNMtUB/mRUv0X7ewW5dO4AERT1ThVKo= 162 | github.com/onsi/gomega v1.10.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= 163 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 164 | github.com/pieterclaerhout/go-waitgroup v1.0.7 h1:W9zHJzXO3SPo7Rb3E8hkk8D0gJPdP6DWENt6M08IxWE= 165 | github.com/pieterclaerhout/go-waitgroup v1.0.7/go.mod h1:TJIzZKRP4sc5AT8M0RVUwXhw/OWMxg/lW8VsQz4SRBo= 166 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 167 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 168 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 169 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 170 | github.com/roylee0704/gron v0.0.0-20160621042432-e78485adab46 h1:dp1iW1JOTY63249ZTwxzwN0EKG6EvuPdfMohKo4EomY= 171 | github.com/roylee0704/gron v0.0.0-20160621042432-e78485adab46/go.mod h1:MDhl6ujYU3dbEiGclVk5uA4pHEjTS659POKAtiAWB94= 172 | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= 173 | github.com/ryanuber/columnize v2.1.0+incompatible h1:j1Wcmh8OrK4Q7GXY+V7SVSY8nUWQxHW5TkBe7YUl+2s= 174 | github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 175 | github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk= 176 | github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= 177 | github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= 178 | github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= 179 | github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= 180 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 181 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= 182 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 183 | github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= 184 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 185 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= 186 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 187 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 188 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 189 | github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= 190 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 191 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 192 | github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= 193 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 194 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 195 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 196 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= 197 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 198 | github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= 199 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 200 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 201 | github.com/valyala/fasthttp v1.12.0 h1:TsB9qkSeiMXB40ELWWSRMjlsE+8IkqXHcs01y2d9aw0= 202 | github.com/valyala/fasthttp v1.12.0/go.mod h1:229t1eWu9UXTPmoUkbpN/fctKPBY4IJoFXQnxHGXy6E= 203 | github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= 204 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= 205 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= 206 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= 207 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= 208 | github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= 209 | github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= 210 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 211 | github.com/xujiajun/gorouter v1.2.0/go.mod h1:yJrIta+bTNpBM/2UT8hLOaEAFckO+m/qmR3luMIQygM= 212 | github.com/xujiajun/mmap-go v1.0.1 h1:7Se7ss1fLPPRW+ePgqGpCkfGIZzJV6JPq9Wq9iv/WHc= 213 | github.com/xujiajun/mmap-go v1.0.1/go.mod h1:CNN6Sw4SL69Sui00p0zEzcZKbt+5HtEnYUsc6BKKRMg= 214 | github.com/xujiajun/nutsdb v0.5.0 h1:j/jM3Zw7Chg8WK7bAcKR0Xr7Mal47U1oJAMgySfDn9E= 215 | github.com/xujiajun/nutsdb v0.5.0/go.mod h1:owdwN0tW084RxEodABLbO7h4Z2s9WiAjZGZFhRh0/1Q= 216 | github.com/xujiajun/nutsdb v0.6.0 h1:voRbF4bQO6gF9xiFZ+5w/fPHgEfR7Jea1NOtU34I8yE= 217 | github.com/xujiajun/nutsdb v0.6.0/go.mod h1:Q8FXi2zeQRluPpUl/CKQ6J7u/9gcI02J6cZp3owFLyA= 218 | github.com/xujiajun/utils v0.0.0-20190123093513-8bf096c4f53b h1:jKG9OiL4T4xQN3IUrhUpc1tG+HfDXppkgVcrAiiaI/0= 219 | github.com/xujiajun/utils v0.0.0-20190123093513-8bf096c4f53b/go.mod h1:AZd87GYJlUzl82Yab2kTjx1EyXSQCAfZDhpTo1SQC4k= 220 | github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY= 221 | github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= 222 | github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA= 223 | github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= 224 | github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= 225 | github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= 226 | github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI= 227 | github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= 228 | golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 229 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 230 | golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 231 | golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 232 | golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw= 233 | golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 234 | golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce h1:Roh6XWxHFKrPgC/EQhVubSAGQ6Ozk6IdxHSzt1mR0EI= 235 | golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 236 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 237 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 238 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 239 | golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 240 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= 241 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 242 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 243 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 244 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k= 245 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 246 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 247 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= 248 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 249 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 250 | golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 251 | golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6 h1:IcgEB62HYgAhX0Nd/QrVgZlxlcyxbGQHElLUhW2X4Fo= 252 | golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 253 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 254 | golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= 255 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 256 | golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 257 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 258 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 259 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 260 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 261 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= 262 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 263 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 264 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 265 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 266 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 267 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 268 | golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 269 | golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 270 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 271 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= 272 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 273 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= 274 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 275 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 276 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 277 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 278 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 279 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 280 | gopkg.in/ini.v1 v1.51.1 h1:GyboHr4UqMiLUybYjd22ZjQIKEJEpgtLXtuGbR21Oho= 281 | gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 282 | gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY+ILlfndk4bRioVHaU= 283 | gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= 284 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 285 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 286 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 287 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 288 | gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= 289 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 290 | gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM= 291 | gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 292 | -------------------------------------------------------------------------------- /hlp.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright © 2020 Andrey Kuvshinov. Contacts: 4 | Copyright © 2020 Eltaline OU. Contacts: 5 | All rights reserved. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | The wZD project contains unmodified/modified libraries imports too with 20 | separate copyright notices and license terms. Your use of the source code 21 | this libraries is subject to the terms and conditions of licenses these libraries. 22 | 23 | */ 24 | 25 | package main 26 | 27 | import ( 28 | "fmt" 29 | "os" 30 | ) 31 | 32 | // Working helpers 33 | 34 | // StringOne : function returns true and sequence of received value if value == 1 35 | func StringOne(values ...interface{}) (bool, int) { 36 | 37 | c := 0 38 | 39 | for _, value := range values { 40 | c++ 41 | 42 | if value == "1" { 43 | return true, c 44 | } 45 | 46 | } 47 | 48 | return false, 0 49 | 50 | } 51 | 52 | // RBInt : check int32 acceptable range function and then return true or false 53 | func RBInt(i int, min int, max int) bool { 54 | 55 | switch { 56 | case i >= min && i <= max: 57 | return true 58 | default: 59 | return false 60 | } 61 | 62 | } 63 | 64 | // RBInt64 : check int64 acceptable range function and return true or false 65 | func RBInt64(i int64, min int64, max int64) bool { 66 | 67 | switch { 68 | case i >= min && i <= max: 69 | return true 70 | default: 71 | return false 72 | } 73 | 74 | } 75 | 76 | // Check : if received value is false, then run DoExit function 77 | func Check(bvar bool, sec string, name string, val string, perm string, ferr func(string, string, string, string)) { 78 | 79 | if !bvar { 80 | ferr(sec, name, val, perm) 81 | } 82 | 83 | } 84 | 85 | // DoExit : exit program function 86 | func DoExit(sec string, name string, val string, perm string) { 87 | fmt.Printf("Bad option value error | Section [%s] | Name [%s] | Value [%v] | Permissible Value [%s]\n", sec, name, val, perm) 88 | os.Exit(1) 89 | } 90 | -------------------------------------------------------------------------------- /images/get-put-32M.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eltaline/wzd/cd6cbc1dd517a779a2c66b27d68418cd846c276d/images/get-put-32M.png -------------------------------------------------------------------------------- /images/get.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eltaline/wzd/cd6cbc1dd517a779a2c66b27d68418cd846c276d/images/get.png -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eltaline/wzd/cd6cbc1dd517a779a2c66b27d68418cd846c276d/images/logo.png -------------------------------------------------------------------------------- /images/paypal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eltaline/wzd/cd6cbc1dd517a779a2c66b27d68418cd846c276d/images/paypal.png -------------------------------------------------------------------------------- /images/put.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eltaline/wzd/cd6cbc1dd517a779a2c66b27d68418cd846c276d/images/put.png -------------------------------------------------------------------------------- /images/reduction-full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eltaline/wzd/cd6cbc1dd517a779a2c66b27d68418cd846c276d/images/reduction-full.png -------------------------------------------------------------------------------- /images/wzd-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eltaline/wzd/cd6cbc1dd517a779a2c66b27d68418cd846c276d/images/wzd-arch.png -------------------------------------------------------------------------------- /images/wzd-scheme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eltaline/wzd/cd6cbc1dd517a779a2c66b27d68418cd846c276d/images/wzd-scheme.png -------------------------------------------------------------------------------- /init.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright © 2020 Andrey Kuvshinov. Contacts: 4 | Copyright © 2020 Eltaline OU. Contacts: 5 | All rights reserved. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | The wZD project contains unmodified/modified libraries imports too with 20 | separate copyright notices and license terms. Your use of the source code 21 | this libraries is subject to the terms and conditions of licenses these libraries. 22 | 23 | */ 24 | 25 | package main 26 | 27 | import ( 28 | "bytes" 29 | "encoding/binary" 30 | // "encoding/json" 31 | "errors" 32 | "fmt" 33 | "github.com/eltaline/bolt" 34 | "github.com/eltaline/nutsdb" 35 | "io/ioutil" 36 | "math/rand" 37 | "os" 38 | "path/filepath" 39 | "strconv" 40 | "strings" 41 | "sync" 42 | "time" 43 | ) 44 | 45 | // SearchInit : Search Metadata Database Initialization 46 | func SearchInit(nopt nutsdb.Options) { 47 | 48 | // Wait Group 49 | 50 | var wgs sync.WaitGroup 51 | 52 | // Variables 53 | 54 | var err error 55 | 56 | type mfiles struct { 57 | dir string 58 | crc uint64 59 | thr int 60 | } 61 | 62 | var mslice []mfiles 63 | var m mfiles 64 | 65 | ibucket := "index" 66 | sbucket := "size" 67 | tbucket := "time" 68 | 69 | opentries := 30 70 | timeout := time.Duration(60) * time.Second 71 | 72 | verr := errors.New("index/size/time bucket not exists") 73 | serr := errors.New("key size/date empty") 74 | 75 | // Loggers 76 | 77 | appLogger, applogfile := AppLogger() 78 | defer applogfile.Close() 79 | 80 | ndb, err := nutsdb.Open(nopt) 81 | if err != nil { 82 | appLogger.Errorf("| Can`t open search db error | DB Directory [%s] | %v", nopt.Dir, err) 83 | fmt.Printf("Can`t open search db error | DB Directory [%s] | %v\n", nopt.Dir, err) 84 | os.Exit(1) 85 | } 86 | defer ndb.Close() 87 | 88 | root := tree.Root() 89 | 90 | walk := func(bdir []byte, dcrc interface{}) bool { 91 | 92 | rand.Seed(time.Now().UnixNano()) 93 | 94 | m.dir = string(bdir) 95 | m.crc = dcrc.(uint64) 96 | m.thr = rand.Intn(searchinit-1+1) + 1 97 | 98 | mslice = append(mslice, m) 99 | 100 | return false 101 | 102 | } 103 | 104 | root.Walk(walk) 105 | 106 | for i := 1; i <= searchinit; i++ { 107 | 108 | wgs.Add(1) 109 | 110 | go func(i int) { 111 | defer wgs.Done() 112 | 113 | var nval RawKeysData 114 | 115 | for _, elm := range mslice { 116 | 117 | if elm.thr != i { 118 | continue 119 | } 120 | 121 | dirname := elm.dir 122 | dcrc := elm.crc 123 | 124 | nbucket := strconv.FormatUint(dcrc, 16) 125 | 126 | files, err := ioutil.ReadDir(dirname) 127 | if err != nil { 128 | appLogger.Errorf("| Can`t read directory error | Path [%s] | %v", dirname, err) 129 | fmt.Printf("Can`t read directory error | Path [%s] | %v\n", dirname, err) 130 | os.Exit(1) 131 | } 132 | 133 | for _, file := range files { 134 | 135 | fname := file.Name() 136 | fmode := file.Mode() 137 | 138 | if !file.IsDir() && file.Mode()&os.ModeType != 0 { 139 | continue 140 | } 141 | 142 | var nkey []byte 143 | 144 | size := int64(0) 145 | date := int64(0) 146 | 147 | bname := rgxbolt.MatchString(fname) 148 | cname := rgxcrcbolt.MatchString(fname) 149 | 150 | if !bname && !cname { 151 | 152 | size = file.Size() 153 | date = file.ModTime().Unix() 154 | 155 | nval.Size = uint64(size) 156 | nval.Date = uint64(date) 157 | nval.Prnt = uint32(0) 158 | nval.Buck = uint16(0) 159 | nval.Type = uint16(0) 160 | 161 | if !file.IsDir() { 162 | nkey = []byte("f:" + fname) 163 | nval.Type = uint16(0) 164 | } else { 165 | nkey = []byte("d:" + fname) 166 | nval.Type = uint16(2) 167 | } 168 | 169 | nbuffer := new(bytes.Buffer) 170 | 171 | err = binary.Write(nbuffer, Endian, nval) 172 | if err != nil { 173 | appLogger.Errorf("| Can`t write to binary buffer error | File/Path [%s] | %v", fname, err) 174 | fmt.Printf("Can`t write to binary buffer error | File/Path [%s] | %v\n", fname, err) 175 | os.Exit(1) 176 | } 177 | 178 | // json.NewEncoder(nbuffer).Encode(nval) 179 | 180 | err = ndb.Update(func(tx *nutsdb.Tx) error { 181 | 182 | err := tx.Put(nbucket, nkey, nbuffer.Bytes(), 0) 183 | if err != nil { 184 | return err 185 | } 186 | 187 | return nil 188 | 189 | }) 190 | 191 | nbuffer.Reset() 192 | 193 | if err != nil { 194 | appLogger.Errorf("| Can`t write to search db error | File/Path [%s] | %v", fname, err) 195 | fmt.Printf("Can`t write to search db error | File/Path [%s] | %v\n", fname, err) 196 | os.Exit(1) 197 | } 198 | 199 | continue 200 | 201 | } 202 | 203 | if file.IsDir() || cname { 204 | continue 205 | } 206 | 207 | var prnt uint64 = 0 208 | 209 | dbf := filepath.Clean(dirname + "/" + fname) 210 | 211 | if strings.ContainsRune(fname, 95) { 212 | 213 | prnt, err = strconv.ParseUint(strings.Split(strings.TrimSuffix(fname, ".bolt"), "_")[1], 10, 64) 214 | if err != nil { 215 | appLogger.Errorf("| Bad db file name error | DB [%s] | %v", dbf, err) 216 | fmt.Printf("Bad db file name error | DB [%s] | %v\n", dbf, err) 217 | os.Exit(1) 218 | } 219 | 220 | } 221 | 222 | db, err := BoltOpenRead(dbf, fmode, timeout, opentries, freelist) 223 | if err != nil { 224 | appLogger.Errorf("| Can`t open db file error | DB [%s] | %v", dbf, err) 225 | fmt.Printf("Can`t open db file error | DB [%s] | %v\n", dbf, err) 226 | os.Exit(1) 227 | } 228 | 229 | err = db.View(func(tx *bolt.Tx) error { 230 | 231 | b := tx.Bucket([]byte(ibucket)) 232 | if b != nil { 233 | 234 | pos := b.Cursor() 235 | 236 | for inkey, inval := pos.First(); inkey != nil; inkey, inval = pos.Next() { 237 | 238 | nkey = []byte("b:" + string(inkey)) 239 | 240 | nbck, err := strconv.Atoi(strings.TrimPrefix(string(inval), "wzd")) 241 | if err != nil { 242 | return err 243 | } 244 | 245 | ksize, err := KeyGetVal(db, sbucket, inkey) 246 | if err != nil { 247 | return err 248 | } 249 | 250 | kdate, err := KeyGetVal(db, tbucket, inkey) 251 | if err != nil { 252 | return err 253 | } 254 | 255 | if ksize != nil && kdate != nil { 256 | 257 | nval.Size = Endian.Uint64(ksize) 258 | nval.Date = Endian.Uint64(kdate) 259 | nval.Prnt = uint32(prnt) 260 | nval.Buck = uint16(nbck) 261 | nval.Type = uint16(1) 262 | 263 | } else { 264 | return serr 265 | } 266 | 267 | nbuffer := new(bytes.Buffer) 268 | 269 | err = binary.Write(nbuffer, Endian, nval) 270 | if err != nil { 271 | return err 272 | } 273 | 274 | // json.NewEncoder(nbuffer).Encode(nval) 275 | 276 | err = NDBInsert(ndb, nbucket, nkey, nbuffer.Bytes(), 0) 277 | if err != nil { 278 | appLogger.Errorf("| Can`t write to search db error | File/Path [%s] | In-Bolt File [%s] | %v", fname, string(inkey), err) 279 | fmt.Printf("Can`t write to search db error | File/Path [%s] | In-Bolt File [%s] | %v\n", fname, string(inkey), err) 280 | os.Exit(1) 281 | } 282 | 283 | nbuffer.Reset() 284 | 285 | } 286 | 287 | } else { 288 | return verr 289 | } 290 | 291 | return nil 292 | 293 | }) 294 | 295 | if err != nil { 296 | db.Close() 297 | appLogger.Errorf("| Can`t do work with db`s error | File [%s] | DB [%s] | Search DB [%s] | %v", fname, dbf, nopt.Dir, err) 298 | fmt.Printf("Can`t do work with db`s error | File [%s] | DB [%s] | Search DB [%s] | %v\n", fname, dbf, nopt.Dir, err) 299 | os.Exit(1) 300 | } 301 | 302 | db.Close() 303 | continue 304 | 305 | } 306 | 307 | } 308 | 309 | }(i) 310 | 311 | } 312 | 313 | wgs.Wait() 314 | 315 | mslice = nil 316 | 317 | } 318 | -------------------------------------------------------------------------------- /log.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright © 2020 Andrey Kuvshinov. Contacts: 4 | Copyright © 2020 Eltaline OU. Contacts: 5 | All rights reserved. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | The wZD project contains unmodified/modified libraries imports too with 20 | separate copyright notices and license terms. Your use of the source code 21 | this libraries is subject to the terms and conditions of licenses these libraries. 22 | 23 | */ 24 | 25 | package main 26 | 27 | import ( 28 | "fmt" 29 | "github.com/kataras/golog" 30 | "os" 31 | "path/filepath" 32 | ) 33 | 34 | // Loggers 35 | 36 | // AppLogger : logger 37 | func AppLogger() (*golog.Logger, *os.File) { 38 | 39 | appLogger := golog.New() 40 | 41 | applogfile := appLogFile() 42 | 43 | if debugmode { 44 | appLogger.SetLevel("debug") 45 | appLogger.AddOutput(applogfile) 46 | } else { 47 | appLogger.SetLevel("warn") 48 | appLogger.SetOutput(applogfile) 49 | } 50 | 51 | return appLogger, applogfile 52 | 53 | } 54 | 55 | // GetLogger : logger 56 | func GetLogger() (*golog.Logger, *os.File) { 57 | 58 | getLogger := golog.New() 59 | 60 | getlogfile := getLogFile() 61 | 62 | if debugmode { 63 | getLogger.SetLevel("debug") 64 | getLogger.AddOutput(getlogfile) 65 | } else { 66 | getLogger.SetLevel("warn") 67 | getLogger.SetOutput(getlogfile) 68 | } 69 | 70 | return getLogger, getlogfile 71 | 72 | } 73 | 74 | // PutLogger : logger 75 | func PutLogger() (*golog.Logger, *os.File) { 76 | 77 | putLogger := golog.New() 78 | 79 | putlogfile := putLogFile() 80 | 81 | if debugmode { 82 | putLogger.SetLevel("debug") 83 | putLogger.AddOutput(putlogfile) 84 | } else { 85 | putLogger.SetLevel("warn") 86 | putLogger.SetOutput(putlogfile) 87 | } 88 | 89 | return putLogger, putlogfile 90 | 91 | } 92 | 93 | // DelLogger : logger 94 | func DelLogger() (*golog.Logger, *os.File) { 95 | 96 | delLogger := golog.New() 97 | 98 | dellogfile := delLogFile() 99 | 100 | if debugmode { 101 | delLogger.SetLevel("debug") 102 | delLogger.AddOutput(dellogfile) 103 | } else { 104 | delLogger.SetLevel("warn") 105 | delLogger.SetOutput(dellogfile) 106 | } 107 | 108 | return delLogger, dellogfile 109 | 110 | } 111 | 112 | // Log Paths 113 | 114 | func todayAppFilename() string { 115 | logfile := filepath.Clean(logdir + "/app.log") 116 | return logfile 117 | } 118 | 119 | func todayGetFilename() string { 120 | logfile := filepath.Clean(logdir + "/get.log") 121 | return logfile 122 | } 123 | 124 | func todayPutFilename() string { 125 | logfile := filepath.Clean(logdir + "/put.log") 126 | return logfile 127 | } 128 | 129 | func todayDelFilename() string { 130 | logfile := filepath.Clean(logdir + "/del.log") 131 | return logfile 132 | } 133 | 134 | // Log Files 135 | 136 | func appLogFile() *os.File { 137 | 138 | filename := todayAppFilename() 139 | applogfile, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, logmode) 140 | if err != nil { 141 | fmt.Printf("Can`t open/create 'app' log file error | File [%s] | %v", filename, err) 142 | os.Exit(1) 143 | } 144 | 145 | err = os.Chmod(filename, logmode) 146 | if err != nil { 147 | fmt.Printf("Can`t chmod log file error | File [%s] | %v", filename, err) 148 | os.Exit(1) 149 | } 150 | 151 | return applogfile 152 | } 153 | 154 | func getLogFile() *os.File { 155 | filename := todayGetFilename() 156 | getlogfile, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, logmode) 157 | if err != nil { 158 | fmt.Printf("Can`t open/create 'get' log file error | File [%s] | %v", filename, err) 159 | os.Exit(1) 160 | } 161 | 162 | err = os.Chmod(filename, logmode) 163 | if err != nil { 164 | fmt.Printf("Can`t chmod log file error | File [%s] | %v", filename, err) 165 | os.Exit(1) 166 | } 167 | 168 | return getlogfile 169 | } 170 | 171 | func putLogFile() *os.File { 172 | filename := todayPutFilename() 173 | putlogfile, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, logmode) 174 | if err != nil { 175 | fmt.Printf("Can`t open/create 'put' log file error | File [%s] | %v", filename, err) 176 | os.Exit(1) 177 | } 178 | 179 | err = os.Chmod(filename, logmode) 180 | if err != nil { 181 | fmt.Printf("Can`t chmod log file error | File [%s] | %v", filename, err) 182 | os.Exit(1) 183 | } 184 | 185 | return putlogfile 186 | } 187 | 188 | func delLogFile() *os.File { 189 | filename := todayDelFilename() 190 | dellogfile, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, logmode) 191 | if err != nil { 192 | fmt.Printf("Can`t open/create 'del' log file error | File [%s] | %v", filename, err) 193 | os.Exit(1) 194 | } 195 | 196 | err = os.Chmod(filename, logmode) 197 | if err != nil { 198 | fmt.Printf("Can`t chmod log file error | File [%s] | %v", filename, err) 199 | os.Exit(1) 200 | } 201 | 202 | return dellogfile 203 | } 204 | -------------------------------------------------------------------------------- /range.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright © 2020 Andrey Kuvshinov. Contacts: 4 | Copyright © 2020 Eltaline OU. Contacts: 5 | All rights reserved. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | The wZD project contains unmodified/modified libraries imports too with 20 | separate copyright notices and license terms. Your use of the source code 21 | this libraries is subject to the terms and conditions of licenses these libraries. 22 | 23 | */ 24 | 25 | package main 26 | 27 | import ( 28 | "errors" 29 | "strconv" 30 | "strings" 31 | ) 32 | 33 | // Ranges Helpers 34 | 35 | // ParseByRange : Accept-Ranges helper 36 | func ParseByRange(rngs string, size int64) ([]ReqRange, error) { 37 | 38 | rngerr := errors.New("bad range") 39 | 40 | var ranges []ReqRange 41 | 42 | const headb = "bytes=" 43 | if !strings.HasPrefix(rngs, headb) { 44 | return nil, rngerr 45 | } 46 | 47 | for _, rngobj := range strings.Split(rngs[len(headb):], ",") { 48 | 49 | rngobj = strings.TrimSpace(rngobj) 50 | if rngobj == "" { 51 | continue 52 | } 53 | 54 | i := strings.Index(rngobj, "-") 55 | if i < 0 { 56 | return nil, rngerr 57 | } 58 | 59 | start, end := strings.TrimSpace(rngobj[:i]), strings.TrimSpace(rngobj[i+1:]) 60 | 61 | var r ReqRange 62 | 63 | if start == "" { 64 | 65 | i, err := strconv.ParseInt(end, 10, 64) 66 | if err != nil { 67 | return nil, rngerr 68 | } 69 | 70 | if i > size { 71 | i = size 72 | } 73 | 74 | r.start = size - i 75 | r.length = size - r.start 76 | 77 | } else { 78 | 79 | i, err := strconv.ParseInt(start, 10, 64) 80 | if err != nil || i >= size || i < 0 { 81 | return nil, rngerr 82 | } 83 | 84 | r.start = i 85 | 86 | if end == "" { 87 | 88 | r.length = size - r.start 89 | 90 | } else { 91 | 92 | i, err := strconv.ParseInt(end, 10, 64) 93 | if err != nil || r.start > i { 94 | return nil, rngerr 95 | } 96 | 97 | if i >= size { 98 | i = size - 1 99 | } 100 | 101 | r.length = i - r.start + 1 102 | 103 | } 104 | 105 | } 106 | 107 | ranges = append(ranges, r) 108 | 109 | } 110 | 111 | return ranges, nil 112 | 113 | } 114 | -------------------------------------------------------------------------------- /scripts/docker/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for X in `env`; do 4 | 5 | echo "$VARKEY:$VARVAL" 6 | 7 | VARKEY=`echo $X | awk -F "=" '{print $1}'` 8 | VARVAL=`echo $X | awk -F "=" '{print $2}'` 9 | 10 | sed -i -e "s#\bvar_$VARKEY\b#$VARVAL#" /etc/wzd/wzd.conf 11 | 12 | if [ "$VARKEY" == "root" ] ; then 13 | 14 | chown -R wzd.wzd $VARVAL 15 | 16 | fi 17 | 18 | done 19 | 20 | ulimit -n 131072 21 | 22 | su wzd -c 'ulimit -n 131072 ; /usr/bin/wzd --config /etc/wzd/wzd.conf' 23 | 24 | #END -------------------------------------------------------------------------------- /scripts/postinstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z `getent group wzd` ]; then 4 | groupadd wzd 5 | fi 6 | 7 | if [ -z `getent passwd wzd` ]; then 8 | useradd wzd -g wzd 9 | fi 10 | 11 | install --mode=755 --owner=wzd --group=wzd --directory /var/lib/wzd 12 | install --mode=755 --owner=wzd --group=wzd --directory /var/log/wzd 13 | 14 | systemctl daemon-reload 15 | 16 | #END -------------------------------------------------------------------------------- /sys.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright © 2020 Andrey Kuvshinov. Contacts: 4 | Copyright © 2020 Eltaline OU. Contacts: 5 | All rights reserved. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | The wZD project contains unmodified/modified libraries imports too with 20 | separate copyright notices and license terms. Your use of the source code 21 | this libraries is subject to the terms and conditions of licenses these libraries. 22 | 23 | */ 24 | 25 | package main 26 | 27 | import ( 28 | "encoding/binary" 29 | "fmt" 30 | // "github.com/eltaline/machineid" 31 | "os" 32 | "os/user" 33 | "strconv" 34 | "unsafe" 35 | ) 36 | 37 | // System Helpers 38 | 39 | // DetectEndian : determine system endianess function 40 | func DetectEndian() { 41 | 42 | buf := [2]byte{} 43 | *(*uint16)(unsafe.Pointer(&buf[0])) = uint16(0xABCD) 44 | 45 | switch buf { 46 | case [2]byte{0xCD, 0xAB}: 47 | Endian = binary.LittleEndian 48 | case [2]byte{0xAB, 0xCD}: 49 | Endian = binary.BigEndian 50 | default: 51 | fmt.Printf("Can`t determine native endianness error\n") 52 | os.Exit(1) 53 | } 54 | 55 | } 56 | 57 | // DetectUser : determine current system user and group 58 | func DetectUser() { 59 | 60 | cuser, err := user.Current() 61 | if err != nil { 62 | fmt.Printf("Can`t determine current user error | %v\n", err) 63 | os.Exit(1) 64 | } 65 | 66 | Uid, err = strconv.ParseInt(cuser.Uid, 10, 16) 67 | if err != nil { 68 | fmt.Printf("Can`t int convert current user uid error | %v\n", err) 69 | os.Exit(1) 70 | } 71 | 72 | Gid, err = strconv.ParseInt(cuser.Gid, 10, 16) 73 | if err != nil { 74 | fmt.Printf("Can`t int convert current user gid error | %v\n", err) 75 | os.Exit(1) 76 | } 77 | 78 | } 79 | 80 | // GetPID : get current pid number and return int and string representation of pid 81 | func GetPID() (gpid string, fpid string) { 82 | 83 | gpid = fmt.Sprintf("%d", os.Getpid()) 84 | fpid = fmt.Sprintf("%s\n", gpid) 85 | 86 | return gpid, fpid 87 | 88 | } 89 | 90 | // MachineID : set globally machine identity 91 | /*func MachineID() { 92 | 93 | var err error 94 | 95 | machid, err = machineid.ID() 96 | if err != nil { 97 | machid = "nomachineid" 98 | } 99 | 100 | }*/ 101 | -------------------------------------------------------------------------------- /tree.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright © 2020 Andrey Kuvshinov. Contacts: 4 | Copyright © 2020 Eltaline OU. Contacts: 5 | All rights reserved. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | The wZD project contains unmodified/modified libraries imports too with 20 | separate copyright notices and license terms. Your use of the source code 21 | this libraries is subject to the terms and conditions of licenses these libraries. 22 | 23 | */ 24 | 25 | package main 26 | 27 | import ( 28 | "context" 29 | "fmt" 30 | "github.com/eltaline/cwalk" 31 | "github.com/pieterclaerhout/go-waitgroup" 32 | "hash/crc64" 33 | "os" 34 | "path/filepath" 35 | "strings" 36 | ) 37 | 38 | // TreeInit : Tree Database Initialization 39 | func TreeInit() { 40 | 41 | var err error 42 | 43 | // Variables 44 | 45 | mdir := make(map[string]bool) 46 | 47 | for _, Server := range config.Server { 48 | 49 | root := filepath.Clean(Server.ROOT) 50 | uroot := root 51 | 52 | _, found := mdir[root] 53 | 54 | for { 55 | 56 | if uroot == "/" { 57 | break 58 | } 59 | 60 | for dirname := range mdir { 61 | 62 | if strings.HasPrefix(dirname, root+"/") { 63 | delete(mdir, dirname) 64 | found = false 65 | } 66 | 67 | } 68 | 69 | _, ufound := mdir[uroot] 70 | 71 | if ufound { 72 | found = true 73 | break 74 | } 75 | 76 | uroot = filepath.Dir(uroot) 77 | 78 | } 79 | 80 | if !found { 81 | mdir[root] = true 82 | } 83 | 84 | } 85 | 86 | ctx, cancel := context.WithCancel(context.Background()) 87 | defer cancel() 88 | 89 | qwg, ctx := waitgroup.NewErrorGroup(ctx, searchinit) 90 | 91 | Main: 92 | 93 | for idirname := range mdir { 94 | 95 | select { 96 | case <-ctx.Done(): 97 | err = ctx.Err() 98 | break Main 99 | default: 100 | } 101 | 102 | qwait := make(chan bool) 103 | 104 | qwg.Add(func() error { 105 | 106 | dirname := idirname 107 | 108 | qwait <- true 109 | 110 | err := cwalk.Walk(dirname, func(partpath string, ln os.FileInfo, err error) error { 111 | 112 | if err != nil { 113 | return err 114 | } 115 | 116 | if ln.IsDir() { 117 | 118 | fullpath := []byte(filepath.Clean(dirname + "/" + partpath)) 119 | 120 | radix.Lock() 121 | tree, _, _ = tree.Insert(fullpath, crc64.Checksum(fullpath, ctbl64)) 122 | radix.Unlock() 123 | 124 | } 125 | 126 | return nil 127 | 128 | }) 129 | 130 | if err != nil { 131 | cancel() 132 | return err 133 | } 134 | 135 | return nil 136 | 137 | }) 138 | 139 | <-qwait 140 | 141 | } 142 | 143 | werr := qwg.Wait() 144 | 145 | if err != nil || werr != nil { 146 | fmt.Printf("Tree initialization error | %v | %v\n", err, werr) 147 | os.Exit(1) 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /wza/wza: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eltaline/wzd/cd6cbc1dd517a779a2c66b27d68418cd846c276d/wza/wza --------------------------------------------------------------------------------