init import

This commit is contained in:
Олег Бородин
2026-05-24 11:02:51 +02:00
commit 25365aef77
154 changed files with 21501 additions and 0 deletions
+29
View File
@@ -0,0 +1,29 @@
#!/bin/sh
MAX_SIZE=1024 # 1 MB limit in KB
for file in $(git diff --cached --name-only --diff-filter=ACM); do
if [ ! -e "$file" ]; then continue; fi
size=$(du -k "$file" | cut -f1)
if [ $size -gt $MAX_SIZE ]; then
echo "Error: File $file is larger than the allowed size of $((MAX_SIZE / 1024)) MB."
exit 1
fi
done
status=0
for file in $(git diff --cached --name-only | grep -E '\.go$'); do
badfile=$(gofmt -l "$file")
if test -n "$badfile" ; then
echo "Error: file needs gofmt: $badfile"
status=1
fi
done
# If any files were not formatted, exit with a non-zero status to abort the commit.
if [ "$status" -ne 0 ]; then
echo "git pre-commit check failed: some Go files are not formatted."
exit 1
fi
exit 0
+24
View File
@@ -0,0 +1,24 @@
*~
autom4te.cache
Makefile
config.status
config.log
cmd/mbased/mbased
cmd/mbasectl/mbasectl
*.db
*.db-shm
*.db-wal
*.tar.*
*.tmp.*
*.tar
*.bin
tmp
mbased
mbasectl
DIST
*.tar.gz
*.deb
*.rpm
mbased.service
variant.go
initrc/mbased
+26
View File
@@ -0,0 +1,26 @@
FROM alpine:3.23 AS builder
RUN apk --no-cache add make binutils gcc libc-dev automake autoconf curl
RUN curl -o /usr/local/lib/go.tar.gz https://dl.google.com/go/go1.26.2.linux-amd64.tar.gz
RUN cd /usr/local/lib && tar xzf go.tar.gz
RUN cd /usr/local/bin && ln -sf ../lib/go/bin/* .
WORKDIR /app/src/
COPY go.mod go.sum .
COPY . .
RUN ./configure --prefix=/app
RUN make all install
RUN make clean
RUN rm -rf /app/src
FROM alpine:3.23 AS runner
COPY --from=builder /app /app
RUN chmod 1777 /var
RUN mkdir -p /app/etc/mbase
RUN touch /app/etc/mbase/mbased.yaml
WORKDIR /app
#USER daemon:daemon
ENTRYPOINT ["/app/sbin/mbased", "--asDaemon=false"]
+439
View File
@@ -0,0 +1,439 @@
Attribution-NonCommercial-ShareAlike 4.0 International
=======================================================================
Creative Commons Corporation ("Creative Commons") is not a law firm and
does not provide legal services or legal advice. Distribution of
Creative Commons public licenses does not create a lawyer-client or
other relationship. Creative Commons makes its licenses and related
information available on an "as-is" basis. Creative Commons gives no
warranties regarding its licenses, any material licensed under their
terms and conditions, or any related information. Creative Commons
disclaims all liability for damages resulting from their use to the
fullest extent possible.
Using Creative Commons Public Licenses
Creative Commons public licenses provide a standard set of terms and
conditions that creators and other rights holders may use to share
original works of authorship and other material subject to copyright
and certain other rights specified in the public license below. The
following considerations are for informational purposes only, are not
exhaustive, and do not form part of our licenses.
Considerations for licensors: Our public licenses are
intended for use by those authorized to give the public
permission to use material in ways otherwise restricted by
copyright and certain other rights. Our licenses are
irrevocable. Licensors should read and understand the terms
and conditions of the license they choose before applying it.
Licensors should also secure all rights necessary before
applying our licenses so that the public can reuse the
material as expected. Licensors should clearly mark any
material not subject to the license. This includes other CC-
licensed material, or material used under an exception or
limitation to copyright. More considerations for licensors:
wiki.creativecommons.org/Considerations_for_licensors
Considerations for the public: By using one of our public
licenses, a licensor grants the public permission to use the
licensed material under specified terms and conditions. If
the licensor's permission is not necessary for any reason--for
example, because of any applicable exception or limitation to
copyright--then that use is not regulated by the license. Our
licenses grant only permissions under copyright and certain
other rights that a licensor has authority to grant. Use of
the licensed material may still be restricted for other
reasons, including because others have copyright or other
rights in the material. A licensor may make special requests,
such as asking that all changes be marked or described.
Although not required by our licenses, you are encouraged to
respect those requests where reasonable. More considerations
for the public:
wiki.creativecommons.org/Considerations_for_licensees
=======================================================================
Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
Public License
By exercising the Licensed Rights (defined below), You accept and agree
to be bound by the terms and conditions of this Creative Commons
Attribution-NonCommercial-ShareAlike 4.0 International Public License
("Public License"). To the extent this Public License may be
interpreted as a contract, You are granted the Licensed Rights in
consideration of Your acceptance of these terms and conditions, and the
Licensor grants You such rights in consideration of benefits the
Licensor receives from making the Licensed Material available under
these terms and conditions.
Section 1 -- Definitions.
a. Adapted Material means material subject to Copyright and Similar
Rights that is derived from or based upon the Licensed Material
and in which the Licensed Material is translated, altered,
arranged, transformed, or otherwise modified in a manner requiring
permission under the Copyright and Similar Rights held by the
Licensor. For purposes of this Public License, where the Licensed
Material is a musical work, performance, or sound recording,
Adapted Material is always produced where the Licensed Material is
synched in timed relation with a moving image.
b. Adapter's License means the license You apply to Your Copyright
and Similar Rights in Your contributions to Adapted Material in
accordance with the terms and conditions of this Public License.
c. BY-NC-SA Compatible License means a license listed at
creativecommons.org/compatiblelicenses, approved by Creative
Commons as essentially the equivalent of this Public License.
d. Copyright and Similar Rights means copyright and/or similar rights
closely related to copyright including, without limitation,
performance, broadcast, sound recording, and Sui Generis Database
Rights, without regard to how the rights are labeled or
categorized. For purposes of this Public License, the rights
specified in Section 2(b)(1)-(2) are not Copyright and Similar
Rights.
e. Effective Technological Measures means those measures that, in the
absence of proper authority, may not be circumvented under laws
fulfilling obligations under Article 11 of the WIPO Copyright
Treaty adopted on December 20, 1996, and/or similar international
agreements.
f. Exceptions and Limitations means fair use, fair dealing, and/or
any other exception or limitation to Copyright and Similar Rights
that applies to Your use of the Licensed Material.
g. License Elements means the license attributes listed in the name
of a Creative Commons Public License. The License Elements of this
Public License are Attribution, NonCommercial, and ShareAlike.
h. Licensed Material means the artistic or literary work, database,
or other material to which the Licensor applied this Public
License.
i. Licensed Rights means the rights granted to You subject to the
terms and conditions of this Public License, which are limited to
all Copyright and Similar Rights that apply to Your use of the
Licensed Material and that the Licensor has authority to license.
j. Licensor means the individual(s) or entity(ies) granting rights
under this Public License.
k. NonCommercial means not primarily intended for or directed towards
commercial advantage or monetary compensation. For purposes of
this Public License, the exchange of the Licensed Material for
other material subject to Copyright and Similar Rights by digital
file-sharing or similar means is NonCommercial provided there is
no payment of monetary compensation in connection with the
exchange.
l. Share means to provide material to the public by any means or
process that requires permission under the Licensed Rights, such
as reproduction, public display, public performance, distribution,
dissemination, communication, or importation, and to make material
available to the public including in ways that members of the
public may access the material from a place and at a time
individually chosen by them.
m. Sui Generis Database Rights means rights other than copyright
resulting from Directive 96/9/EC of the European Parliament and of
the Council of 11 March 1996 on the legal protection of databases,
as amended and/or succeeded, as well as other essentially
equivalent rights anywhere in the world.
n. You means the individual or entity exercising the Licensed Rights
under this Public License. Your has a corresponding meaning.
Section 2 -- Scope.
a. License grant.
1. Subject to the terms and conditions of this Public License,
the Licensor hereby grants You a worldwide, royalty-free,
non-sublicensable, non-exclusive, irrevocable license to
exercise the Licensed Rights in the Licensed Material to:
a. reproduce and Share the Licensed Material, in whole or
in part, for NonCommercial purposes only; and
b. produce, reproduce, and Share Adapted Material for
NonCommercial purposes only.
2. Exceptions and Limitations. For the avoidance of doubt, where
Exceptions and Limitations apply to Your use, this Public
License does not apply, and You do not need to comply with
its terms and conditions.
3. Term. The term of this Public License is specified in Section
6(a).
4. Media and formats; technical modifications allowed. The
Licensor authorizes You to exercise the Licensed Rights in
all media and formats whether now known or hereafter created,
and to make technical modifications necessary to do so. The
Licensor waives and/or agrees not to assert any right or
authority to forbid You from making technical modifications
necessary to exercise the Licensed Rights, including
technical modifications necessary to circumvent Effective
Technological Measures. For purposes of this Public License,
simply making modifications authorized by this Section 2(a)
(4) never produces Adapted Material.
5. Downstream recipients.
a. Offer from the Licensor -- Licensed Material. Every
recipient of the Licensed Material automatically
receives an offer from the Licensor to exercise the
Licensed Rights under the terms and conditions of this
Public License.
b. Additional offer from the Licensor -- Adapted Material.
Every recipient of Adapted Material from You
automatically receives an offer from the Licensor to
exercise the Licensed Rights in the Adapted Material
under the conditions of the Adapter's License You apply.
c. No downstream restrictions. You may not offer or impose
any additional or different terms or conditions on, or
apply any Effective Technological Measures to, the
Licensed Material if doing so restricts exercise of the
Licensed Rights by any recipient of the Licensed
Material.
6. No endorsement. Nothing in this Public License constitutes or
may be construed as permission to assert or imply that You
are, or that Your use of the Licensed Material is, connected
with, or sponsored, endorsed, or granted official status by,
the Licensor or others designated to receive attribution as
provided in Section 3(a)(1)(A)(i).
b. Other rights.
1. Moral rights, such as the right of integrity, are not
licensed under this Public License, nor are publicity,
privacy, and/or other similar personality rights; however, to
the extent possible, the Licensor waives and/or agrees not to
assert any such rights held by the Licensor to the limited
extent necessary to allow You to exercise the Licensed
Rights, but not otherwise.
2. Patent and trademark rights are not licensed under this
Public License.
3. To the extent possible, the Licensor waives any right to
collect royalties from You for the exercise of the Licensed
Rights, whether directly or through a collecting society
under any voluntary or waivable statutory or compulsory
licensing scheme. In all other cases the Licensor expressly
reserves any right to collect such royalties, including when
the Licensed Material is used other than for NonCommercial
purposes.
Section 3 -- License Conditions.
Your exercise of the Licensed Rights is expressly made subject to the
following conditions.
a. Attribution.
1. If You Share the Licensed Material (including in modified
form), You must:
a. retain the following if it is supplied by the Licensor
with the Licensed Material:
i. identification of the creator(s) of the Licensed
Material and any others designated to receive
attribution, in any reasonable manner requested by
the Licensor (including by pseudonym if
designated);
ii. a copyright notice;
iii. a notice that refers to this Public License;
iv. a notice that refers to the disclaimer of
warranties;
v. a URI or hyperlink to the Licensed Material to the
extent reasonably practicable;
b. indicate if You modified the Licensed Material and
retain an indication of any previous modifications; and
c. indicate the Licensed Material is licensed under this
Public License, and include the text of, or the URI or
hyperlink to, this Public License.
2. You may satisfy the conditions in Section 3(a)(1) in any
reasonable manner based on the medium, means, and context in
which You Share the Licensed Material. For example, it may be
reasonable to satisfy the conditions by providing a URI or
hyperlink to a resource that includes the required
information.
3. If requested by the Licensor, You must remove any of the
information required by Section 3(a)(1)(A) to the extent
reasonably practicable.
b. ShareAlike.
In addition to the conditions in Section 3(a), if You Share
Adapted Material You produce, the following conditions also apply.
1. The Adapter's License You apply must be a Creative Commons
license with the same License Elements, this version or
later, or a BY-NC-SA Compatible License.
2. You must include the text of, or the URI or hyperlink to, the
Adapter's License You apply. You may satisfy this condition
in any reasonable manner based on the medium, means, and
context in which You Share Adapted Material.
3. You may not offer or impose any additional or different terms
or conditions on, or apply any Effective Technological
Measures to, Adapted Material that restrict exercise of the
rights granted under the Adapter's License You apply.
Section 4 -- Sui Generis Database Rights.
Where the Licensed Rights include Sui Generis Database Rights that
apply to Your use of the Licensed Material:
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
to extract, reuse, reproduce, and Share all or a substantial
portion of the contents of the database for NonCommercial purposes
only;
b. if You include all or a substantial portion of the database
contents in a database in which You have Sui Generis Database
Rights, then the database in which You have Sui Generis Database
Rights (but not its individual contents) is Adapted Material,
including for purposes of Section 3(b); and
c. You must comply with the conditions in Section 3(a) if You Share
all or a substantial portion of the contents of the database.
For the avoidance of doubt, this Section 4 supplements and does not
replace Your obligations under this Public License where the Licensed
Rights include other Copyright and Similar Rights.
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
c. The disclaimer of warranties and limitation of liability provided
above shall be interpreted in a manner that, to the extent
possible, most closely approximates an absolute disclaimer and
waiver of all liability.
Section 6 -- Term and Termination.
a. This Public License applies for the term of the Copyright and
Similar Rights licensed here. However, if You fail to comply with
this Public License, then Your rights under this Public License
terminate automatically.
b. Where Your right to use the Licensed Material has terminated under
Section 6(a), it reinstates:
1. automatically as of the date the violation is cured, provided
it is cured within 30 days of Your discovery of the
violation; or
2. upon express reinstatement by the Licensor.
For the avoidance of doubt, this Section 6(b) does not affect any
right the Licensor may have to seek remedies for Your violations
of this Public License.
c. For the avoidance of doubt, the Licensor may also offer the
Licensed Material under separate terms or conditions or stop
distributing the Licensed Material at any time; however, doing so
will not terminate this Public License.
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
License.
Section 7 -- Other Terms and Conditions.
a. The Licensor shall not be bound by any additional or different
terms or conditions communicated by You unless expressly agreed.
b. Any arrangements, understandings, or agreements regarding the
Licensed Material not stated herein are separate from and
independent of the terms and conditions of this Public License.
Section 8 -- Interpretation.
a. For the avoidance of doubt, this Public License does not, and
shall not be interpreted to, reduce, limit, restrict, or impose
conditions on any use of the Licensed Material that could lawfully
be made without permission under this Public License.
b. To the extent possible, if any provision of this Public License is
deemed unenforceable, it shall be automatically reformed to the
minimum extent necessary to make it enforceable. If the provision
cannot be reformed, it shall be severed from this Public License
without affecting the enforceability of the remaining terms and
conditions.
c. No term or condition of this Public License will be waived and no
failure to comply consented to unless expressly agreed to by the
Licensor.
d. Nothing in this Public License constitutes or may be interpreted
as a limitation upon, or waiver of, any privileges and immunities
that apply to the Licensor or You, including from the legal
processes of any jurisdiction or authority.
=======================================================================
Creative Commons is not a party to its public
licenses. Notwithstanding, Creative Commons may elect to apply one of
its public licenses to material it publishes and in those instances
will be considered the “Licensor.” The text of the Creative Commons
public licenses is dedicated to the public domain under the CC0 Public
Domain Dedication. Except for the limited purpose of indicating that
material is shared under a Creative Commons public license or as
otherwise permitted by the Creative Commons policies published at
creativecommons.org/policies, Creative Commons does not authorize the
use of the trademark "Creative Commons" or any other trademark or logo
of Creative Commons without its prior written consent including,
without limitation, in connection with any unauthorized modifications
to any of its public licenses or any other arrangements,
understandings, or agreements concerning use of licensed material. For
the avoidance of doubt, this paragraph does not form part of the
public licenses.
Creative Commons may be contacted at creativecommons.org.
+280
View File
@@ -0,0 +1,280 @@
AUTOMAKE_OPTIONS = foreign no-dependencies no-installinfo
SUBDIRS = mans
GOFLAGS= -v
bin_PROGRAMS = mbasectl
sbin_PROGRAMS = mbased
mbasectl_SOURCES = \
cmd/mbasectl/main.go
EXTRA_mbasectl_SOURCES = \
cmd/mbasectl/util/util.go \
cmd/mbasectl/accountcmd/acccmd.go \
cmd/mbasectl/accountcmd/createacc.go \
cmd/mbasectl/accountcmd/creategrant.go \
cmd/mbasectl/accountcmd/delacc.go \
cmd/mbasectl/accountcmd/delgrant.go \
cmd/mbasectl/accountcmd/getacc.go \
cmd/mbasectl/accountcmd/getgrant.go \
cmd/mbasectl/accountcmd/grantcmd.go \
cmd/mbasectl/accountcmd/listacc.go \
cmd/mbasectl/accountcmd/listgrant.go \
cmd/mbasectl/accountcmd/printresp.go \
cmd/mbasectl/accountcmd/updacc.go \
cmd/mbasectl/accountcmd/updgrant.go
mbased_SOURCES = cmd/mbased/main.go
EXTRA_mbased_SOURCES = cmd/mbased/starter/starter.go
mbasectl$(EXEEXT): $(mbasectl_SOURCES) $(EXTRA_mbasectl_SOURCES) $(EXTRA_mbased_SOURCES)
env CGO_ENABLED=0 $(GO) build $(GOFLAGS) -o mbasectl$(EXEEXT) $(mbasectl_SOURCES)
mbased$(EXEEXT): $(mbased_SOURCES) $(EXTRA_mbased_SOURCES)
env CGO_ENABLED=1 $(GO) build $(GOFLAGS) -o mbased$(EXEEXT) $(mbased_SOURCES)
run: $(mbased_SOURCES)
cd cmd/mbased && env CGO_ENABLED=1 $(GO) run . --asDaemon=false --port=443
CWD=$(shell pwd)
EXTRA_mbased_SOURCES += \
app/accoper/createacc.go \
app/accoper/creategrant.go \
app/accoper/delacc.go \
app/accoper/delgrant.go \
app/accoper/getacc.go \
app/accoper/getgrant.go \
app/accoper/listacc.go \
app/accoper/listgrant.go \
app/accoper/operator.go \
app/accoper/updacc.go \
app/accoper/updgrant.go \
app/config/config.go \
app/config/variant.go \
app/handler/aaafunc.go \
app/handler/account.go \
app/handler/grant.go \
app/handler/handler.go \
app/handler/notfound.go \
app/handler/response.go \
app/handler/service.go \
app/locker/locker.go \
app/logger/logger.go \
app/maindb/account.go \
app/maindb/grant.go \
app/maindb/init.go \
app/maindb/maindb.go \
app/maindb/scheme.go \
app/router/bindobj.go \
app/router/context.go \
app/router/corsmw.go \
app/router/loggingmw.go \
app/router/pathc.go \
app/router/recovermw.go \
app/router/router.go \
app/server/server.go \
app/server/listen.go \
app/service/service.go \
\
app/servoper/operator.go \
app/servoper/service.go \
\
pkg/accntcli/client.go \
pkg/accntcli/createacc.go \
pkg/accntcli/creategrant.go \
pkg/accntcli/delacc.go \
pkg/accntcli/delgrant.go \
pkg/accntcli/getacc.go \
pkg/accntcli/getgrant.go \
pkg/accntcli/httpcall.go \
pkg/accntcli/listacc.go \
pkg/accntcli/listgrants.go \
pkg/accntcli/referer.go \
pkg/accntcli/servhello.go \
pkg/accntcli/updateacc.go \
pkg/accntcli/updgrant.go \
pkg/auxhttp/basic.go \
pkg/auxhttp/crange.go \
pkg/auxpwd/passwd.go \
pkg/auxtool/cleandir.go \
pkg/auxtool/fileex.go \
pkg/auxtool/randstr.go \
pkg/auxtool/tmpfile.go \
pkg/auxtool/unixnow.go \
pkg/auxutar/utar.go \
pkg/auxuuid/uuid.go \
pkg/auxx509/x509cert.go \
pkg/descr/account.go \
pkg/descr/grant.go \
pkg/descr/server.go \
pkg/terms/terms.go
EXTRA_DIST = vendor/* \
\
Containerfile \
go.mod \
go.sum \
LICENSE.txt \
README.md \
DEVEL.md \
\
test/file_test.go \
\
chart/Chart.yaml.in \
chart/.gitignore \
chart/.helmignore \
chart/templates/configmap.yaml \
chart/templates/deployment.yaml \
chart/templates/_imagepath.tpl \
chart/templates/_serviceport.tmpl \
chart/templates/service.yaml \
chart/templates/_storageclass.tpl \
chart/templates/_storagesize.tpl \
chart/templates/_userpass.tpl \
chart/templates/volumeclaim.yaml \
chart/values.yaml.in \
\
debian/changelog.in \
debian/compat \
debian/control.in \
debian/files \
debian/.gitignore \
debian/mbase-control.install \
debian/mbase-control.postinst \
debian/mbase-control.postrm \
debian/mbase-control.preinst \
debian/mbase-control.prerm \
debian/mbase-service.install \
debian/mbase-service.postinst \
debian/mbase-service.postrm \
debian/mbase-service.preinst \
debian/mbase-service.prerm \
debian/patches/series \
debian/README.Debian \
debian/rules \
debian/source/format \
debian/source/include-binaries \
debian/watch \
\
initrc/.gitignore \
initrc/mbased.in \
initrc/mbased.service.in \
\
app/locker/locker_test.go \
app/logger/logger_test.go \
app/maindb/file_test.go \
app/maindb/grant_test.go \
app/router/pathc_test.go \
app/router/router_test.go \
pkg/auxpwd/passwd_test.go \
pkg/auxx509/x509cert_test.go \
\
docs/helm-chart-manifest.json.txt \
docs/mbase.drawio \
docs/mbase.png \
docs/podman-manifest.json.txt
format:
@dirs=$$($(FIND) $(CWD)/app $(CWD)/cmd $(CWD)/pkg $(CWD)/test \
-name '*.go' | $(XARGS) -n1 dirname | $(SORT) | $(UNIQ)); \
for dir in $$dirs;do \
(echo "====$$dir===="; cd $$dir && $(GO) fmt .); \
done
.PHONY: test
test:
cd test && $(GO) test -v .
DIST_DIR= $(shell pwd)/DIST
BUILD_DIR= $(shell pwd)/BUILD
IMAGE_REPO = localhost
IMAGE_NAME = $(PACKAGE_NAME):$(PACKAGE_VERSION)
IMAGE_TARNAME = $(PACKAGE_NAME)-$(PACKAGE_VERSION).img
IMAGE_CONTAINERFILE = Containerfile
image:: build-image
build-image: clean
# $(GO) mod vendor
mkdir -p $(DIST_DIR)
$(SUDO) $(PODMAN) build -t $(IMAGE_REPO)/$(IMAGE_NAME) -f $(IMAGE_CONTAINERFILE) .
rm -f $(DIST_DIR)/$(IMAGE_TARNAME)
$(SUDO) $(PODMAN) image save $(IMAGE_REPO)/$(IMAGE_NAME) --format oci-archive \
-o $(DIST_DIR)/$(IMAGE_TARNAME)
username=$$(whoami); \
$(SUDO) chown $$username $(DIST_DIR)/$(IMAGE_TARNAME)
# rm -rf vendor
CHART_NAME = $(PACKAGE_NAME)-$(PACKAGE_VERSION).tgz
chart:: build-chart
build-chart:
mkdir -p $(DIST_DIR)
$(HELM) package --destination $(DIST_DIR) chart/
$(DIST_ARCHIVES): dist
package:: debian-package
debian-package: $(DIST_ARCHIVES)
mkdir -p $(BUILD_DIR)
mv $(DIST_ARCHIVES) $(BUILD_DIR)
cd $(BUILD_DIR) && $(AMTAR) -xf $(DIST_ARCHIVES)
cd $(BUILD_DIR)/$(distdir) && ./configure --prefix=/usr
cd $(BUILD_DIR)/$(distdir) && $(MAKE) clean
cd $(BUILD_DIR)/$(distdir) && $(DPKGSOURCE) -i --before-build .
cd $(BUILD_DIR)/$(distdir) && $(DBUILDPACKAGE) -nc -us -uc -ui -i -b
mkdir -p $(DIST_DIR)
$(CP) $(BUILD_DIR)/*.deb $(DIST_DIR)
rm -rf $(BUILD_DIR)
FREEBSD_LOCALBASE = /usr/local
FREEBSD_RCDIR = $(FREEBSD_LOCALBASE)/etc/rc.d
LINUX_SYSTEMDDIR = /lib/systemd/system
install-data-local:
test -z $(DESTDIR)$(srv_confdir) || $(MKDIR_P) $(DESTDIR)$(srv_confdir)
test -z $(DESTDIR)$(srv_logdir) || $(MKDIR_P) $(DESTDIR)$(srv_logdir)
test -z $(DESTDIR)$(srv_rundir) || $(MKDIR_P) $(DESTDIR)$(srv_rundir)
test -z $(DESTDIR)$(srv_datadir) || $(MKDIR_P) $(DESTDIR)$(srv_datadir)
if FREEBSD_OS
test -z $(DESTDIR)$(FREEBSD_RCDIR) || $(MKDIR_P) $(DESTDIR)$(FREEBSD_RCDIR)
$(INSTALL_DATA) initrc/mbased $(DESTDIR)$(FREEBSD_RCDIR)
chmod a+x $(DESTDIR)$(FREEBSD_RCDIR)/mbased
endif
if LINUX_OS
if SYSTEMD
test -z $(DESTDIR)$(LINUX_SYSTEMDDIR) || $(MKDIR_P) $(DESTDIR)$(LINUX_SYSTEMDDIR)
$(INSTALL_DATA) initrc/mbased.service $(DESTDIR)$(LINUX_SYSTEMDDIR)
endif
endif
clean-local:
$(FIND) $(CWD) -name '*~' | $(XARGS) rm -f
rm -rf autom4te.cache
rm -f cmd/mbased/mbased
rm -f cmd/mbasectl/mbasectl
# rm -rf tmp/
distclean-local:
rm -rf autom4te.cache
rm -rf $(DIST_DIR)
rm -rf tmp/
SWAG_OUTDIR = ./
SWAG_OPTS = --parseDependency \
--parseDepth 3 \
--exclude pkg,vendor \
--outputTypes yaml,json \
--output $(SWAG_OUTDIR)
apidoc:
mkdir -p $(SWAG_OUTDIR)
$(SWAG) init $(SWAG_OPTS) -g ./cmd/mbased/main.go
+1162
View File
File diff suppressed because it is too large Load Diff
+55
View File
@@ -0,0 +1,55 @@
# mStore - multifunсtion Storage
Under contruction.
With the application you can store and share
- Any blobs
- Helm charts
- OCI images
## Copyright EN
Copyright 2026 Oleg Borodin <onborodin@gmail.com>
This work is published and licensed under a Creative CommonsAttribution-NonCommercial-ShareAlike 4.0 International License.
* Attribution — You must give appropriate credit,
provide a link to the license, and indicate if changes
were made. You may do so in any reasonable manner,
but not in any way that suggests the licensor endorses
you or your use.
* NonCommercial — You may not use the material for commercial purposes.
* ShareAlike — If you remix, transform, or build upon the material,
you must distribute your contributions under the same license
as the original.
Distribution of this work is permitted, but commercial usu are
strictly prohibited.
## Copyright RU
Права на данное произведение принадлежат Олег Бородин <onborodin@gmail.com> 2026
Это произведение распространяется под лицензией Creative Commons
Attribution-NonCommercial-NoDerivatives 4.0 International License
Разрешается распространение, при условии указания автора, произведенных изменений
и сохранении лицензии, но запрещаются коммерческое использование
данного произведения.
Вы можете свободно делиться, то есть копировать, распространять и передавать
другим лицам данное произведение при обязательном соблюдении следующих условий:
* Атрибуция: Вы должны атрибутировать произведение (указывать автора и источник)
в порядке, предусмотренном автором или лицензиаром.
* Некоммерческое использование: Вы не можете использовать это произведение
в коммерческих целях, в целях получения выгоды или оплаты.
* Распостранять аналогично: Если вы перерабатываете, преобразуете или
создаете производные произведения на основе данного материала,
вы должны распространять свои результаты на условиях той же лицензии,
что и оригинал.
Vendored
+977
View File
@@ -0,0 +1,977 @@
# generated automatically by aclocal 1.17 -*- Autoconf -*-
# Copyright (C) 1996-2024 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
m4_ifndef([AC_AUTOCONF_VERSION],
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.72],,
[m4_warning([this file was generated for autoconf 2.72.
You have another version of autoconf. It may work, but is not guaranteed to.
If you have problems, you may need to regenerate the build system entirely.
To do so, use the procedure documented by the package, typically 'autoreconf'.])])
# Copyright (C) 2002-2024 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_AUTOMAKE_VERSION(VERSION)
# ----------------------------
# Automake X.Y traces this macro to ensure aclocal.m4 has been
# generated from the m4 files accompanying Automake X.Y.
# (This private macro should not be called outside this file.)
AC_DEFUN([AM_AUTOMAKE_VERSION],
[am__api_version='1.17'
dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
dnl require some minimum version. Point them to the right macro.
m4_if([$1], [1.17], [],
[AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
])
# _AM_AUTOCONF_VERSION(VERSION)
# -----------------------------
# aclocal traces this macro to find the Autoconf version.
# This is a private macro too. Using m4_define simplifies
# the logic in aclocal, which can simply ignore this definition.
m4_define([_AM_AUTOCONF_VERSION], [])
# AM_SET_CURRENT_AUTOMAKE_VERSION
# -------------------------------
# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
[AM_AUTOMAKE_VERSION([1.17])dnl
m4_ifndef([AC_AUTOCONF_VERSION],
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
# AM_AUX_DIR_EXPAND -*- Autoconf -*-
# Copyright (C) 2001-2024 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
# $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to
# '$srcdir', '$srcdir/..', or '$srcdir/../..'.
#
# Of course, Automake must honor this variable whenever it calls a
# tool from the auxiliary directory. The problem is that $srcdir (and
# therefore $ac_aux_dir as well) can be either absolute or relative,
# depending on how configure is run. This is pretty annoying, since
# it makes $ac_aux_dir quite unusable in subdirectories: in the top
# source directory, any form will work fine, but in subdirectories a
# relative path needs to be adjusted first.
#
# $ac_aux_dir/missing
# fails when called from a subdirectory if $ac_aux_dir is relative
# $top_srcdir/$ac_aux_dir/missing
# fails if $ac_aux_dir is absolute,
# fails when called from a subdirectory in a VPATH build with
# a relative $ac_aux_dir
#
# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
# are both prefixed by $srcdir. In an in-source build this is usually
# harmless because $srcdir is '.', but things will broke when you
# start a VPATH build or use an absolute $srcdir.
#
# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
# iff we strip the leading $srcdir from $ac_aux_dir. That would be:
# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
# and then we would define $MISSING as
# MISSING="\${SHELL} $am_aux_dir/missing"
# This will work as long as MISSING is not called from configure, because
# unfortunately $(top_srcdir) has no meaning in configure.
# However there are other variables, like CC, which are often used in
# configure, and could therefore not use this "fixed" $ac_aux_dir.
#
# Another solution, used here, is to always expand $ac_aux_dir to an
# absolute PATH. The drawback is that using absolute paths prevent a
# configured tree to be moved without reconfiguration.
AC_DEFUN([AM_AUX_DIR_EXPAND],
[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
# Expand $ac_aux_dir to an absolute path.
am_aux_dir=`cd "$ac_aux_dir" && pwd`
])
# AM_CONDITIONAL -*- Autoconf -*-
# Copyright (C) 1997-2024 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_CONDITIONAL(NAME, SHELL-CONDITION)
# -------------------------------------
# Define a conditional.
AC_DEFUN([AM_CONDITIONAL],
[AC_PREREQ([2.52])dnl
m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
[$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
AC_SUBST([$1_TRUE])dnl
AC_SUBST([$1_FALSE])dnl
_AM_SUBST_NOTMAKE([$1_TRUE])dnl
_AM_SUBST_NOTMAKE([$1_FALSE])dnl
m4_define([_AM_COND_VALUE_$1], [$2])dnl
if $2; then
$1_TRUE=
$1_FALSE='#'
else
$1_TRUE='#'
$1_FALSE=
fi
AC_CONFIG_COMMANDS_PRE(
[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
AC_MSG_ERROR([[conditional "$1" was never defined.
Usually this means the macro was only invoked conditionally.]])
fi])])
# AM_EXTRA_RECURSIVE_TARGETS -*- Autoconf -*-
# Copyright (C) 2012-2024 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_EXTRA_RECURSIVE_TARGETS
# --------------------------
# Define the list of user recursive targets. This macro exists only to
# be traced by Automake, which will ensure that a proper definition of
# user-defined recursive targets (and associated rules) is propagated
# into all the generated Makefiles.
# TODO: We should really reject non-literal arguments here...
AC_DEFUN([AM_EXTRA_RECURSIVE_TARGETS], [])
# Do all the work for Automake. -*- Autoconf -*-
# Copyright (C) 1996-2024 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This macro actually does too much. Some checks are only needed if
# your package does certain things. But this isn't really a big deal.
dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O.
m4_define([AC_PROG_CC],
m4_defn([AC_PROG_CC])
[_AM_PROG_CC_C_O
])
# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
# AM_INIT_AUTOMAKE([OPTIONS])
# -----------------------------------------------
# The call with PACKAGE and VERSION arguments is the old style
# call (pre autoconf-2.50), which is being phased out. PACKAGE
# and VERSION should now be passed to AC_INIT and removed from
# the call to AM_INIT_AUTOMAKE.
# We support both call styles for the transition. After
# the next Automake release, Autoconf can make the AC_INIT
# arguments mandatory, and then we can depend on a new Autoconf
# release and drop the old call support.
AC_DEFUN([AM_INIT_AUTOMAKE],
[AC_PREREQ([2.65])dnl
m4_ifdef([_$0_ALREADY_INIT],
[m4_fatal([$0 expanded multiple times
]m4_defn([_$0_ALREADY_INIT]))],
[m4_define([_$0_ALREADY_INIT], m4_expansion_stack)])dnl
dnl Autoconf wants to disallow AM_ names. We explicitly allow
dnl the ones we care about.
m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
AC_REQUIRE([AC_PROG_INSTALL])dnl
if test "`cd $srcdir && pwd`" != "`pwd`"; then
# Use -I$(srcdir) only when $(srcdir) != ., so that make's output
# is not polluted with repeated "-I."
AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
# test to see if srcdir already configured
if test -f $srcdir/config.status; then
AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
fi
fi
# test whether we have cygpath
if test -z "$CYGPATH_W"; then
if (cygpath --version) >/dev/null 2>/dev/null; then
CYGPATH_W='cygpath -w'
else
CYGPATH_W=echo
fi
fi
AC_SUBST([CYGPATH_W])
# Define the identity of the package.
dnl Distinguish between old-style and new-style calls.
m4_ifval([$2],
[AC_DIAGNOSE([obsolete],
[$0: two- and three-arguments forms are deprecated.])
m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
AC_SUBST([PACKAGE], [$1])dnl
AC_SUBST([VERSION], [$2])],
[_AM_SET_OPTIONS([$1])dnl
dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
m4_if(
m4_ifset([AC_PACKAGE_NAME], [ok]):m4_ifset([AC_PACKAGE_VERSION], [ok]),
[ok:ok],,
[m4_fatal([AC_INIT should be called with package and version arguments])])dnl
AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
_AM_IF_OPTION([no-define],,
[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package])
AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl
# Some tools Automake needs.
AC_REQUIRE([AM_SANITY_CHECK])dnl
AC_REQUIRE([AC_ARG_PROGRAM])dnl
AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}])
AM_MISSING_PROG([AUTOCONF], [autoconf])
AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}])
AM_MISSING_PROG([AUTOHEADER], [autoheader])
AM_MISSING_PROG([MAKEINFO], [makeinfo])
AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl
AC_REQUIRE([AC_PROG_MKDIR_P])dnl
# For better backward compatibility. To be removed once Automake 1.9.x
# dies out for good. For more background, see:
# <https://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
# <https://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
AC_SUBST([mkdir_p], ['$(MKDIR_P)'])
# We need awk for the "check" target (and possibly the TAP driver). The
# system "awk" is bad on some platforms.
AC_REQUIRE([AC_PROG_AWK])dnl
AC_REQUIRE([AC_PROG_MAKE_SET])dnl
AC_REQUIRE([AM_SET_LEADING_DOT])dnl
_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
[_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
[_AM_PROG_TAR([v7])])])
_AM_IF_OPTION([no-dependencies],,
[AC_PROVIDE_IFELSE([AC_PROG_CC],
[_AM_DEPENDENCIES([CC])],
[m4_define([AC_PROG_CC],
m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl
AC_PROVIDE_IFELSE([AC_PROG_CXX],
[_AM_DEPENDENCIES([CXX])],
[m4_define([AC_PROG_CXX],
m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl
AC_PROVIDE_IFELSE([AC_PROG_OBJC],
[_AM_DEPENDENCIES([OBJC])],
[m4_define([AC_PROG_OBJC],
m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl
AC_PROVIDE_IFELSE([AC_PROG_OBJCXX],
[_AM_DEPENDENCIES([OBJCXX])],
[m4_define([AC_PROG_OBJCXX],
m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl
])
# Variables for tags utilities; see am/tags.am
if test -z "$CTAGS"; then
CTAGS=ctags
fi
AC_SUBST([CTAGS])
if test -z "$ETAGS"; then
ETAGS=etags
fi
AC_SUBST([ETAGS])
if test -z "$CSCOPE"; then
CSCOPE=cscope
fi
AC_SUBST([CSCOPE])
AC_REQUIRE([_AM_SILENT_RULES])dnl
dnl The testsuite driver may need to know about EXEEXT, so add the
dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This
dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below.
AC_CONFIG_COMMANDS_PRE(dnl
[m4_provide_if([_AM_COMPILER_EXEEXT],
[AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl
AC_REQUIRE([_AM_PROG_RM_F])
AC_REQUIRE([_AM_PROG_XARGS_N])
dnl The trailing newline in this macro's definition is deliberate, for
dnl backward compatibility and to allow trailing 'dnl'-style comments
dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841.
])
dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not
dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further
dnl mangled by Autoconf and run in a shell conditional statement.
m4_define([_AC_COMPILER_EXEEXT],
m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])])
# When config.status generates a header, we must update the stamp-h file.
# This file resides in the same directory as the config header
# that is generated. The stamp files are numbered to have different names.
# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
# loop where config.status creates the headers, so we can generate
# our stamp files there.
AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
[# Compute $1's index in $config_headers.
_am_arg=$1
_am_stamp_count=1
for _am_header in $config_headers :; do
case $_am_header in
$_am_arg | $_am_arg:* )
break ;;
* )
_am_stamp_count=`expr $_am_stamp_count + 1` ;;
esac
done
echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
# Copyright (C) 2001-2024 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_PROG_INSTALL_SH
# ------------------
# Define $install_sh.
AC_DEFUN([AM_PROG_INSTALL_SH],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
if test x"${install_sh+set}" != xset; then
case $am_aux_dir in
*\ * | *\ *)
install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
*)
install_sh="\${SHELL} $am_aux_dir/install-sh"
esac
fi
AC_SUBST([install_sh])])
# Copyright (C) 2003-2024 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# Check whether the underlying file-system supports filenames
# with a leading dot. For instance MS-DOS doesn't.
AC_DEFUN([AM_SET_LEADING_DOT],
[rm -rf .tst 2>/dev/null
mkdir .tst 2>/dev/null
if test -d .tst; then
am__leading_dot=.
else
am__leading_dot=_
fi
rmdir .tst 2>/dev/null
AC_SUBST([am__leading_dot])])
# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
# Copyright (C) 1997-2024 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_MISSING_PROG(NAME, PROGRAM)
# ------------------------------
AC_DEFUN([AM_MISSING_PROG],
[AC_REQUIRE([AM_MISSING_HAS_RUN])
$1=${$1-"${am_missing_run}$2"}
AC_SUBST($1)])
# AM_MISSING_HAS_RUN
# ------------------
# Define MISSING if not defined so far and test if it is modern enough.
# If it is, set am_missing_run to use it, otherwise, to nothing.
AC_DEFUN([AM_MISSING_HAS_RUN],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
AC_REQUIRE_AUX_FILE([missing])dnl
if test x"${MISSING+set}" != xset; then
MISSING="\${SHELL} '$am_aux_dir/missing'"
fi
# Use eval to expand $SHELL
if eval "$MISSING --is-lightweight"; then
am_missing_run="$MISSING "
else
am_missing_run=
AC_MSG_WARN(['missing' script is too old or missing])
fi
])
# Helper functions for option handling. -*- Autoconf -*-
# Copyright (C) 2001-2024 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_MANGLE_OPTION(NAME)
# -----------------------
AC_DEFUN([_AM_MANGLE_OPTION],
[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
# _AM_SET_OPTION(NAME)
# --------------------
# Set option NAME. Presently that only means defining a flag for this option.
AC_DEFUN([_AM_SET_OPTION],
[m4_define(_AM_MANGLE_OPTION([$1]), [1])])
# _AM_SET_OPTIONS(OPTIONS)
# ------------------------
# OPTIONS is a space-separated list of Automake options.
AC_DEFUN([_AM_SET_OPTIONS],
[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
# -------------------------------------------
# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
AC_DEFUN([_AM_IF_OPTION],
[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
# Copyright (C) 2022-2024 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_PROG_RM_F
# ---------------
# Check whether 'rm -f' without any arguments works.
# https://bugs.gnu.org/10828
AC_DEFUN([_AM_PROG_RM_F],
[am__rm_f_notfound=
AS_IF([(rm -f && rm -fr && rm -rf) 2>/dev/null], [], [am__rm_f_notfound='""'])
AC_SUBST(am__rm_f_notfound)
])
# Copyright (C) 2001-2024 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_RUN_LOG(COMMAND)
# -------------------
# Run COMMAND, save the exit status in ac_status, and log it.
# (This has been adapted from Autoconf's _AC_RUN_LOG macro.)
AC_DEFUN([AM_RUN_LOG],
[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD
($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
(exit $ac_status); }])
# Check to make sure that the build environment is sane. -*- Autoconf -*-
# Copyright (C) 1996-2024 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_SLEEP_FRACTIONAL_SECONDS
# ----------------------------
AC_DEFUN([_AM_SLEEP_FRACTIONAL_SECONDS], [dnl
AC_CACHE_CHECK([whether sleep supports fractional seconds],
am_cv_sleep_fractional_seconds, [dnl
AS_IF([sleep 0.001 2>/dev/null], [am_cv_sleep_fractional_seconds=yes],
[am_cv_sleep_fractional_seconds=no])
])])
# _AM_FILESYSTEM_TIMESTAMP_RESOLUTION
# -----------------------------------
# Determine the filesystem's resolution for file modification
# timestamps. The coarsest we know of is FAT, with a resolution
# of only two seconds, even with the most recent "exFAT" extensions.
# The finest (e.g. ext4 with large inodes, XFS, ZFS) is one
# nanosecond, matching clock_gettime. However, it is probably not
# possible to delay execution of a shell script for less than one
# millisecond, due to process creation overhead and scheduling
# granularity, so we don't check for anything finer than that. (See below.)
AC_DEFUN([_AM_FILESYSTEM_TIMESTAMP_RESOLUTION], [dnl
AC_REQUIRE([_AM_SLEEP_FRACTIONAL_SECONDS])
AC_CACHE_CHECK([filesystem timestamp resolution],
am_cv_filesystem_timestamp_resolution, [dnl
# Default to the worst case.
am_cv_filesystem_timestamp_resolution=2
# Only try to go finer than 1 sec if sleep can do it.
# Don't try 1 sec, because if 0.01 sec and 0.1 sec don't work,
# - 1 sec is not much of a win compared to 2 sec, and
# - it takes 2 seconds to perform the test whether 1 sec works.
#
# Instead, just use the default 2s on platforms that have 1s resolution,
# accept the extra 1s delay when using $sleep in the Automake tests, in
# exchange for not incurring the 2s delay for running the test for all
# packages.
#
am_try_resolutions=
if test "$am_cv_sleep_fractional_seconds" = yes; then
# Even a millisecond often causes a bunch of false positives,
# so just try a hundredth of a second. The time saved between .001 and
# .01 is not terribly consequential.
am_try_resolutions="0.01 0.1 $am_try_resolutions"
fi
# In order to catch current-generation FAT out, we must *modify* files
# that already exist; the *creation* timestamp is finer. Use names
# that make ls -t sort them differently when they have equal
# timestamps than when they have distinct timestamps, keeping
# in mind that ls -t prints the *newest* file first.
rm -f conftest.ts?
: > conftest.ts1
: > conftest.ts2
: > conftest.ts3
# Make sure ls -t actually works. Do 'set' in a subshell so we don't
# clobber the current shell's arguments. (Outer-level square brackets
# are removed by m4; they're present so that m4 does not expand
# <dollar><star>; be careful, easy to get confused.)
if (
set X `[ls -t conftest.ts[12]]` &&
{
test "$[]*" != "X conftest.ts1 conftest.ts2" ||
test "$[]*" != "X conftest.ts2 conftest.ts1";
}
); then :; else
# If neither matched, then we have a broken ls. This can happen
# if, for instance, CONFIG_SHELL is bash and it inherits a
# broken ls alias from the environment. This has actually
# happened. Such a system could not be considered "sane".
_AS_ECHO_UNQUOTED(
["Bad output from ls -t: \"`[ls -t conftest.ts[12]]`\""],
[AS_MESSAGE_LOG_FD])
AC_MSG_FAILURE([ls -t produces unexpected output.
Make sure there is not a broken ls alias in your environment.])
fi
for am_try_res in $am_try_resolutions; do
# Any one fine-grained sleep might happen to cross the boundary
# between two values of a coarser actual resolution, but if we do
# two fine-grained sleeps in a row, at least one of them will fall
# entirely within a coarse interval.
echo alpha > conftest.ts1
sleep $am_try_res
echo beta > conftest.ts2
sleep $am_try_res
echo gamma > conftest.ts3
# We assume that 'ls -t' will make use of high-resolution
# timestamps if the operating system supports them at all.
if (set X `ls -t conftest.ts?` &&
test "$[]2" = conftest.ts3 &&
test "$[]3" = conftest.ts2 &&
test "$[]4" = conftest.ts1); then
#
# Ok, ls -t worked. If we're at a resolution of 1 second, we're done,
# because we don't need to test make.
make_ok=true
if test $am_try_res != 1; then
# But if we've succeeded so far with a subsecond resolution, we
# have one more thing to check: make. It can happen that
# everything else supports the subsecond mtimes, but make doesn't;
# notably on macOS, which ships make 3.81 from 2006 (the last one
# released under GPLv2). https://bugs.gnu.org/68808
#
# We test $MAKE if it is defined in the environment, else "make".
# It might get overridden later, but our hope is that in practice
# it does not matter: it is the system "make" which is (by far)
# the most likely to be broken, whereas if the user overrides it,
# probably they did so with a better, or at least not worse, make.
# https://lists.gnu.org/archive/html/automake/2024-06/msg00051.html
#
# Create a Makefile (real tab character here):
rm -f conftest.mk
echo 'conftest.ts1: conftest.ts2' >conftest.mk
echo ' touch conftest.ts2' >>conftest.mk
#
# Now, running
# touch conftest.ts1; touch conftest.ts2; make
# should touch ts1 because ts2 is newer. This could happen by luck,
# but most often, it will fail if make's support is insufficient. So
# test for several consecutive successes.
#
# (We reuse conftest.ts[12] because we still want to modify existing
# files, not create new ones, per above.)
n=0
make=${MAKE-make}
until test $n -eq 3; do
echo one > conftest.ts1
sleep $am_try_res
echo two > conftest.ts2 # ts2 should now be newer than ts1
if $make -f conftest.mk | grep 'up to date' >/dev/null; then
make_ok=false
break # out of $n loop
fi
n=`expr $n + 1`
done
fi
#
if $make_ok; then
# Everything we know to check worked out, so call this resolution good.
am_cv_filesystem_timestamp_resolution=$am_try_res
break # out of $am_try_res loop
fi
# Otherwise, we'll go on to check the next resolution.
fi
done
rm -f conftest.ts?
# (end _am_filesystem_timestamp_resolution)
])])
# AM_SANITY_CHECK
# ---------------
AC_DEFUN([AM_SANITY_CHECK],
[AC_REQUIRE([_AM_FILESYSTEM_TIMESTAMP_RESOLUTION])
# This check should not be cached, as it may vary across builds of
# different projects.
AC_MSG_CHECKING([whether build environment is sane])
# Reject unsafe characters in $srcdir or the absolute working directory
# name. Accept space and tab only in the latter.
am_lf='
'
case `pwd` in
*[[\\\"\#\$\&\'\`$am_lf]]*)
AC_MSG_ERROR([unsafe absolute working directory name]);;
esac
case $srcdir in
*[[\\\"\#\$\&\'\`$am_lf\ \ ]]*)
AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);;
esac
# Do 'set' in a subshell so we don't clobber the current shell's
# arguments. Must try -L first in case configure is actually a
# symlink; some systems play weird games with the mod time of symlinks
# (eg FreeBSD returns the mod time of the symlink's containing
# directory).
am_build_env_is_sane=no
am_has_slept=no
rm -f conftest.file
for am_try in 1 2; do
echo "timestamp, slept: $am_has_slept" > conftest.file
if (
set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
if test "$[]*" = "X"; then
# -L didn't work.
set X `ls -t "$srcdir/configure" conftest.file`
fi
test "$[]2" = conftest.file
); then
am_build_env_is_sane=yes
break
fi
# Just in case.
sleep "$am_cv_filesystem_timestamp_resolution"
am_has_slept=yes
done
AC_MSG_RESULT([$am_build_env_is_sane])
if test "$am_build_env_is_sane" = no; then
AC_MSG_ERROR([newly created file is older than distributed files!
Check your system clock])
fi
# If we didn't sleep, we still need to ensure time stamps of config.status and
# generated files are strictly newer.
am_sleep_pid=
AS_IF([test -e conftest.file || grep 'slept: no' conftest.file >/dev/null 2>&1],, [dnl
( sleep "$am_cv_filesystem_timestamp_resolution" ) &
am_sleep_pid=$!
])
AC_CONFIG_COMMANDS_PRE(
[AC_MSG_CHECKING([that generated files are newer than configure])
if test -n "$am_sleep_pid"; then
# Hide warnings about reused PIDs.
wait $am_sleep_pid 2>/dev/null
fi
AC_MSG_RESULT([done])])
rm -f conftest.file
])
# Copyright (C) 2009-2024 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_SILENT_RULES
# ----------------
# Enable less verbose build rules support.
AC_DEFUN([_AM_SILENT_RULES],
[AM_DEFAULT_VERBOSITY=1
AC_ARG_ENABLE([silent-rules], [dnl
AS_HELP_STRING(
[--enable-silent-rules],
[less verbose build output (undo: "make V=1")])
AS_HELP_STRING(
[--disable-silent-rules],
[verbose build output (undo: "make V=0")])dnl
])
dnl
dnl A few 'make' implementations (e.g., NonStop OS and NextStep)
dnl do not support nested variable expansions.
dnl See automake bug#9928 and bug#10237.
am_make=${MAKE-make}
AC_CACHE_CHECK([whether $am_make supports nested variables],
[am_cv_make_support_nested_variables],
[if AS_ECHO([['TRUE=$(BAR$(V))
BAR0=false
BAR1=true
V=1
am__doit:
@$(TRUE)
.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then
am_cv_make_support_nested_variables=yes
else
am_cv_make_support_nested_variables=no
fi])
AC_SUBST([AM_V])dnl
AM_SUBST_NOTMAKE([AM_V])dnl
AC_SUBST([AM_DEFAULT_V])dnl
AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl
AC_SUBST([AM_DEFAULT_VERBOSITY])dnl
AM_BACKSLASH='\'
AC_SUBST([AM_BACKSLASH])dnl
_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
dnl Delay evaluation of AM_DEFAULT_VERBOSITY to the end to allow multiple calls
dnl to AM_SILENT_RULES to change the default value.
AC_CONFIG_COMMANDS_PRE([dnl
case $enable_silent_rules in @%:@ (((
yes) AM_DEFAULT_VERBOSITY=0;;
no) AM_DEFAULT_VERBOSITY=1;;
esac
if test $am_cv_make_support_nested_variables = yes; then
dnl Using '$V' instead of '$(V)' breaks IRIX make.
AM_V='$(V)'
AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
else
AM_V=$AM_DEFAULT_VERBOSITY
AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
fi
])dnl
])
# AM_SILENT_RULES([DEFAULT])
# --------------------------
# Set the default verbosity level to DEFAULT ("yes" being less verbose, "no" or
# empty being verbose).
AC_DEFUN([AM_SILENT_RULES],
[AC_REQUIRE([_AM_SILENT_RULES])
AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1])])
# Copyright (C) 2001-2024 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_PROG_INSTALL_STRIP
# ---------------------
# One issue with vendor 'install' (even GNU) is that you can't
# specify the program used to strip binaries. This is especially
# annoying in cross-compiling environments, where the build's strip
# is unlikely to handle the host's binaries.
# Fortunately install-sh will honor a STRIPPROG variable, so we
# always use install-sh in "make install-strip", and initialize
# STRIPPROG with the value of the STRIP variable (set by the user).
AC_DEFUN([AM_PROG_INSTALL_STRIP],
[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
# Installed binaries are usually stripped using 'strip' when the user
# run "make install-strip". However 'strip' might not be the right
# tool to use in cross-compilation environments, therefore Automake
# will honor the 'STRIP' environment variable to overrule this program.
dnl Don't test for $cross_compiling = yes, because it might be 'maybe'.
if test "$cross_compiling" != no; then
AC_CHECK_TOOL([STRIP], [strip], :)
fi
INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
AC_SUBST([INSTALL_STRIP_PROGRAM])])
# Copyright (C) 2006-2024 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_SUBST_NOTMAKE(VARIABLE)
# ---------------------------
# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in.
# This macro is traced by Automake.
AC_DEFUN([_AM_SUBST_NOTMAKE])
# AM_SUBST_NOTMAKE(VARIABLE)
# --------------------------
# Public sister of _AM_SUBST_NOTMAKE.
AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
# Check how to create a tarball. -*- Autoconf -*-
# Copyright (C) 2004-2024 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_PROG_TAR(FORMAT)
# --------------------
# Check how to create a tarball in format FORMAT.
# FORMAT should be one of 'v7', 'ustar', or 'pax'.
#
# Substitute a variable $(am__tar) that is a command
# writing to stdout a FORMAT-tarball containing the directory
# $tardir.
# tardir=directory && $(am__tar) > result.tar
#
# Substitute a variable $(am__untar) that extract such
# a tarball read from stdin.
# $(am__untar) < result.tar
#
AC_DEFUN([_AM_PROG_TAR],
[# Always define AMTAR for backward compatibility. Yes, it's still used
# in the wild :-( We should find a proper way to deprecate it ...
AC_SUBST([AMTAR], ['$${TAR-tar}'])
# We'll loop over all known methods to create a tar archive until one works.
_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
m4_if([$1], [v7],
[am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'],
[m4_case([$1],
[ustar],
[# The POSIX 1988 'ustar' format is defined with fixed-size fields.
# There is notably a 21 bits limit for the UID and the GID. In fact,
# the 'pax' utility can hang on bigger UID/GID (see automake bug#8343
# and bug#13588).
am_max_uid=2097151 # 2^21 - 1
am_max_gid=$am_max_uid
# The $UID and $GID variables are not portable, so we need to resort
# to the POSIX-mandated id(1) utility. Errors in the 'id' calls
# below are definitely unexpected, so allow the users to see them
# (that is, avoid stderr redirection).
am_uid=`id -u || echo unknown`
am_gid=`id -g || echo unknown`
AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format])
if test x$am_uid = xunknown; then
AC_MSG_WARN([ancient id detected; assuming current UID is ok, but dist-ustar might not work])
elif test $am_uid -le $am_max_uid; then
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
_am_tools=none
fi
AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format])
if test x$gm_gid = xunknown; then
AC_MSG_WARN([ancient id detected; assuming current GID is ok, but dist-ustar might not work])
elif test $am_gid -le $am_max_gid; then
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
_am_tools=none
fi],
[pax],
[],
[m4_fatal([Unknown tar format])])
AC_MSG_CHECKING([how to create a $1 tar archive])
# Go ahead even if we have the value already cached. We do so because we
# need to set the values for the 'am__tar' and 'am__untar' variables.
_am_tools=${am_cv_prog_tar_$1-$_am_tools}
for _am_tool in $_am_tools; do
case $_am_tool in
gnutar)
for _am_tar in tar gnutar gtar; do
AM_RUN_LOG([$_am_tar --version]) && break
done
am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
am__untar="$_am_tar -xf -"
;;
plaintar)
# Must skip GNU tar: if it does not support --format= it doesn't create
# ustar tarball either.
(tar --version) >/dev/null 2>&1 && continue
am__tar='tar chf - "$$tardir"'
am__tar_='tar chf - "$tardir"'
am__untar='tar xf -'
;;
pax)
am__tar='pax -L -x $1 -w "$$tardir"'
am__tar_='pax -L -x $1 -w "$tardir"'
am__untar='pax -r'
;;
cpio)
am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
am__untar='cpio -i -H $1 -d'
;;
none)
am__tar=false
am__tar_=false
am__untar=false
;;
esac
# If the value was cached, stop now. We just wanted to have am__tar
# and am__untar set.
test -n "${am_cv_prog_tar_$1}" && break
# tar/untar a dummy directory, and stop if the command works.
rm -rf conftest.dir
mkdir conftest.dir
echo GrepMe > conftest.dir/file
AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
rm -rf conftest.dir
if test -s conftest.tar; then
AM_RUN_LOG([$am__untar <conftest.tar])
AM_RUN_LOG([cat conftest.dir/file])
grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
fi
done
rm -rf conftest.dir
AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
AC_MSG_RESULT([$am_cv_prog_tar_$1])])
AC_SUBST([am__tar])
AC_SUBST([am__untar])
]) # _AM_PROG_TAR
# Copyright (C) 2022-2024 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_PROG_XARGS_N
# ----------------
# Check whether 'xargs -n' works. It should work everywhere, so the fallback
# is not optimized at all as we never expect to use it.
AC_DEFUN([_AM_PROG_XARGS_N],
[AC_CACHE_CHECK([xargs -n works], am_cv_xargs_n_works, [dnl
AS_IF([test "`echo 1 2 3 | xargs -n2 echo`" = "1 2
3"], [am_cv_xargs_n_works=yes], [am_cv_xargs_n_works=no])])
AS_IF([test "$am_cv_xargs_n_works" = yes], [am__xargs_n='xargs -n'], [dnl
am__xargs_n='am__xargs_n () { shift; sed "s/ /\\n/g" | while read am__xargs_n_arg; do "$@" "$am__xargs_n_arg"; done; }'
])dnl
AC_SUBST(am__xargs_n)
])
+64
View File
@@ -0,0 +1,64 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package accoper
import (
"context"
"fmt"
"mbase/pkg/auxpwd"
"mbase/pkg/auxtool"
"mbase/pkg/auxuuid"
"mbase/pkg/descr"
)
type CreateAccountParams struct {
Username string `json:"username"`
Password string `json:"password"`
}
type CreateAccountResult struct {
AccountID string `json:"accountId"`
}
func (oper *Operator) CreateAccount(ctx context.Context, operatorID string, params *CreateAccountParams) (*CreateAccountResult, error) {
var err error
res := &CreateAccountResult{}
if params.Username == "" {
err := fmt.Errorf("Empty username parameters")
return res, err
}
if params.Password == "" {
err := fmt.Errorf("Empty password parameter")
return res, err
}
accountExists, _, err := oper.mdb.GetAccountByUsername(ctx, params.Username)
if err != nil {
return res, err
}
if accountExists {
err := fmt.Errorf("Account with thist name already exists")
return res, err
}
now := auxtool.TimeNow()
passhash := auxpwd.MakeSHA256Hash([]byte(params.Password))
accountDescr := &descr.Account{
ID: auxuuid.NewUUID(),
Username: params.Username,
Passhash: passhash,
Disabled: false,
CreatedAt: now,
UpdatedAt: now,
CreatedBy: operatorID,
UpdatedBy: operatorID,
}
err = oper.mdb.InsertAccount(ctx, accountDescr)
if err != nil {
return res, err
}
res.AccountID = accountDescr.ID
return res, err
}
+97
View File
@@ -0,0 +1,97 @@
package accoper
import (
"context"
"fmt"
"regexp"
"mbase/pkg/auxtool"
"mbase/pkg/auxuuid"
"mbase/pkg/descr"
)
// CreateGrant
type CreateGrantParams struct {
AccountID string `json:"accountID"`
Username string `json:"username"`
Right string `json:"operation"`
Pattern string `json:"pattern"`
}
type CreateGrantResult struct {
GrantID string `json:"grantId"`
}
func (oper *Operator) CreateGrant(ctx context.Context, operatorID string, params *CreateGrantParams) (*CreateGrantResult, error) {
var err error
res := &CreateGrantResult{}
if params.AccountID == "" {
err := fmt.Errorf("Empty accountId parameters")
return res, err
}
if params.Right == "" {
err := fmt.Errorf("Empty operation parameter")
return res, err
}
if params.Pattern == "" {
err := fmt.Errorf("Empty pattern parameter")
return res, err
}
_, err = regexp.Compile(params.Pattern)
if err != nil {
err := fmt.Errorf("Cannot compile regexp %s: %v", err)
return res, err
}
var accountDescr *descr.Account
var accountExists bool
switch {
case params.AccountID != "":
accountExists, accountDescr, err = oper.mdb.GetAccountByID(ctx, params.AccountID)
if err != nil {
return res, err
}
if !accountExists {
err := fmt.Errorf("Account with ID %s dont exists", params.AccountID)
return res, err
}
case params.Username != "":
accountExists, accountDescr, err = oper.mdb.GetAccountByUsername(ctx, params.Username)
if err != nil {
return res, err
}
if !accountExists {
err := fmt.Errorf("Account with name %s dont exists", params.Username)
return res, err
}
default:
err := fmt.Errorf("Empty username and accountId parameter")
return res, err
}
grantExists, _, err := oper.mdb.GetGrantByAccoundIDRightPattern(ctx, params.AccountID, params.Right, params.Pattern)
if err != nil {
return res, err
}
if grantExists {
err := fmt.Errorf("Grant with this right already exists")
return res, err
}
now := auxtool.TimeNow()
grantDescr := &descr.Grant{
ID: auxuuid.NewUUID(),
AccountID: accountDescr.ID,
Right: params.Right,
Pattern: params.Pattern,
CreatedAt: now,
UpdatedAt: now,
CreatedBy: operatorID,
UpdatedBy: operatorID,
}
err = oper.mdb.InsertGrant(ctx, grantDescr)
if err != nil {
return res, err
}
res.GrantID = grantDescr.ID
return res, err
}
+66
View File
@@ -0,0 +1,66 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package accoper
import (
"context"
"fmt"
"mbase/pkg/descr"
)
type DeleteAccountParams struct {
Username string `json:"username"`
AccountID string `json:"accountId"`
}
type DeleteAccountResult struct{}
func (oper *Operator) DeleteAccount(ctx context.Context, operatorID string, params *DeleteAccountParams) (*DeleteAccountResult, error) {
var err error
res := &DeleteAccountResult{}
if params.Username == "" && params.AccountID == "" {
err := fmt.Errorf("Empty username and accountId parameter")
return res, err
}
var accountDescr *descr.Account
var accountExists bool
switch {
case params.AccountID != "":
accountExists, accountDescr, err = oper.mdb.GetAccountByID(ctx, params.AccountID)
if err != nil {
return res, err
}
if !accountExists {
err := fmt.Errorf("Account with ID %s dont exists", params.AccountID)
return res, err
}
case params.Username != "":
accountExists, accountDescr, err = oper.mdb.GetAccountByUsername(ctx, params.Username)
if err != nil {
return res, err
}
if !accountExists {
err := fmt.Errorf("Account with name %s dont exists", params.Username)
return res, err
}
default:
err := fmt.Errorf("Empty username and accountId parameter")
return res, err
}
if accountDescr == nil {
err := fmt.Errorf("Null account desriptor")
return res, err
}
err = oper.mdb.DeleteAllGrantsForAccountID(ctx, accountDescr.ID)
if err != nil {
return res, err
}
err = oper.mdb.DeleteAccountByID(ctx, accountDescr.ID)
if err != nil {
return res, err
}
return res, err
}
+41
View File
@@ -0,0 +1,41 @@
package accoper
import (
"context"
"fmt"
"mbase/pkg/descr"
)
// DeleteGrant
type DeleteGrantParams struct {
GrantID string `json:"grantId"`
}
type DeleteGrantResult struct{}
func (oper *Operator) DeleteGrant(ctx context.Context, operatorID string, params *DeleteGrantParams) (*DeleteGrantResult, error) {
var err error
res := &DeleteGrantResult{}
if params.GrantID == "" {
err := fmt.Errorf("Empty grantId parameter")
return res, err
}
var grantDescr *descr.Grant
var grantExists bool
grantExists, grantDescr, err = oper.mdb.GetGrantByID(ctx, params.GrantID)
if err != nil {
return res, err
}
if !grantExists {
err := fmt.Errorf("Grant with ID %s dont exists", params.GrantID)
return res, err
}
err = oper.mdb.DeleteGrantByID(ctx, grantDescr.ID)
if err != nil {
return res, err
}
return res, err
}
+78
View File
@@ -0,0 +1,78 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package accoper
import (
"context"
"fmt"
"mbase/pkg/descr"
)
// GetAccount
type GetAccountParams struct {
Username string `json:"username"`
AccountID string `json:"accountId"`
}
type GetAccountResult struct {
Account *descr.AccountShort `json:"account"`
}
func (oper *Operator) GetAccount(ctx context.Context, operatorID string, params *GetAccountParams) (*GetAccountResult, error) {
var err error
res := &GetAccountResult{}
if params.Username == "" && params.AccountID == "" {
err := fmt.Errorf("Empty username and accountId parameter")
return res, err
}
var accountDescr *descr.Account
var accountExists bool
switch {
case params.AccountID != "":
accountExists, accountDescr, err = oper.mdb.GetAccountByID(ctx, params.AccountID)
if err != nil {
return res, err
}
if !accountExists {
err := fmt.Errorf("Account with ID %s dont exists", params.AccountID)
return res, err
}
case params.Username != "":
accountExists, accountDescr, err = oper.mdb.GetAccountByUsername(ctx, params.Username)
if err != nil {
return res, err
}
if !accountExists {
err := fmt.Errorf("Account with name %s dont exists", params.Username)
return res, err
}
default:
err := fmt.Errorf("Empty username and accountId parameter")
return res, err
}
if accountDescr == nil {
err := fmt.Errorf("Null account desriptor")
return res, err
}
accountShort := &descr.AccountShort{
ID: accountDescr.ID,
Username: accountDescr.Username,
CreatedAt: accountDescr.CreatedAt,
UpdatedAt: accountDescr.UpdatedAt,
CreatedBy: accountDescr.CreatedBy,
UpdatedBy: accountDescr.UpdatedBy,
Disabled: accountDescr.Disabled,
Grants: make([]descr.Grant, 0),
}
grantDescrs, err := oper.mdb.ListGrantsByAccountID(ctx, accountDescr.ID)
if err != nil {
return res, err
}
accountShort.Grants = grantDescrs
res.Account = accountShort
return res, err
}
+40
View File
@@ -0,0 +1,40 @@
package accoper
import (
"context"
"fmt"
"mbase/pkg/descr"
)
// Get Grants
type GetGrantParams struct {
GrantID string `json:"grantId"`
}
type GetGrantResult struct {
Grant *descr.Grant `json:"grant"`
}
func (oper *Operator) GetGrant(ctx context.Context, operatorID string, params *GetGrantParams) (*GetGrantResult, error) {
var err error
res := &GetGrantResult{}
if params.GrantID == "" {
err := fmt.Errorf("Empty grantId parameter")
return res, err
}
var grantDescr *descr.Grant
var grantExists bool
grantExists, grantDescr, err = oper.mdb.GetGrantByID(ctx, params.GrantID)
if err != nil {
return res, err
}
if !grantExists {
err := fmt.Errorf("Grant with ID %s dont exists", params.GrantID)
return res, err
}
res.Grant = grantDescr
return res, err
}
+44
View File
@@ -0,0 +1,44 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package accoper
import (
"context"
"mbase/pkg/descr"
)
type ListAccountsParams struct{}
type ListAccountsResult struct {
Accounts []descr.AccountShort `json:"accounts"`
}
func (oper *Operator) ListAccounts(ctx context.Context, params *ListAccountsParams) (*ListAccountsResult, error) {
var err error
res := &ListAccountsResult{}
accountDescrs, err := oper.mdb.ReducedListAccounts(ctx)
if err != nil {
return res, err
}
for _, accountDescr := range accountDescrs {
accountShort := descr.AccountShort{
ID: accountDescr.ID,
Username: accountDescr.Username,
Disabled: accountDescr.Disabled,
CreatedAt: accountDescr.CreatedAt,
UpdatedAt: accountDescr.UpdatedAt,
CreatedBy: accountDescr.CreatedBy,
UpdatedBy: accountDescr.UpdatedBy,
Grants: make([]descr.Grant, 0),
}
grantDescrs, err := oper.mdb.ListGrantsByAccountID(ctx, accountDescr.ID)
if err != nil {
return res, err
}
accountShort.Grants = grantDescrs
res.Accounts = append(res.Accounts, accountShort)
}
return res, err
}
+56
View File
@@ -0,0 +1,56 @@
package accoper
import (
"context"
"fmt"
"mbase/pkg/descr"
)
// ListGrants
type ListGrantsParams struct {
Username string
AccountID string
}
type ListGrantsResult struct {
Grants []descr.Grant `json:"grants"`
}
func (oper *Operator) ListGrants(ctx context.Context, operatorID string, params *ListGrantsParams) (*ListGrantsResult, error) {
var err error
res := &ListGrantsResult{
Grants: make([]descr.Grant, 0),
}
var accountDescr *descr.Account
var accountExists bool
switch {
case params.AccountID != "":
accountExists, accountDescr, err = oper.mdb.GetAccountByID(ctx, params.AccountID)
if err != nil {
return res, err
}
if !accountExists {
err := fmt.Errorf("Account with ID %s dont exists", params.AccountID)
return res, err
}
case params.Username != "":
accountExists, accountDescr, err = oper.mdb.GetAccountByUsername(ctx, params.Username)
if err != nil {
return res, err
}
if !accountExists {
err := fmt.Errorf("Account with name %s dont exists", params.Username)
return res, err
}
default:
err := fmt.Errorf("Empty username and accountId parameter")
return res, err
}
accountID := accountDescr.ID
grantDescrs, err := oper.mdb.ListGrantsByAccountID(ctx, accountID)
if err != nil {
return res, err
}
res.Grants = grantDescrs
return res, err
}
+27
View File
@@ -0,0 +1,27 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package accoper
import (
"mbase/app/logger"
"mbase/app/maindb"
)
type OperatorParams struct {
MainDB *maindb.Database
}
type Operator struct {
mdb *maindb.Database
logg *logger.Logger
}
func NewOperator(params *OperatorParams) (*Operator, error) {
var err error
oper := &Operator{
mdb: params.MainDB,
}
oper.logg = logger.NewLoggerWithSubject("imageoper")
return oper, err
}
+80
View File
@@ -0,0 +1,80 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package accoper
import (
"context"
"fmt"
"mbase/pkg/auxpwd"
"mbase/pkg/auxtool"
"mbase/pkg/descr"
)
type UpdateAccountParams struct {
Username string `json:"username"`
AccountID string `json:"accountId"`
NewUsername string `json:"newUsername"`
NewPassword string `json:"newPassword"`
Disabled bool `json:"disabled"`
}
type UpdateAccountResult struct{}
func (oper *Operator) UpdateAccount(ctx context.Context, operatorID string, params *UpdateAccountParams) (*UpdateAccountResult, error) {
var err error
res := &UpdateAccountResult{}
if params.Username == "" && params.AccountID == "" {
err := fmt.Errorf("Empty username and accountId parameter")
return res, err
}
var accountDescr *descr.Account
var accountExists bool
switch {
case params.AccountID != "":
accountExists, accountDescr, err = oper.mdb.GetAccountByID(ctx, params.AccountID)
if err != nil {
return res, err
}
if !accountExists {
err := fmt.Errorf("Account with ID %s dont exists", params.AccountID)
return res, err
}
case params.Username != "":
accountExists, accountDescr, err = oper.mdb.GetAccountByUsername(ctx, params.Username)
if err != nil {
return res, err
}
if !accountExists {
err := fmt.Errorf("Account with name %s dont exists", params.Username)
return res, err
}
default:
err := fmt.Errorf("Empty username and accountId parameter")
return res, err
}
if accountDescr == nil {
err := fmt.Errorf("Null account desriptor")
return res, err
}
now := auxtool.TimeNow()
if params.NewUsername != "" {
accountDescr.UpdatedAt = now
accountDescr.Username = params.NewUsername
}
if params.NewPassword != "" {
accountDescr.UpdatedAt = now
passhash := auxpwd.MakeSHA256Hash([]byte(params.NewPassword))
accountDescr.Passhash = passhash
}
if params.Disabled != accountDescr.Disabled {
accountDescr.UpdatedAt = now
accountDescr.Disabled = params.Disabled
}
err = oper.mdb.UpdateAccountByID(ctx, accountDescr.ID, accountDescr)
if err != nil {
return res, err
}
return res, err
}
+52
View File
@@ -0,0 +1,52 @@
package accoper
import (
"context"
"fmt"
"mbase/pkg/auxtool"
"mbase/pkg/descr"
)
// UpdateGrant
type UpdateGrantParams struct {
GrantID string
NewPattern string
}
type UpdateGrantResult struct{}
func (oper *Operator) UpdateGrant(ctx context.Context, operatorID string, params *UpdateGrantParams) (*UpdateGrantResult, error) {
var err error
res := &UpdateGrantResult{}
if params.NewPattern == "" {
err := fmt.Errorf("Empty newPattern parameter")
return res, err
}
if params.GrantID == "" {
err := fmt.Errorf("Empty grantId parameter")
return res, err
}
var grantDescr *descr.Grant
var grantExists bool
grantExists, grantDescr, err = oper.mdb.GetGrantByID(ctx, params.GrantID)
if err != nil {
return res, err
}
if !grantExists {
err := fmt.Errorf("Grant with ID %s dont exists", params.GrantID)
return res, err
}
now := auxtool.TimeNow()
if params.NewPattern != "" {
grantDescr.UpdatedAt = now
grantDescr.UpdatedBy = operatorID
grantDescr.Pattern = params.NewPattern
}
err = oper.mdb.UpdateGrantByID(ctx, grantDescr.ID, grantDescr)
if err != nil {
return res, err
}
return res, err
}
+159
View File
@@ -0,0 +1,159 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package config
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"mbase/pkg/auxx509"
yaml "go.yaml.in/yaml/v4"
)
type Service struct {
Address string `json:"address" yaml:"address"`
Port uint32 `json:"port" yaml:"port"`
}
type Database struct {
Basepath string `json:"basepath" yaml:"basepath"`
}
type Storage struct {
Basepath string `json:"basepath" yaml:"basepath"`
}
type Config struct {
Service Service `json:"service" yaml:"service"`
Database Database `json:"database" yaml:"database"`
Storage Storage `json:"storage" yaml:"storage"`
AsDaemon bool `json:"asDaemon" yaml:"asDaemon"`
Logpath string `json:"logpath" yaml:"logpath"`
Runpath string `json:"runpath" yaml:"runpath"`
Version string `json:"version" yaml:"version"`
Certpath string `json:"certpath,omitempty" yaml:"certpath,omitempty"`
Keypath string `json:"keypath,omitempty" yaml:"keypath,omitempty"`
X509Cert string `json:"-" yaml:"-"`
X509Key string `json:"-" yaml:"-"`
Datadir string `json:"datadir" yaml:datadir`
Hostname string `json:"hostname" yaml:hostname`
Hostnames []string `json:"hostnames" yaml:hostnames`
LogLimit int64 `json:"logLimit" yaml:logLimit`
RunUser string `json:"runUser" yaml:runUser`
}
func NewConfig() *Config {
logfile := fmt.Sprintf("%s.log", srvname)
logpath := filepath.Join(logdir, logfile)
runfile := fmt.Sprintf("%s.pid", srvname)
runpath := filepath.Join(rundir, runfile)
//certpath := fmt.Sprintf("%s.crt", srvname)
//certpath = filepath.Join(confdir, certpath)
//keypath := fmt.Sprintf("%s.crt", srvname)
//keypath = filepath.Join(confdir, keypath)
return &Config{
Service: Service{
Address: "0.0.0.0",
Port: 1025,
},
Database: Database{
Basepath: datadir,
},
Storage: Storage{
Basepath: datadir,
},
AsDaemon: false,
Logpath: logpath,
Runpath: runpath,
Version: version,
Datadir: datadir,
//Certpath: certpath,
//Keypath: keypath,
Hostnames: make([]string, 0),
LogLimit: 1024 * 1024 * 10, // 10 Mb
RunUser: "daemon",
}
}
func (conf *Config) String() string {
confbytes, _ := yaml.Marshal(conf)
return string(confbytes)
}
func (conf *Config) ReadConfigfile() error {
conffile := fmt.Sprintf("%s.yaml", srvname)
confpath := filepath.Join(confdir, conffile)
confdata, err := ioutil.ReadFile(confpath)
if err != nil {
return err
}
err = yaml.Unmarshal(confdata, conf)
if err != nil {
return err
}
return err
}
func (conf *Config) ReadX509Cert() error {
var err error
if conf.Certpath != "" && conf.Keypath != "" {
if !filepath.IsAbs(conf.Certpath) {
conf.Certpath = filepath.Join(confdir, conf.Certpath)
}
certBytes, err := os.ReadFile(conf.Certpath)
if err != nil {
return err
}
if !filepath.IsAbs(conf.Keypath) {
conf.Keypath = filepath.Join(confdir, conf.Keypath)
}
keyBytes, err := os.ReadFile(conf.Keypath)
if err != nil {
return err
}
conf.X509Cert = string(certBytes)
conf.X509Key = string(keyBytes)
return err
}
/*
if conf.X509Cert != "" && conf.X509Key != "" {
x509Cert, err := base64.StdEncoding.DecodeString(conf.X509Cert)
if err != nil {
return err
}
conf.X509Cert = string(x509Cert)
x509Key, err := base64.StdEncoding.DecodeString(conf.X509Key)
if err != nil {
return err
}
conf.X509Key = string(x509Key)
}
*/
if conf.X509Cert == "" || conf.X509Key == "" {
if conf.Hostname == "" {
conf.Hostname, err = os.Hostname()
if err != nil {
return err
}
}
certBytes, keyBytes, err := auxx509.CreateSelfSignedCert(conf.Hostname, conf.Hostnames...)
if err != nil {
return err
}
conf.X509Cert = string(certBytes)
conf.X509Key = string(keyBytes)
return err
}
return err
}
+10
View File
@@ -0,0 +1,10 @@
package config
const (
confdir = "@srv_confdir@"
rundir = "@srv_rundir@"
logdir = "@srv_logdir@"
datadir = "@srv_datadir@"
version = "@PACKAGE_VERSION@"
srvname = "@PACKAGE_NAME@d"
)
+103
View File
@@ -0,0 +1,103 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package handler
import (
"context"
"fmt"
"mbase/app/router"
"mbase/pkg/auxhttp"
"mbase/pkg/auxpwd"
"mbase/pkg/terms"
)
const (
authTag = "authpass"
userTag = "accountID"
)
func (hand *Handler) AuthMiddleware(next router.Handler) router.Handler {
var handlerFunc router.HandlerFunc
handlerFunc = func(rctx *router.Context) {
success, accountID, err := hand.CheckAccess(rctx)
if success {
rctx.SetBool(authTag, true)
rctx.SetString(userTag, string(accountID))
}
if err != nil {
hand.logg.Errorf("Authorization middleware error: %v", err)
}
next.ServeHTTP(rctx)
}
return handlerFunc
}
// Authentification
func (hand *Handler) CheckAccess(rctx *router.Context) (bool, string, error) {
var err error
var success bool
var username string
var password string
var accountID string
accountID = terms.AnonymousID
//hand.logg.Debugf("URL: %s", rctx.URL().String())
authHeader := rctx.GetHeader("Authorization")
hand.logg.Debugf("Authorization: [%s]", authHeader)
if authHeader != "" {
username, password, err = auxhttp.ParseBasicAuth(authHeader)
if err != nil {
return success, accountID, err
}
if username == "" || password == "" {
goto anonymous
}
success, id, err := hand.ValidatePassword(rctx.Ctx, username, password)
if err != nil {
return false, accountID, err
}
if !success {
err = fmt.Errorf("Incorrect username or password")
return false, accountID, err
}
accountID = id
return success, accountID, err
}
anonymous:
success = true
accountID = terms.AnonymousID
return success, accountID, err
}
func (hand *Handler) ValidatePassword(ctx context.Context, username, password string) (bool, string, error) {
var err error
var accountID string
valid := false
accountExists, accountDescr, err := hand.mdb.GetAccountByUsername(ctx, username)
if !accountExists {
err := fmt.Errorf("Account not exists")
return valid, accountID, err
}
if !auxpwd.PasswordMatch([]byte(password), accountDescr.Passhash) {
err := fmt.Errorf("Login data mismatch")
return valid, accountID, err
}
valid = true
accountID = accountDescr.ID
return valid, accountID, err
}
// Authorization
func (hand *Handler) CheckRight(ctx context.Context, accountID, reqRight, subject string) (bool, error) {
var err error
var res bool
res = true
return res, err
}
+177
View File
@@ -0,0 +1,177 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package handler
import (
"fmt"
"mbase/app/accoper"
"mbase/app/router"
"mbase/pkg/terms"
)
// POST /v3/account/create 200 200
func (hand *Handler) CreateAccount(rctx *router.Context) {
var err error
params := &accoper.CreateAccountParams{}
err = rctx.BindJSON(params)
if err != nil {
hand.SendError(rctx, err)
return
}
// Rigth checking
operatorID, _ := rctx.GetString(userTag)
opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightWriteAccounts, "")
if err != nil {
err := fmt.Errorf("Operation error: %v", err)
hand.SendError(rctx, err)
return
}
if !opEnable {
err := fmt.Errorf("Operation not enabled for this account")
hand.SendError(rctx, err)
return
}
// Execution of the operation
res, err := hand.acop.CreateAccount(rctx.Ctx, operatorID, params)
if err != nil {
hand.logg.Errorf("Operation error: %v", err)
hand.SendError(rctx, err)
return
}
hand.SendResult(rctx, res)
}
// POST /v3/account/get 200 200
func (hand *Handler) GetAccount(rctx *router.Context) {
var err error
params := &accoper.GetAccountParams{}
err = rctx.BindJSON(params)
if err != nil {
hand.SendError(rctx, err)
return
}
// Rigth checking
operatorID, _ := rctx.GetString(userTag)
opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightWriteAccounts, "")
if err != nil {
err := fmt.Errorf("Operation error: %v", err)
hand.SendError(rctx, err)
return
}
if !opEnable {
err := fmt.Errorf("Operation not enabled for this account")
hand.SendError(rctx, err)
return
}
// Execution of the operation
res, err := hand.acop.GetAccount(rctx.Ctx, operatorID, params)
if err != nil {
hand.logg.Errorf("Operation error: %v", err)
hand.SendError(rctx, err)
return
}
hand.SendResult(rctx, res)
}
// POST /v3/accounts/list 200 200
func (hand *Handler) ListAccounts(rctx *router.Context) {
var err error
params := &accoper.ListAccountsParams{}
err = rctx.BindJSON(params)
if err != nil {
hand.SendError(rctx, err)
return
}
// Rigth checking
operatorID, _ := rctx.GetString(userTag)
opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightWriteAccounts, "")
if err != nil {
err := fmt.Errorf("Operation error: %v", err)
hand.SendError(rctx, err)
return
}
if !opEnable {
err := fmt.Errorf("Operation not enabled for this account")
hand.SendError(rctx, err)
return
}
// Execution of the operation
res, err := hand.acop.ListAccounts(rctx.Ctx, params)
if err != nil {
hand.logg.Errorf("ListAccounts error: %v", err)
hand.SendError(rctx, err)
return
}
hand.SendResult(rctx, res)
}
// POST /v3/account/get 200 200
func (hand *Handler) UpdateAccount(rctx *router.Context) {
var err error
params := &accoper.UpdateAccountParams{}
err = rctx.BindJSON(params)
if err != nil {
hand.SendError(rctx, err)
return
}
// Rigth checking
operatorID, _ := rctx.GetString(userTag)
opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightWriteAccounts, "")
if err != nil {
err := fmt.Errorf("Operation error: %v", err)
hand.SendError(rctx, err)
return
}
if !opEnable {
err := fmt.Errorf("Operation not enabled for this account")
hand.SendError(rctx, err)
return
}
// Execution of the operation
res, err := hand.acop.UpdateAccount(rctx.Ctx, operatorID, params)
if err != nil {
hand.logg.Errorf("UpdateAccount error: %v", err)
hand.SendError(rctx, err)
return
}
hand.SendResult(rctx, res)
}
// POST /v3/account/delete 200 200
func (hand *Handler) DeleteAccount(rctx *router.Context) {
var err error
params := &accoper.DeleteAccountParams{}
err = rctx.BindJSON(params)
if err != nil {
hand.SendError(rctx, err)
return
}
// Rigth checking
operatorID, _ := rctx.GetString(userTag)
opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightWriteAccounts, params.Username)
if err != nil {
err := fmt.Errorf("Operation error: %v", err)
hand.SendError(rctx, err)
return
}
if !opEnable {
err := fmt.Errorf("Operation not enabled for this account")
hand.SendError(rctx, err)
return
}
// Execution of the operation
res, err := hand.acop.DeleteAccount(rctx.Ctx, operatorID, params)
if err != nil {
hand.logg.Errorf("DeleteAccount error: %v", err)
hand.SendError(rctx, err)
return
}
hand.SendResult(rctx, res)
}
+177
View File
@@ -0,0 +1,177 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package handler
import (
"fmt"
"mbase/app/accoper"
"mbase/app/router"
"mbase/pkg/terms"
)
// POST /v3/grant/create 200 200
func (hand *Handler) CreateGrant(rctx *router.Context) {
var err error
params := &accoper.CreateGrantParams{}
err = rctx.BindJSON(params)
if err != nil {
hand.SendError(rctx, err)
return
}
// Rigth checking
operatorID, _ := rctx.GetString(userTag)
opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightWriteAccounts, "")
if err != nil {
err := fmt.Errorf("Operation error: %v", err)
hand.SendError(rctx, err)
return
}
if !opEnable {
err := fmt.Errorf("Operation not enabled for this account")
hand.SendError(rctx, err)
return
}
// Execution of the operation
res, err := hand.acop.CreateGrant(rctx.Ctx, operatorID, params)
if err != nil {
hand.logg.Errorf("CreateGrant error: %v", err)
hand.SendError(rctx, err)
return
}
hand.SendResult(rctx, res)
}
// POST /v3/grant/get 200 200
func (hand *Handler) GetGrant(rctx *router.Context) {
var err error
params := &accoper.GetGrantParams{}
err = rctx.BindJSON(params)
if err != nil {
hand.SendError(rctx, err)
return
}
// Rigth checking
operatorID, _ := rctx.GetString(userTag)
opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightReadAccounts, "")
if err != nil {
err := fmt.Errorf("Operation error: %v", err)
hand.SendError(rctx, err)
return
}
if !opEnable {
err := fmt.Errorf("Operation not enabled for this account")
hand.SendError(rctx, err)
return
}
// Execution of the operation
res, err := hand.acop.GetGrant(rctx.Ctx, operatorID, params)
if err != nil {
hand.logg.Errorf("CreateGrant error: %v", err)
hand.SendError(rctx, err)
return
}
hand.SendResult(rctx, res)
}
// POST /v3/grants/list 200 200
func (hand *Handler) ListGrants(rctx *router.Context) {
var err error
params := &accoper.ListGrantsParams{}
err = rctx.BindJSON(params)
if err != nil {
hand.SendError(rctx, err)
return
}
// Rigth checking
operatorID, _ := rctx.GetString(userTag)
opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightReadAccounts, "")
if err != nil {
err := fmt.Errorf("Operation error: %v", err)
hand.SendError(rctx, err)
return
}
if !opEnable {
err := fmt.Errorf("Operation not enabled for this account")
hand.SendError(rctx, err)
return
}
// Execution of the operation
res, err := hand.acop.ListGrants(rctx.Ctx, operatorID, params)
if err != nil {
hand.logg.Errorf("ListGrants error: %v", err)
hand.SendError(rctx, err)
return
}
hand.SendResult(rctx, res)
}
// POST /v3/grant/get 200 200
func (hand *Handler) UpdateGrant(rctx *router.Context) {
var err error
params := &accoper.UpdateGrantParams{}
err = rctx.BindJSON(params)
if err != nil {
hand.SendError(rctx, err)
return
}
// Rigth checking
operatorID, _ := rctx.GetString(userTag)
opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightWriteAccounts, "")
if err != nil {
err := fmt.Errorf("Operation error: %v", err)
hand.SendError(rctx, err)
return
}
if !opEnable {
err := fmt.Errorf("Operation not enabled for this account")
hand.SendError(rctx, err)
return
}
// Execution of the operation
res, err := hand.acop.UpdateGrant(rctx.Ctx, operatorID, params)
if err != nil {
hand.logg.Errorf("UpdateGrant error: %v", err)
hand.SendError(rctx, err)
return
}
hand.SendResult(rctx, res)
}
// POST /v3/grant/delete 200 200
func (hand *Handler) DeleteGrant(rctx *router.Context) {
var err error
params := &accoper.DeleteGrantParams{}
err = rctx.BindJSON(params)
if err != nil {
hand.SendError(rctx, err)
return
}
// Rigth checking
operatorID, _ := rctx.GetString(userTag)
opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightWriteAccounts, "")
if err != nil {
err := fmt.Errorf("Operation error: %v", err)
hand.SendError(rctx, err)
return
}
if !opEnable {
err := fmt.Errorf("Operation not enabled for this account")
hand.SendError(rctx, err)
return
}
// Execution of the operation
res, err := hand.acop.DeleteGrant(rctx.Ctx, operatorID, params)
if err != nil {
hand.logg.Errorf("DeleteGrant error: %v", err)
hand.SendError(rctx, err)
return
}
hand.SendResult(rctx, res)
}
+45
View File
@@ -0,0 +1,45 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package handler
import (
"mbase/app/logger"
"mbase/app/maindb"
"mbase/app/router"
"mbase/app/accoper"
"mbase/app/servoper"
yaml "go.yaml.in/yaml/v4"
)
type HandlerParams struct {
MainDB *maindb.Database
AccOper *accoper.Operator
ServOper *servoper.Operator
}
type Handler struct {
mdb *maindb.Database
logg *logger.Logger
acop *accoper.Operator
seop *servoper.Operator
}
func NewHandler(params *HandlerParams) (*Handler, error) {
var err error
hand := &Handler{
mdb: params.MainDB,
acop: params.AccOper,
seop: params.ServOper,
}
hand.logg = logger.NewLoggerWithSubject("handler")
return hand, err
}
func (hand *Handler) DumpHeaders(label string, rctx *router.Context) {
headers := rctx.GetHeaders()
yamlData, _ := yaml.Marshal(headers)
hand.logg.Debugf("%s:\n%s\n", label, string(yamlData))
}
+15
View File
@@ -0,0 +1,15 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package handler
import (
"net/http"
"mbase/app/router"
)
func (hand *Handler) NotFound(rctx *router.Context) {
hand.logg.Warningf("Route for [%s %s] not found", rctx.Request.Method, rctx.Request.URL.String())
rctx.SetStatus(http.StatusNotFound)
}
+36
View File
@@ -0,0 +1,36 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package handler
import (
"net/http"
"mbase/app/router"
)
type Response[T any] struct {
Error bool `json:"error" yaml:"error"`
Message string `json:"message,omitempty" yaml:"message,omitempty"`
Result T `json:"result,omitempty" yaml:"result,result"`
}
func NewResponse[T any]() *Response[T] {
return &Response[T]{}
}
func (hand *Handler) SendResult(rctx *router.Context, result any) {
response := &Response[any]{
Error: false,
Result: result,
}
rctx.SendJSON(http.StatusOK, response)
}
func (hand *Handler) SendError(rctx *router.Context, err error) {
response := &Response[any]{
Error: true,
Message: err.Error(),
}
rctx.SendJSON(http.StatusOK, response)
}
+15
View File
@@ -0,0 +1,15 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package handler
import (
"mbase/app/servoper"
"mbase/app/router"
)
func (hand *Handler) SendHello(rctx *router.Context) {
params := &servoper.SendHelloParams{}
res, _ := hand.seop.SendHello(params)
hand.SendResult(rctx, res)
}
+66
View File
@@ -0,0 +1,66 @@
package locker
import (
"sync"
)
type Elem struct {
Pipe chan bool
Usage int
}
func NewElem() *Elem {
return &Elem{
Pipe: make(chan bool, 1),
}
}
type Locker struct {
mtx sync.Mutex
lMap map[string]*Elem
}
func NewLocker() *Locker {
lock := &Locker{
lMap: make(map[string]*Elem),
}
return lock
}
func (lock *Locker) WaitAndLock(name string) {
lock.mtx.Lock()
p, exist := lock.lMap[name]
if !exist {
p = NewElem()
lock.lMap[name] = p
p.Pipe <- true
}
p.Usage += 1
lock.mtx.Unlock()
select {
case <-p.Pipe:
// NOP
}
}
func (lock *Locker) Done(name string) {
lock.mtx.Lock()
p, exist := lock.lMap[name]
if exist {
p.Pipe <- true
if p.Usage > 0 {
p.Usage -= 1
}
}
garbageKeys := make([]string, 0)
for key, _ := range lock.lMap {
elem := lock.lMap[key]
if elem.Usage == 0 && key != name {
garbageKeys = append(garbageKeys, key)
}
}
for _, key := range garbageKeys {
delete(lock.lMap, key)
}
lock.mtx.Unlock()
}
+62
View File
@@ -0,0 +1,62 @@
package locker
import (
"fmt"
"math/rand"
"sync"
"testing"
"time"
)
type Runner struct {
lock *Locker
res sync.Map
}
func NewRunner() *Runner {
return &Runner{
lock: NewLocker(),
}
}
func (r *Runner) Run(wg *sync.WaitGroup, resName string, t *testing.T) {
for n := 2; n < 1000; n++ {
r.lock.WaitAndLock(resName)
val := fmt.Sprintf("%d", n)
r.res.Store(resName, val)
td := time.Duration(rand.Uint64()%1000 + 1)
time.Sleep(td * time.Nanosecond)
foo, exist := r.res.Load(resName)
if !exist {
t.Errorf("not exist!\n")
}
if foo != val {
t.Errorf("not val!\n")
}
r.res.Delete(resName)
r.lock.Done(resName)
time.Sleep(1 * time.Millisecond)
}
wg.Done()
}
func TestLocker(t *testing.T) {
run := NewRunner()
var wg sync.WaitGroup
for n := 1; n < 200; n++ {
go run.Run(&wg, "foo", t)
wg.Add(1)
}
for n := 1; n < 200; n++ {
go run.Run(&wg, "foo/bare", t)
wg.Add(1)
}
for n := 1; n < 200; n++ {
go run.Run(&wg, "foo/bare/foo", t)
wg.Add(1)
}
wg.Wait()
}
+84
View File
@@ -0,0 +1,84 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package logger
import (
"bytes"
"fmt"
"io"
"os"
"sync"
"time"
)
var (
mtx sync.Mutex
output io.WriteCloser = os.Stderr
)
type Logger struct {
subject string
writer io.WriteCloser
mtx *sync.Mutex
}
func NewLoggerWithSubject(subj string) *Logger {
return &Logger{
subject: subj,
writer: output,
mtx: &mtx,
}
}
func NewLogger() *Logger {
return &Logger{
writer: output,
mtx: &mtx,
}
}
func SetWriter(newOut io.WriteCloser) {
mtx.Lock()
output = newOut
mtx.Unlock()
}
func (logg *Logger) SetWriter(newOut io.WriteCloser) {
mtx.Lock()
logg.writer = newOut
var newMtx sync.Mutex
logg.mtx = &newMtx
mtx.Unlock()
}
func (logg *Logger) Debugf(message string, args ...any) {
logg.printf("debug", message, args...)
}
func (logg *Logger) Infof(message string, args ...any) {
logg.printf("info", message, args...)
}
func (logg *Logger) Warningf(message string, args ...any) {
logg.printf("warning", message, args...)
}
func (logg *Logger) Errorf(message string, args ...any) {
logg.printf("error", message, args...)
}
func (logg *Logger) printf(level, message string, args ...any) {
timestamp := time.Now().Format(time.RFC3339)
buffer := bytes.NewBuffer([]byte{})
if logg.subject != "" {
fmt.Fprintf(buffer, "%s %s.%s: ", timestamp, logg.subject, level)
} else {
fmt.Fprintf(buffer, "%s %s: ", timestamp, level)
}
fmt.Fprintf(buffer, message, args...)
fmt.Fprintf(buffer, "\n")
logg.mtx.Lock()
fmt.Fprint(output, buffer.String())
logg.mtx.Unlock()
}
+33
View File
@@ -0,0 +1,33 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package logger
import (
"io/ioutil"
"testing"
)
func TestLogger(t *testing.T) {
logg := NewLogger("test")
logg.Debugf("foo: %s", "bar")
}
func BenchmarkLoggerL(b *testing.B) {
SetWriter(ioutil.Discard)
logg := NewLogger("test")
for i := 0; i < b.N; i++ {
logg.Debugf("foo: %s", "bar")
}
}
func BenchmarkLoggerP(b *testing.B) {
SetWriter(ioutil.Discard)
logg := NewLogger("test")
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
logg.Debugf("foo: %s", "bar")
}
})
}
+113
View File
@@ -0,0 +1,113 @@
package maindb
import (
"context"
"mbase/pkg/descr"
)
func (db *Database) InsertAccount(ctx context.Context, account *descr.Account) error {
var err error
request := `INSERT INTO accounts(id, username, passhash, disabled, created_at, updated_at, created_by, updated_by)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`
_, err = db.db.Exec(request, account.ID, account.Username, account.Passhash, account.Disabled,
account.CreatedAt, account.UpdatedAt, account.CreatedBy, account.UpdatedBy)
if err != nil {
return err
}
return err
}
func (db *Database) UpdateAccountByID(ctx context.Context, accountID string, account *descr.Account) error {
var err error
request := `UPDATE accounts SET username = $1, passhash = $2, disabled = $3, updated_at = $4, updated_by = $5 WHERE id = $6`
_, err = db.db.Exec(request, account.Username, account.Passhash, account.Disabled, account.UpdatedAt, account.UpdatedBy, accountID)
if err != nil {
return err
}
return err
}
func (db *Database) ReducedListAccounts(ctx context.Context) ([]descr.Account, error) {
var err error
request := `SELECT id, username, disabled, created_at, updated_at, created_by, updated_by FROM accounts`
res := make([]descr.Account, 0)
err = db.db.Select(&res, request)
if err != nil {
return res, err
}
return res, err
}
func (db *Database) ListAccounts(ctx context.Context) ([]descr.Account, error) {
var err error
request := `SELECT * FROM accounts`
res := make([]descr.Account, 0)
err = db.db.Select(&res, request)
if err != nil {
return res, err
}
return res, err
}
func (db *Database) GetAccountByID(ctx context.Context, accountID string) (bool, *descr.Account, error) {
var err error
var res *descr.Account
var exists bool = false
request := `SELECT * FROM accounts WHERE id = $1 LiMIT 1`
dbRes := make([]descr.Account, 0)
err = db.db.Select(&dbRes, request, accountID)
if err != nil {
return exists, res, err
}
if len(dbRes) == 0 {
return exists, res, err
}
exists = true
res = &dbRes[0]
return exists, res, err
}
func (db *Database) GetAccountByUsername(ctx context.Context, username string) (bool, *descr.Account, error) {
var err error
var res *descr.Account
var exists bool
request := `SELECT * FROM accounts WHERE username = $1 LIMIT 1`
dbRes := make([]descr.Account, 0)
err = db.db.Select(&dbRes, request, username)
if err != nil {
return exists, res, err
}
if len(dbRes) == 0 {
return false, res, err
}
exists = true
res = &dbRes[0]
return exists, res, err
}
func (db *Database) DeleteAccountByID(ctx context.Context, accountID string) error {
var err error
request := `DELETE FROM accounts WHERE id = $1`
_, err = db.db.Exec(request, accountID)
if err != nil {
return err
}
return err
}
func (db *Database) DeleteAccountByUsername(ctx context.Context, username string) error {
var err error
request := `DELETE FROM accounts WHERE username = $1`
_, err = db.db.Exec(request, username)
if err != nil {
return err
}
return err
}
+149
View File
@@ -0,0 +1,149 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package maindb
import (
"context"
"mbase/pkg/descr"
)
func (db *Database) InsertGrant(ctx context.Context, grant *descr.Grant) error {
var err error
request := `INSERT INTO grants(id, account_id, right, pattern, created_at, updated_at, created_by, updated_by)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`
_, err = db.db.Exec(request, grant.ID, grant.AccountID, grant.Right, grant.Pattern,
grant.CreatedAt, grant.UpdatedAt, grant.CreatedBy, grant.UpdatedBy)
if err != nil {
return err
}
return err
}
func (db *Database) UpdateGrantByID(ctx context.Context, grantID string, grant *descr.Grant) error {
var err error
request := `UPDATE grants SET pattern = $1, updated_at = $2, updated_by = $3 WHERE id = $4`
_, err = db.db.Exec(request, grant.Pattern, grant.UpdatedAt, grant.UpdatedBy, grantID)
if err != nil {
return err
}
return err
}
func (db *Database) ListGrantsByAccountID(ctx context.Context, accountID string) ([]descr.Grant, error) {
var err error
request := `SELECT * FROM grants WHERE account_id = $1`
res := make([]descr.Grant, 0)
err = db.db.Select(&res, request, accountID)
if err != nil {
return res, err
}
return res, err
}
func (db *Database) ListGrants(ctx context.Context) ([]descr.Grant, error) {
var err error
request := `SELECT * FROM grants`
res := make([]descr.Grant, 0)
err = db.db.Select(&res, request)
if err != nil {
return res, err
}
return res, err
}
func (db *Database) GetGrantByID(ctx context.Context, garntID string) (bool, *descr.Grant, error) {
var err error
res := &descr.Grant{}
request := `SELECT * FROM grants WHERE id = $1 LIMIT 1`
dbRes := make([]descr.Grant, 0)
err = db.db.Select(&dbRes, request, garntID)
if err != nil {
return false, res, err
}
if len(dbRes) == 0 {
return false, res, err
}
res = &dbRes[0]
return true, res, err
}
func (db *Database) GetGrantByAccoundIDRight(ctx context.Context, accountID, right string) (bool, *descr.Grant, error) {
var err error
res := &descr.Grant{}
request := `SELECT * FROM grants WHERE account_id = $1 AND right = $2 LIMIT 1`
dbRes := make([]descr.Grant, 0)
err = db.db.Select(&dbRes, request, accountID, right)
if err != nil {
return false, res, err
}
if len(dbRes) == 0 {
return false, res, err
}
res = &dbRes[0]
return true, res, err
}
func (db *Database) ListGrantsByAccoundIDRight(ctx context.Context, accountID, right string) (bool, []descr.Grant, error) {
var err error
request := `SELECT * FROM grants WHERE account_id = $1 AND right = $2`
res := make([]descr.Grant, 0)
err = db.db.Select(&res, request, accountID, right)
if err != nil {
return false, res, err
}
if len(res) == 0 {
return false, res, err
}
return true, res, err
}
func (db *Database) GetGrantByAccoundIDRightPattern(ctx context.Context, accountID, right, pattern string) (bool, *descr.Grant, error) {
var err error
res := &descr.Grant{}
request := `SELECT * FROM grants WHERE account_id = $1 AND right = $2 AND pattern = $3 LIMIT 1`
dbRes := make([]descr.Grant, 0)
err = db.db.Select(&dbRes, request, accountID, right, pattern)
if err != nil {
return false, res, err
}
if len(dbRes) == 0 {
return false, res, err
}
res = &dbRes[0]
return true, res, err
}
func (db *Database) DeleteGrantByAccountIDRightPattern(ctx context.Context, accountID, right, pattern string) error {
var err error
request := `DELETE FROM grants WHERE account_id = $1 AND right = $2 AND pattern = $3`
_, err = db.db.Exec(request, accountID, right, pattern)
if err != nil {
return err
}
return err
}
func (db *Database) DeleteGrantByID(ctx context.Context, grantID string) error {
var err error
request := `DELETE FROM grants WHERE id = $1`
_, err = db.db.Exec(request, grantID)
if err != nil {
return err
}
return err
}
func (db *Database) DeleteAllGrantsForAccountID(ctx context.Context, grantID string) error {
var err error
request := `DELETE FROM grants WHERE account_id = $1`
_, err = db.db.Exec(request, grantID)
if err != nil {
return err
}
return err
}
+58
View File
@@ -0,0 +1,58 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package maindb
import (
"context"
"fmt"
"testing"
"time"
"mbase/pkg/auxtool"
"mbase/pkg/auxuuid"
"mbase/pkg/descr"
"github.com/stretchr/testify/require"
)
func TestGrant(t *testing.T) {
var err error
dbDir := t.TempDir()
db := NewDatabase(dbDir)
err = db.OpenDatabase()
require.NoError(t, err)
err = db.InitDatabase()
require.NoError(t, err)
id := auxuuid.NewUUID()
accountID := auxuuid.NewUUID()
timenow := auxtool.TimeNow()
creator := auxuuid.NewUUID()
newGrant := &descr.Grant{
ID: id,
AccountID: accountID,
Right: "rigthFoo",
Pattern: `*`,
CreatedAt: timenow,
UpdatedAt: timenow,
CreatedBy: creator,
UpdatedBy: creator,
}
ctx, _ := context.WithTimeout(context.Background(), 1*time.Second)
err = db.InsertGrant(ctx, newGrant)
require.NoError(t, err)
files, err := db.ListGrantsByAccountID(ctx, accountID)
require.NoError(t, err)
require.Equal(t, len(files), 1)
require.Equal(t, files[0].ID, id)
require.Equal(t, files[0].AccountID, accountID)
require.Equal(t, files[0].CreatedBy, creator)
fmt.Println(files[0].CreatedBy)
}
+83
View File
@@ -0,0 +1,83 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*
*
*/
package maindb
import (
"context"
"mbase/pkg/auxpwd"
"mbase/pkg/auxtool"
"mbase/pkg/auxuuid"
"mbase/pkg/descr"
"mbase/pkg/terms"
)
func (db *Database) WriteAnonymous(ctx context.Context) error {
var err error
now := auxtool.TimeNow()
password := auxtool.RandomString(64)
passhash := auxpwd.MakeSHA256Hash([]byte(password))
accountDescr := &descr.Account{
ID: terms.AnonymousID,
Username: terms.AnonimousUsername,
Passhash: passhash,
Disabled: false,
CreatedAt: now,
UpdatedAt: now,
CreatedBy: terms.ServerID,
UpdatedBy: terms.ServerID,
}
err = db.InsertAccount(ctx, accountDescr)
if err != nil {
return err
}
return err
}
func (db *Database) WriteInituser(ctx context.Context) error {
var err error
now := auxtool.TimeNow()
passhash := auxpwd.MakeSHA256Hash([]byte(terms.InitUsername))
accountDescr := &descr.Account{
ID: terms.InitID,
Username: terms.InitUsername,
Passhash: passhash,
Disabled: false,
CreatedAt: now,
UpdatedAt: now,
CreatedBy: terms.ServerID,
UpdatedBy: terms.ServerID,
}
err = db.InsertAccount(ctx, accountDescr)
if err != nil {
return err
}
fullRights := []string{
terms.RightWriteAccounts,
terms.RightReadAccounts,
}
for _, right := range fullRights {
grantDescr := &descr.Grant{
ID: auxuuid.NewUUID(),
AccountID: accountDescr.ID,
Right: right,
Pattern: ".*",
CreatedAt: now,
UpdatedAt: now,
CreatedBy: terms.ServerID,
UpdatedBy: terms.ServerID,
}
err = db.InsertGrant(ctx, grantDescr)
if err != nil {
return err
}
}
return err
}
+50
View File
@@ -0,0 +1,50 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package maindb
import (
"fmt"
"path/filepath"
"mbase/app/logger"
"github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"
)
type Database struct {
datapath string
logg *logger.Logger
db *sqlx.DB
}
func NewDatabase(datapath string) *Database {
return &Database{
datapath: datapath,
logg: logger.NewLoggerWithSubject("maindb"),
}
}
func (db *Database) OpenDatabase() error {
var err error
dbPath := filepath.Join(db.datapath, "mbase.db")
db.db, err = sqlx.Open("sqlite3", fmt.Sprintf("%s?cache=shared&mode=rwc&_journal_mode=WAL", dbPath))
if err != nil {
return fmt.Errorf("Open database error: %v", err)
}
err = db.db.Ping()
if err != nil {
return fmt.Errorf("Ping database error: %v", err)
}
return err
}
func (db *Database) InitDatabase() error {
var err error
_, err = db.db.Exec(schema)
if err != nil {
return fmt.Errorf("Init database error: %v", err)
}
return err
}
+43
View File
@@ -0,0 +1,43 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package maindb
const schema = `
--- DROP TABLE IF EXISTS accounts;
CREATE TABLE IF NOT EXISTS accounts (
id TEXT NOT NULL,
username TEXT NOT NULL,
passhash TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
created_by TEXT NOT NULL,
updated_by TEXT NOT NULL,
disabled BOOL
);
CREATE UNIQUE INDEX IF NOT EXISTS accounts_index01
ON accounts(id);
CREATE UNIQUE INDEX IF NOT EXISTS accounts_index02
ON accounts(username);
--- DROP TABLE IF EXISTS grants;
CREATE TABLE IF NOT EXISTS grants (
id TEXT NOT NULL,
account_id INT NOT NULL,
right TEXT NOT NULL,
pattern TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
created_by TEXT NOT NULL,
updated_by TEXT NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS grants_index00
ON grants(id);
CREATE INDEX IF NOT EXISTS grants_index01
ON grants(account_id);
CREATE INDEX IF NOT EXISTS grants_index02
ON grants(account_id, right);
CREATE UNIQUE INDEX IF NOT EXISTS grants_index03
ON grants(account_id, right, pattern);
`
+57
View File
@@ -0,0 +1,57 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package router
import (
"reflect"
"strconv"
)
// The code reflect string-string map to taged structure
// Limited, used only base types
// Don't ask me how it works. I'm only writer ;)
func bindObj(obj interface{}, kvmap map[string]string, sTag string) error {
var err error
vElem := reflect.ValueOf(obj).Elem()
sElem := reflect.TypeOf(obj).Elem()
for i := 0; i < vElem.NumField(); i++ {
vField := vElem.Field(i)
tag := sElem.Field(i).Tag.Get(sTag)
if tag != "" {
sVal, exists := kvmap[tag]
if exists {
switch vField.Kind() {
case reflect.String:
vField.SetString(sVal)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
iVal, err := strconv.ParseInt(sVal, 10, 64)
if err != nil {
return err
}
vField.SetInt(iVal)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
iVal, err := strconv.ParseUint(sVal, 10, 64)
if err != nil {
return err
}
vField.SetUint(iVal)
case reflect.Bool:
bVal, err := strconv.ParseBool(sVal)
if err != nil {
return err
}
vField.SetBool(bVal)
case reflect.Float32, reflect.Float64:
fVal, err := strconv.ParseFloat(sVal, 64)
if err != nil {
return err
}
vField.SetFloat(fVal)
}
}
}
}
return err
}
+150
View File
@@ -0,0 +1,150 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package router
import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
"net/url"
"strconv"
)
type Context struct {
Ctx context.Context
Request *http.Request
Writer http.ResponseWriter
PathMap map[string]string
Bools map[string]bool
Strings map[string]string
StatusCode int
}
func NewContext(writer http.ResponseWriter, request *http.Request) *Context {
rctx := &Context{
Writer: writer,
Request: request,
Ctx: request.Context(),
PathMap: make(map[string]string),
Bools: make(map[string]bool),
Strings: make(map[string]string),
}
return rctx
}
// Aux maps
func (rctx *Context) SetBool(key string, value bool) {
rctx.Bools[key] = value
}
func (rctx *Context) GetBool(key string) (bool, bool) {
exists, value := rctx.Bools[key]
return exists, value
}
func (rctx *Context) SetString(key string, value string) {
rctx.Strings[key] = value
}
func (rctx *Context) GetString(key string) (string, bool) {
value, exists := rctx.Strings[key]
return value, exists
}
// Request
func (rctx *Context) GetSubpath(key string) (string, bool) {
value, exists := rctx.PathMap[key]
return value, exists
}
func (rctx *Context) URL() *url.URL {
return rctx.Request.URL
}
func (rctx *Context) GetQuery(key string) string {
return rctx.Request.URL.Query().Get(key)
}
func (rctx *Context) GetHeader(key string) string {
return rctx.Request.Header.Get(key)
}
func (rctx *Context) GetHeaders() http.Header {
return rctx.Request.Header
}
func (rctx *Context) GetContext() context.Context {
return rctx.Request.Context()
}
// Binding
const emptyJSON = "{}"
func (rctx *Context) BindJSON(obj any) error {
buffer := bytes.NewBuffer(nil)
_, err := io.Copy(buffer, rctx.Request.Body)
if err != nil {
return err
}
reqBody := buffer.Bytes()
if len(reqBody) == 0 {
reqBody = []byte(emptyJSON)
}
err = json.Unmarshal(reqBody, obj)
if err != nil {
return err
}
return err
}
func (rctx *Context) BindQuery(obj any) error {
qMap := make(map[string]string)
for key, val := range rctx.Request.URL.Query() {
if len(val) == 1 {
qMap[key] = val[0]
}
}
return bindObj(obj, qMap, "param")
}
// Response
func (rctx *Context) SetHeader(key, value string) {
rctx.Writer.Header().Set(key, value)
}
func (rctx *Context) SetStatus(httpStatus int) {
rctx.StatusCode = httpStatus
rctx.Writer.WriteHeader(httpStatus)
}
func (rctx *Context) SendJSON(statusCode int, payload any) {
rctx.StatusCode = statusCode
buffer := bytes.NewBuffer(nil)
json.NewEncoder(buffer).Encode(payload)
rctx.Writer.Header().Set("Content-Type", "application/json")
size := strconv.FormatInt(int64(len(buffer.Bytes())), 10)
rctx.Writer.Header().Set("Content-Length", size)
rctx.Writer.WriteHeader(statusCode)
rctx.Writer.Write(buffer.Bytes())
}
func (rctx *Context) SendText(statusCode int, payload string) {
rctx.StatusCode = statusCode
size := strconv.FormatInt(int64(len(payload)), 10)
rctx.Writer.Header().Set("Content-Type", "text/plain")
rctx.Writer.Header().Set("Content-Length", size)
rctx.Writer.WriteHeader(statusCode)
rctx.Writer.Write([]byte(payload))
}
func (rctx *Context) SendBytes(statusCode int, ctype string, payload []byte) {
rctx.StatusCode = statusCode
size := strconv.FormatInt(int64(len(payload)), 10)
rctx.Writer.Header().Set("Content-Type", ctype)
rctx.Writer.Header().Set("Content-Length", size)
rctx.Writer.WriteHeader(statusCode)
rctx.Writer.Write(payload)
}
+36
View File
@@ -0,0 +1,36 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package router
func NewCorsMiddleware() MiddlewareFunc {
mw := func(next Handler) Handler {
return newCorsHandler(next)
}
return mw
}
type corsHandler struct {
next Handler
}
func newCorsHandler(next Handler) *corsHandler {
return &corsHandler{
next: next,
}
}
func (hand corsHandler) ServeHTTP(ctx *Context) {
origin := ctx.Request.Header.Get("Origin")
if origin != "" {
ctx.SetHeader("Access-Control-Allow-Origin", origin)
ctx.SetHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE, PATCH")
ctx.SetHeader("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
ctx.SetHeader("Access-Control-Max-Age", "86400")
ctx.SetHeader("Access-Control-Allow-Credentials", "true")
}
if ctx.Request.Method == "OPTIONS" {
return
}
hand.next.ServeHTTP(ctx)
}
+31
View File
@@ -0,0 +1,31 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package router
func NewLoggingMiddleware(print func(string, ...any)) MiddlewareFunc {
mw := func(next Handler) Handler {
return newLoggingHandler(next, print)
}
return mw
}
type loggingHandler struct {
next Handler
printFunc func(string, ...any)
}
func newLoggingHandler(next Handler, print func(string, ...any)) *loggingHandler {
return &loggingHandler{
next: next,
printFunc: print,
}
}
func (logging loggingHandler) ServeHTTP(rctx *Context) {
logging.next.ServeHTTP(rctx)
cl := rctx.Writer.Header().Get("Content-Length")
logging.printFunc("%s %s %s %s %s %d", rctx.Request.RemoteAddr,
rctx.Request.Method, rctx.Request.URL.String(),
rctx.Request.Proto, cl, rctx.StatusCode)
}
+74
View File
@@ -0,0 +1,74 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package router
import (
"fmt"
"strings"
)
const (
compContextPlain int = iota
compContextRegex
startRegex byte = '{'
stopRegex byte = '}'
)
func pathCompiler(path string) (string, error) {
var err error
res := make([]byte, 0)
var pos int = compContextPlain
var depth int = 0
pattern := make([]byte, 0)
for _, b := range []byte(path) {
switch pos {
case compContextPlain:
switch b {
case stopRegex:
depth -= 1
res = append(res, b)
case startRegex:
depth += 1
pos = compContextRegex // pattern started
pattern = make([]byte, 0)
default:
res = append(res, b)
}
case compContextRegex:
switch b {
case startRegex:
depth += 1
case stopRegex:
depth -= 1
if depth == 0 {
pattern = convertRegexp(pattern)
res = append(res, pattern...)
pos = compContextPlain // pattern ended
}
default:
pattern = append(pattern, b)
}
}
}
if depth != 0 {
err = fmt.Errorf("Unbalanced brackets into pattern")
}
return string(res), err
}
const (
defaultRegexp = `[a-zA-Z0-9_\.*][/\-\.,a-zA-Z0-9_%=*\[\]:~\$]+`
)
func convertRegexp(src []byte) []byte {
var res string
const patternSeps = ":"
parts := strings.SplitN(string(src), patternSeps, 2)
if len(parts) == 1 {
parts = append(parts, defaultRegexp)
}
res = fmt.Sprintf("(?<%s>%s)", parts[0], parts[1])
return []byte(res)
}
+49
View File
@@ -0,0 +1,49 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package router
import (
"fmt"
"regexp"
"testing"
"github.com/stretchr/testify/require"
)
func tDebugf(msg string, args ...any) {
fmt.Printf("debug: ")
fmt.Printf(msg, args...)
fmt.Printf("\n")
}
func TestPatchCompilerA(t *testing.T) {
var err error
srcPath := `/v1/file/{collection:[a-zA-Z]+}/{name}`
reSource, err := pathCompiler(srcPath)
require.NoError(t, err)
tDebugf("re: %s\n", reSource)
re, err := regexp.Compile(reSource)
require.NoError(t, err)
reqPath := `/v1/file/foo/bare`
match := re.MatchString(reqPath)
require.True(t, match)
submatch := re.FindStringSubmatch(reqPath)
subnames := re.SubexpNames()
submap := make(map[string]string)
for i, val := range subnames {
tDebugf("subname: %d = %s", i, val)
}
for i, val := range submatch {
key := subnames[i]
if key != "" {
submap[key] = val
}
tDebugf("sub: %d = %s", i, val)
}
tDebugf("submap: %v", submap)
}
+43
View File
@@ -0,0 +1,43 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package router
import (
"net/http"
"runtime/debug"
"time"
)
func NewRecoveryMiddleware(print func(string, ...any)) MiddlewareFunc {
mw := func(next Handler) Handler {
return newRecoveryHandler(next, print)
}
return mw
}
type recoveryHandler struct {
next Handler
print func(string, ...any)
}
func newRecoveryHandler(next Handler, print func(string, ...any)) *recoveryHandler {
return &recoveryHandler{
next: next,
print: print,
}
}
func (hand recoveryHandler) ServeHTTP(rctx *Context) {
exitFunc := func() {
err := recover()
if err != nil {
rctx.Writer.WriteHeader(http.StatusInternalServerError)
stack := string(debug.Stack())
timestamp := time.Now().Format(time.RFC3339)
hand.print("%s %v ; %s\n", timestamp, err, stack)
}
}
defer exitFunc()
hand.next.ServeHTTP(rctx)
}
+162
View File
@@ -0,0 +1,162 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package router
import (
"fmt"
"net/http"
"regexp"
)
type MiddlewareFunc func(next Handler) Handler
type Handler interface {
ServeHTTP(rctx *Context)
}
type HandlerFunc func(rctx *Context)
func (handlerFunc HandlerFunc) ServeHTTP(rctx *Context) {
handlerFunc(rctx)
}
type Router struct {
headHandler Handler
routeHandler *Selector
}
func NewRouter() *Router {
selector := NewSelector()
return &Router{
routeHandler: selector,
headHandler: selector,
}
}
func (rout *Router) Use(mwFunc MiddlewareFunc) {
rout.headHandler = mwFunc(rout.headHandler)
}
func (rout *Router) Selector() *Selector {
return rout.routeHandler
}
func (rout *Router) AddRoute(method, path string, handlerFunc HandlerFunc) {
rout.routeHandler.AddRoute(method, path, handlerFunc)
}
func (rout *Router) Head(path string, handlerFunc HandlerFunc) {
rout.routeHandler.AddRoute("HEAD", path, handlerFunc)
}
func (rout *Router) Get(path string, handlerFunc HandlerFunc) {
rout.routeHandler.AddRoute("GET", path, handlerFunc)
}
func (rout *Router) Post(path string, handlerFunc HandlerFunc) {
rout.routeHandler.AddRoute("POST", path, handlerFunc)
}
func (rout *Router) Put(path string, handlerFunc HandlerFunc) {
rout.routeHandler.AddRoute("PUT", path, handlerFunc)
}
func (rout *Router) Patch(path string, handlerFunc HandlerFunc) {
rout.routeHandler.AddRoute("PATCH", path, handlerFunc)
}
func (rout *Router) Delete(path string, handlerFunc HandlerFunc) {
rout.routeHandler.AddRoute("DELETE", path, handlerFunc)
}
func (rout *Router) ServeHTTP(writer http.ResponseWriter, req *http.Request) {
rctx := NewContext(writer, req)
rout.headHandler.ServeHTTP(rctx)
}
func (rout *Router) NotFound(handlerFunc HandlerFunc) {
rout.routeHandler.notFound = handlerFunc
}
type Selector struct {
Routes []*Route
notFound Handler
}
func NewSelector() *Selector {
notFound := HandlerFunc(func(ctx *Context) {
http.NotFound(ctx.Writer, ctx.Request)
})
return &Selector{
Routes: make([]*Route, 0),
notFound: notFound,
}
}
const globalRoutePattern = `^%s$`
func (hand *Selector) AddRoute(method, path string, handlerFunc HandlerFunc) error {
var err error
rawPath := path
path, err = pathCompiler(path)
if err != nil {
return err
}
path = fmt.Sprintf(globalRoutePattern, path)
re, err := regexp.Compile(path)
if err != nil {
return err
}
route := &Route{
Method: method,
Path: path,
RawPath: rawPath,
Handler: handlerFunc,
Regexp: re,
}
hand.Routes = append(hand.Routes, route)
return err
}
func (hand *Selector) ServeHTTP(rctx *Context) {
realHandler := hand.notFound
for _, route := range hand.Routes {
match := route.Match(rctx.Request)
if !match {
continue
}
subvals := route.Regexp.FindStringSubmatch(rctx.Request.URL.Path)
subkeys := route.Regexp.SubexpNames()
for i, val := range subvals {
key := subkeys[i]
if key != "" {
rctx.PathMap[key] = val
}
}
realHandler = route.Handler
break
}
realHandler.ServeHTTP(rctx)
}
type Route struct {
Method string
Regexp *regexp.Regexp
Path string
RawPath string
Handler HandlerFunc
}
func (route Route) Match(req *http.Request) bool {
if req.Method != route.Method {
return false
}
match := route.Regexp.MatchString(req.URL.Path)
if match {
return true
}
return false
}
+195
View File
@@ -0,0 +1,195 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package router
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/require"
)
func TestRouterStatus(t *testing.T) {
reqPath := "/hello"
handler := NewRouter()
helloHandler := func(rctx *Context) {
rctx.SetStatus(http.StatusOK)
}
handler.Get(reqPath, helloHandler)
request, err := http.NewRequest("GET", reqPath, nil)
require.NoError(t, err)
recorder := httptest.NewRecorder()
handler.ServeHTTP(recorder, request)
require.Equal(t, http.StatusOK, recorder.Code)
fmt.Printf("Response code: %d\n", recorder.Code)
}
func TestRouterSendText(t *testing.T) {
testText := "hello, world"
reqPath := "/hello"
rout := NewRouter()
helloHandler := func(rctx *Context) {
rctx.SendText(http.StatusOK, testText)
}
rout.Get(reqPath, helloHandler)
request, err := http.NewRequest("GET", reqPath, nil)
require.NoError(t, err)
recorder := httptest.NewRecorder()
rout.ServeHTTP(recorder, request)
fmt.Printf("Response code: %d\n", recorder.Code)
require.Equal(t, http.StatusOK, recorder.Code)
bodyReader := recorder.Body
bodyBytes, err := io.ReadAll(bodyReader)
fmt.Printf("Response body: %s\n", string(bodyBytes))
require.Equal(t, string(bodyBytes), testText)
}
func TestRouterSendJSON(t *testing.T) {
type testStruct struct {
Message string `json:"message"`
Code int64 `json:"code"`
}
testVar := testStruct{
Message: "hello, world",
Code: 123,
}
testData, err := json.Marshal(testVar)
require.NoError(t, err)
reqPath := "/hello"
handler := NewRouter()
helloHandler := func(rctx *Context) {
rctx.SendJSON(http.StatusOK, &testVar)
}
handler.Get(reqPath, helloHandler)
request, err := http.NewRequest("GET", reqPath, nil)
require.NoError(t, err)
recorder := httptest.NewRecorder()
handler.ServeHTTP(recorder, request)
fmt.Printf("Response code: %d\n", recorder.Code)
require.Equal(t, http.StatusOK, recorder.Code)
bodyReader := recorder.Body
bodyBytes, err := io.ReadAll(bodyReader)
require.NoError(t, err)
bodyBytes = bytes.Trim(bodyBytes, "\n\r")
fmt.Printf("Response body: %s\n", string(bodyBytes))
require.Equal(t, string(testData), string(bodyBytes))
}
func TestRouterBindJSON(t *testing.T) {
type testStruct struct {
Message string `json:"message"`
Code int64 `json:code"`
}
testVar := testStruct{
Message: "hello, world",
Code: 123,
}
testData, err := json.Marshal(testVar)
require.NoError(t, err)
buffer := bytes.NewBuffer(testData)
reqPath := "/hello"
handler := NewRouter()
helloHandler := func(rctx *Context) {
handVar := testStruct{}
rctx.BindJSON(&handVar)
fmt.Printf("Received message: %s - %d\n", handVar.Message, handVar.Code)
require.Equal(t, handVar.Code, int64(123))
rctx.SendJSON(http.StatusOK, &handVar)
}
handler.Post(reqPath, helloHandler)
request, err := http.NewRequest("POST", reqPath, buffer)
require.NoError(t, err)
recorder := httptest.NewRecorder()
handler.ServeHTTP(recorder, request)
fmt.Printf("Response code: %d\n", recorder.Code)
require.Equal(t, http.StatusOK, recorder.Code)
bodyReader := recorder.Body
bodyBytes, err := io.ReadAll(bodyReader)
require.NoError(t, err)
bodyBytes = bytes.Trim(bodyBytes, "\n\r")
fmt.Printf("Response body: %s\n", string(bodyBytes))
require.Equal(t, string(testData), string(bodyBytes))
}
func TestRouterBindParams(t *testing.T) {
reqPath := "/hello"
handler := NewRouter()
helloHandler := func(rctx *Context) {
type Params struct {
Name string `param:"name"`
Code int64 `param:"code"`
}
params := &Params{}
rctx.BindQuery(params)
fmt.Printf("Received name: %s\n", params.Name)
fmt.Printf("Received code: %d\n", params.Code)
rctx.SendText(http.StatusOK, "hello")
}
handler.Get(reqPath, helloHandler)
reqPath = reqPath + `?name=world&code=123`
request, err := http.NewRequest("GET", reqPath, nil)
require.NoError(t, err)
recorder := httptest.NewRecorder()
handler.ServeHTTP(recorder, request)
fmt.Printf("Response code: %d\n", recorder.Code)
require.Equal(t, http.StatusOK, recorder.Code)
bodyReader := recorder.Body
bodyBytes, err := io.ReadAll(bodyReader)
require.NoError(t, err)
bodyBytes = bytes.Trim(bodyBytes, "\n\r")
fmt.Printf("Response body: %s\n", string(bodyBytes))
}
func BenchmarkLoggerL(b *testing.B) {
reqPath := "/hello"
helloHandler := func(rctx *Context) {
rctx.SetStatus(http.StatusOK)
}
handler := NewRouter()
handler.Get(reqPath, helloHandler)
request, err := http.NewRequest("GET", reqPath, nil)
require.NoError(b, err)
for i := 0; i < b.N; i++ {
recorder := httptest.NewRecorder()
handler.ServeHTTP(recorder, request)
require.Equal(b, http.StatusOK, recorder.Code)
}
}
+37
View File
@@ -0,0 +1,37 @@
package server
import (
"crypto/tls"
"net"
)
const protocol = "tcp"
func CreateTLSListener(addrinfo string, x509cert, x509key []byte) (net.Listener, error) {
var listen net.Listener
var err error
tlsCert, err := tls.X509KeyPair(x509cert, x509key)
if err != nil {
return listen, err
}
tlsConfig := tls.Config{
Certificates: []tls.Certificate{tlsCert},
ClientAuth: tls.NoClientCert,
InsecureSkipVerify: true,
}
listen, err = tls.Listen(protocol, addrinfo, &tlsConfig)
if err != nil {
return listen, err
}
return listen, err
}
func CreateListener(addrinfo string) (net.Listener, error) {
var listen net.Listener
var err error
listen, err = net.Listen(protocol, addrinfo)
if err != nil {
return listen, err
}
return listen, err
}
+547
View File
@@ -0,0 +1,547 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*
*
*/
package server
import (
"context"
"fmt"
"io/ioutil"
"net"
"os"
"os/signal"
"os/user"
"path/filepath"
"strconv"
"sync"
"syscall"
"time"
"mbase/app/config"
"mbase/app/handler"
"mbase/app/logger"
"mbase/app/maindb"
"mbase/app/service"
"mbase/pkg/auxtool"
"mbase/pkg/descr"
"mbase/app/accoper"
"mbase/app/servoper"
yaml "go.yaml.in/yaml/v4"
)
type Server struct {
conf *config.Config
acop *accoper.Operator
seop *servoper.Operator
svc *service.Service
mdb *maindb.Database
hand *handler.Handler
logg *logger.Logger
stat descr.Server
ctx context.Context
cancel context.CancelFunc
wg sync.WaitGroup
logf *os.File
//x509cert []byte
//x509key []byte
listen net.Listener
}
func NewServer() (*Server, error) {
var err error
srv := &Server{}
srv.logg = logger.NewLoggerWithSubject("server")
srv.ctx, srv.cancel = context.WithCancel(context.Background())
return srv, err
}
func (srv *Server) Handler() *handler.Handler {
return srv.hand
}
func (srv *Server) Service() *service.Service {
return srv.svc
}
func (srv *Server) SetLogdir(dir string) {
srv.conf.Logpath = dir
}
func (srv *Server) SetRundir(dir string) {
srv.conf.Runpath = dir
}
func (srv *Server) SetDatadir(dir string) {
srv.conf.Database.Basepath = dir
srv.conf.Storage.Basepath = dir
srv.conf.Datadir = dir
}
func (srv *Server) SetPort(port uint32) {
srv.conf.Service.Port = port
}
func (srv *Server) SetAsDaemon(asDaemon bool) {
srv.conf.AsDaemon = asDaemon
}
func (srv *Server) Configure() error {
var err error
//srv.logg.Infof("Configuration server")
srv.conf = config.NewConfig()
if err != nil {
return err
}
err = srv.conf.ReadConfigfile()
if err != nil {
srv.logg.Warningf("Error loading config file: %v", err)
err = nil
}
err = srv.conf.ReadX509Cert()
if err != nil {
return err
}
return err
}
func (srv *Server) writeStat() error {
// Write status file
var err error
statefilePath := filepath.Join(srv.conf.Datadir, "server.yaml")
stateData, err := yaml.Marshal(srv.stat)
if err != nil {
return err
}
err = ioutil.WriteFile(statefilePath, stateData, 0640)
if err != nil {
return err
}
return err
}
func (srv *Server) readStat() error {
var err error
// Read state file
statefilePath := filepath.Join(srv.conf.Datadir, "server.yaml")
if auxtool.FileExists(statefilePath) {
stateData, err := ioutil.ReadFile(statefilePath)
if err == nil {
stat := descr.Server{}
err = yaml.Unmarshal(stateData, &stat)
if err != nil {
return err
}
srv.stat = stat
}
}
return err
}
func (srv *Server) Build() error {
var err error
//srv.logg.Infof("Server building")
currUser, err := user.Current()
if err != nil {
err = fmt.Errorf("Error getting current user: %v\n", err)
return err
}
cuid64, err := strconv.ParseInt(currUser.Uid, 10, 64)
if err != nil {
return err
}
cgid64, err := strconv.ParseInt(currUser.Gid, 10, 64)
if err != nil {
return err
}
euid := int(cuid64)
egid := int(cgid64)
if cuid64 == 0 {
usr, err := user.Lookup(srv.conf.RunUser)
if err != nil {
return err
}
uid64, err := strconv.ParseInt(usr.Uid, 10, 64)
if err != nil {
return err
}
gid64, err := strconv.ParseInt(usr.Gid, 10, 64)
if err != nil {
return err
}
euid = int(uid64)
egid = int(gid64)
}
// Creating datadir
datadir := srv.conf.Datadir
if !auxtool.DirExists(datadir) { // TODO: check access to dir
//srv.logg.Infof("Creating data directory %s ", datadir)
err = os.MkdirAll(datadir, 0750)
if err != nil {
return err
}
}
err = os.Chown(datadir, euid, egid)
if err != nil {
return err
}
if srv.conf.AsDaemon {
logdir := filepath.Dir(srv.conf.Logpath)
//srv.logg.Infof("Creating log directory %s", logdir)
err = os.MkdirAll(logdir, 0750)
if err != nil {
return err
}
err = os.Chown(logdir, euid, egid)
if err != nil {
return err
}
rundir := filepath.Dir(srv.conf.Runpath)
//srv.logg.Infof("Creating run directory %s", rundir)
err = os.MkdirAll(rundir, 0750)
if err != nil {
return err
}
err = os.Chown(rundir, euid, egid)
if err != nil {
return err
}
// Redirect stderr and stout
logFile, err := os.OpenFile(srv.conf.Logpath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0640)
if err != nil {
return err
}
err = syscall.Dup2(int(logFile.Fd()), int(os.Stdout.Fd()))
if err != nil {
return err
}
err = syscall.Dup2(int(logFile.Fd()), int(os.Stderr.Fd()))
if err != nil {
return err
}
srv.logf = logFile
}
confDump := srv.conf.String()
srv.logg.Infof("Current server configuration is:\n%s\n", confDump)
// Read state file
srv.logg.Infof("Reading server status")
err = srv.readStat()
if err != nil {
return err
}
// Creating database dir
dbdir := srv.conf.Database.Basepath
srv.logg.Infof("Creating database directory %s ", dbdir)
err = os.MkdirAll(dbdir, 0750)
if err != nil {
return err
}
// Creating storage dir
srv.logg.Infof("Creating storage directory")
datadir = srv.conf.Database.Basepath
err = os.MkdirAll(datadir, 0750)
if err != nil {
return err
}
err = os.Chown(datadir, euid, egid)
if err != nil {
return err
}
cert, key := []byte(srv.conf.X509Cert), []byte(srv.conf.X509Key)
addrinfo := fmt.Sprintf("%s:%d", srv.conf.Service.Address, srv.conf.Service.Port)
listener, err := CreateTLSListener(addrinfo, cert, key)
if err != nil {
return err
}
srv.listen = listener
if cuid64 == 0 {
// Change effective user and group
err = syscall.Setgid(egid)
if err != nil {
return err
}
err = syscall.Setuid(euid)
if err != nil {
return err
}
}
//return fmt.Errorf("Debug break")
uidstr := strconv.FormatInt(int64(syscall.Geteuid()), 10)
usr, err := user.LookupId(uidstr)
if err != nil {
return err
}
srv.logg.Warningf("Now run as user: %s", usr.Username)
// Creating database
mdb := maindb.NewDatabase(dbdir)
srv.logg.Infof("Opening main database")
err = mdb.OpenDatabase()
if err != nil {
return err
}
srv.mdb = mdb
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
// Created db scheme
if !srv.stat.SchemeCreated {
srv.logg.Infof("Initialize main database")
err = mdb.InitDatabase()
if err != nil {
return err
}
srv.stat.SchemeCreated = true
srv.stat.SchemeCreatedAt = auxtool.TimeNow()
srv.logg.Infof("Writing server status")
err = srv.writeStat()
if err != nil {
return err
}
}
// Created anonymous user
if !srv.stat.AnonymousCreated {
srv.logg.Infof("Creating anonymous user")
err = mdb.WriteAnonymous(ctx)
if err != nil {
return err
}
srv.stat.AnonymousCreated = true
srv.stat.AnonymousCreatedAt = auxtool.TimeNow()
srv.logg.Infof("Writing server status")
err = srv.writeStat()
if err != nil {
return err
}
}
if !srv.stat.InituserCreated {
srv.logg.Infof("Creating init user")
err = srv.mdb.WriteInituser(ctx)
if err != nil {
return err
}
srv.stat.InituserCreatedAt = auxtool.TimeNow()
srv.stat.InituserCreated = true
srv.logg.Infof("Writing server status")
err = srv.writeStat()
if err != nil {
return err
}
}
// Creating account operator
srv.logg.Infof("Creating account operator")
accoperParams := &accoper.OperatorParams{
MainDB: srv.mdb,
}
srv.acop, err = accoper.NewOperator(accoperParams)
if err != nil {
return err
}
// Creating service operator
srv.logg.Infof("Creating operator")
servoperParams := &servoper.OperatorParams{
MainDB: srv.mdb,
}
srv.seop, err = servoper.NewOperator(servoperParams)
if err != nil {
return err
}
// Creating handler
srv.logg.Infof("Creating handler")
handlerParams := &handler.HandlerParams{
MainDB: srv.mdb,
AccOper: srv.acop,
ServOper: srv.seop,
}
srv.hand, err = handler.NewHandler(handlerParams)
if err != nil {
return err
}
// Creating service
serviceParams := &service.ServiceParams{
Handler: srv.hand,
Listener: srv.listen,
}
srv.logg.Infof("Creating service")
srv.svc, err = service.NewService(serviceParams)
if err != nil {
return err
}
// Building service
err = srv.svc.Build()
if err != nil {
return err
}
return err
}
func (srv *Server) Run() error {
var err error
if srv.conf.AsDaemon {
// Redirect stdin
nullFile, err := os.OpenFile("/dev/null", os.O_RDWR, 0)
if err != nil {
return err
}
err = syscall.Dup2(int(nullFile.Fd()), int(os.Stdin.Fd()))
if err != nil {
return err
}
// Write process ID
pidFile, err := os.OpenFile(srv.conf.Runpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer pidFile.Close()
currPid := os.Getpid()
_, err = pidFile.WriteString(strconv.Itoa(currPid))
if err != nil {
return err
}
// Start log rotator
srv.Rotator()
}
currUser, err := user.Current()
if err != nil {
return err
}
srv.logg.Infof("Server started with user: %s", currUser.Username)
uidstr := strconv.FormatInt(int64(syscall.Geteuid()), 10)
usr, err := user.LookupId(uidstr)
if err != nil {
return err
}
srv.logg.Infof("Server run with user: %s", usr.Username)
svcDone := make(chan error, 1)
// Service run
srv.logg.Infof("Start service")
startService := func(svc *service.Service, done chan error) {
err = svc.Run()
if err != nil {
srv.logg.Errorf("Service error: %v", err)
done <- err
}
}
go startService(srv.svc, svcDone)
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
var signal os.Signal
select {
case signal = <-sigs:
srv.logg.Infof("Services stopped by signal: %v", signal)
srv.cancel()
srv.svc.Stop()
srv.wg.Wait()
case err = <-svcDone:
srv.logg.Infof("Service stopped by service error: %v", err)
srv.cancel()
srv.svc.Stop()
srv.wg.Wait()
}
return err
}
func (srv *Server) PseudoFork() error {
const successExit int = 0
var keyEnv string = "IMX0LTSELMRF8K"
var err error
_, isChild := os.LookupEnv(keyEnv)
switch {
case !isChild:
os.Setenv(keyEnv, "TRUE")
procAttr := syscall.ProcAttr{}
cwd, err := os.Getwd()
if err != nil {
return err
}
var sysFiles = make([]uintptr, 3)
sysFiles[0] = uintptr(syscall.Stdin)
sysFiles[1] = uintptr(syscall.Stdout)
sysFiles[2] = uintptr(syscall.Stderr)
procAttr.Files = sysFiles
procAttr.Env = os.Environ()
procAttr.Dir = cwd
_, err = syscall.ForkExec(os.Args[0], os.Args, &procAttr)
if err != nil {
return err
}
os.Exit(successExit)
case isChild:
_, err = syscall.Setsid()
if err != nil {
return err
}
}
os.Unsetenv(keyEnv)
return err
}
func (srv *Server) Daemonize() error {
var err error
if srv.conf.AsDaemon {
// Restart process process
err = srv.PseudoFork()
if err != nil {
return err
}
}
return err
}
func (srv *Server) Rotator() {
srv.wg.Add(1)
var counter uint64
logFunc := func() {
for {
counter += 1
select {
case <-srv.ctx.Done():
srv.wg.Done()
srv.logg.Infof("Log file rotator done")
return
default:
}
if (counter % 60) == 1 {
stat, err := srv.logf.Stat()
if err == nil && stat.Size() > srv.conf.LogLimit {
srv.logg.Infof("Rotate log file")
countFiles := 3
for i := 1; i < countFiles; i++ {
nextName := fmt.Sprintf("%s.%d", srv.conf.Logpath, i+1)
prevName := fmt.Sprintf("%s.%d", srv.conf.Logpath, i)
os.Rename(prevName, nextName)
}
os.Rename(srv.conf.Logpath, srv.conf.Logpath+".1")
logFile, err := os.OpenFile(srv.conf.Logpath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0640)
if err == nil {
syscall.Dup2(int(logFile.Fd()), int(os.Stdout.Fd()))
syscall.Dup2(int(logFile.Fd()), int(os.Stderr.Fd()))
srv.logf.Close()
srv.logf = logFile
}
}
}
time.Sleep(1 * time.Second)
}
}
go logFunc()
}
+104
View File
@@ -0,0 +1,104 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package service
import (
"context"
"net"
"net/http"
"time"
"mbase/app/handler"
"mbase/app/logger"
"mbase/app/router"
)
type ServiceParams struct {
Handler *handler.Handler
Listener net.Listener
}
type Service struct {
hand *handler.Handler
rout *router.Router
logg *logger.Logger
listen net.Listener
hsrv *http.Server
}
func NewService(params *ServiceParams) (*Service, error) {
var err error
svc := &Service{
hand: params.Handler,
listen: params.Listener,
}
svc.logg = logger.NewLoggerWithSubject("service")
return svc, err
}
func (svc *Service) Build() error {
var err error
svc.logg.Infof("Service build ")
svc.rout = router.NewRouter()
svc.rout.Use(router.NewRecoveryMiddleware(svc.logg.Errorf))
svc.rout.Use(router.NewLoggingMiddleware(svc.logg.Infof))
svc.rout.Use(router.NewCorsMiddleware())
svc.rout.Use(svc.hand.AuthMiddleware)
svc.rout.Get(`/v3/api/service/hello`, svc.hand.SendHello)
svc.rout.Post(`/v3/api/account/create`, svc.hand.CreateAccount)
svc.rout.Post(`/v3/api/account/get`, svc.hand.GetAccount)
svc.rout.Post(`/v3/api/account/update`, svc.hand.UpdateAccount)
svc.rout.Post(`/v3/api/account/delete`, svc.hand.DeleteAccount)
svc.rout.Post(`/v3/api/accounts/list`, svc.hand.ListAccounts)
svc.rout.Post(`/v3/api/grant/create`, svc.hand.CreateGrant)
svc.rout.Post(`/v3/api/grant/get`, svc.hand.GetGrant)
svc.rout.Post(`/v3/api/grant/update`, svc.hand.UpdateGrant)
svc.rout.Post(`/v3/api/grant/delete`, svc.hand.DeleteGrant)
svc.rout.Post(`/v3/api/grants/list`, svc.hand.ListGrants)
svc.rout.NotFound(svc.hand.NotFound)
selector := svc.rout.Selector()
for _, item := range selector.Routes {
svc.logg.Infof("%s\t%s", item.Method, item.RawPath)
}
svc.logg.Infof("Service listening at %v", svc.listen.Addr())
svc.hsrv = &http.Server{
Handler: svc.rout,
}
return err
}
func (svc *Service) Run() error {
var err error
svc.logg.Infof("Service run")
err = svc.hsrv.Serve(svc.listen)
if err == http.ErrServerClosed {
svc.logg.Warningf("Service Closed")
err = nil
}
if err != nil {
return err
}
return err
}
func (svc *Service) Stop() {
if svc.hsrv != nil {
svc.logg.Infof("Service stop")
downWaiting := 10 * time.Second
ctx, cancel := context.WithTimeout(context.Background(), downWaiting)
defer cancel()
err := svc.hsrv.Shutdown(ctx)
if err != nil {
svc.logg.Errorf("Error service shutdown: %v", err)
}
}
}
+27
View File
@@ -0,0 +1,27 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package servoper
import (
"mbase/app/logger"
"mbase/app/maindb"
)
type OperatorParams struct {
MainDB *maindb.Database
}
type Operator struct {
mdb *maindb.Database
logg *logger.Logger
}
func NewOperator(params *OperatorParams) (*Operator, error) {
var err error
oper := &Operator{
mdb: params.MainDB,
}
oper.logg = logger.NewLoggerWithSubject("servoper")
return oper, err
}
+20
View File
@@ -0,0 +1,20 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package servoper
type SendHelloParams struct{}
type SendHelloResult struct {
Message string `json:"message"`
Alive bool `json:"alive"`
}
func (oper *Operator) SendHello(param *SendHelloParams) (*SendHelloResult, error) {
var err error
res := &SendHelloResult{
Alive: true,
Message: "hello",
}
return res, err
}
+3
View File
@@ -0,0 +1,3 @@
*~
Chart.yaml
values.yaml
+2
View File
@@ -0,0 +1,2 @@
*.in
*~
+7
View File
@@ -0,0 +1,7 @@
apiVersion: v1
name: @PACKAGE_NAME@
description: mbase service
type: application
version: "@PACKAGE_VERSION@"
appVersion: "@PACKAGE_VERSION@"
+9
View File
@@ -0,0 +1,9 @@
{{- define "mstore.imagePath" -}}
{{- if .Values.global -}}
{{- if .Values.global.imagePath -}}
{{- .Values.global.imagePath -}}
{{- end -}}
{{- else -}}
{{- .Values.main.image.path -}}
{{- end -}}
{{- end -}}
+10
View File
@@ -0,0 +1,10 @@
{{- define "mstore.servicePort" -}}
{{- if .Values.global -}}
{{- if .Values.global.mstorePort -}}
{{- .Values.global.mstorePort -}}
{{- end -}}
{{- else -}}
{{- .Values.main.service.port -}}
{{- end -}}
{{- end -}}
+10
View File
@@ -0,0 +1,10 @@
{{- define "mstore.storageClass" -}}
{{- if .Values.global -}}
{{- if .Values.global.storageClass -}}
{{- .Values.global.storageClass -}}
{{- end -}}
{{- else -}}
{{- .Values.main.storageClass -}}
{{- end -}}
{{- end -}}
+10
View File
@@ -0,0 +1,10 @@
{{- define "mstore.storageSize" -}}
{{- if .Values.global -}}
{{- if .Values.global.mstoreStorageSize -}}
{{- .Values.global.mstoreStorageSize -}}
{{- end -}}
{{- else -}}
{{- .Values.main.storageSize -}}
{{- end -}}
{{- end -}}
+31
View File
@@ -0,0 +1,31 @@
{{- define "mstore.username" -}}
{{- if .Values.global -}}
{{- if .Values.global.mstoreUsername -}}
{{- .Values.global.mstoreUsername -}}
{{- else -}}
{{- fail "Istore username is empty, fill it in global values.yaml !" -}}
{{- end -}}
{{- else -}}
{{- if .Values.main.auth.username -}}
{{- .Values.main.auth.username -}}
{{- else -}}
{{- fail "Istore username is empty, fill it in values.yaml !" -}}
{{- end }}
{{- end -}}
{{- end -}}
{{- define "mstore.password" -}}
{{- if .Values.global -}}
{{- if .Values.global.mstoreUserpass -}}
{{- .Values.global.mstoreUserpass -}}
{{- else -}}
{{- fail "Istore password is empty, fill it in global values.yaml !"}}
{{- end -}}
{{- else -}}
{{- if .Values.main.auth.password -}}
{{- .Values.main.auth.password -}}
{{- else -}}
{{- fail "Istore password is empty, fill it in values.yaml !"}}
{{- end -}}
{{- end -}}
{{- end -}}
+11
View File
@@ -0,0 +1,11 @@
apiVersion: v1
kind: ConfigMap
metadata:
namespace: {{ .Release.Namespace }}
name: mstored-config
data:
mstored.yaml: |-
service:
address: 0.0.0.0
port: {{ include "mstore.servicePort" . }}
asDaemon: false
+37
View File
@@ -0,0 +1,37 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: mstore
namespace: {{ .Release.Namespace }}
spec:
replicas: 1
selector:
matchLabels:
app: mstore
template:
metadata:
labels:
app: mstore
spec:
restartPolicy: Always
containers:
- securityContext:
privileged: true
image: {{ include "mstore.imagePath" . }}/{{ .Values.main.image.name }}:{{ .Values.main.image.tag }}
imagePullPolicy: {{ .Values.main.image.imagePullPolicy }}
name: mstore
ports:
- containerPort: {{ include "mstore.servicePort" . }}
protocol: TCP
volumeMounts:
- name: config-volume
mountPath: /app/etc/mstore
# - name: db-volume
# mountPath: /var/lib
volumes:
- name: config-volume
configMap:
name: mstored-config
# - name: db-volume
# persistentVolumeClaim:
# claimName: mstore-data
+14
View File
@@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: mstore
namespace: {{ .Release.Namespace }}
spec:
selector:
app: mstore
ports:
- port: {{ include "mstore.servicePort" . }}
protocol: TCP
targetPort: {{ include "mstore.servicePort" . }}
name: api
type: {{ .Values.main.service.type }}
+16
View File
@@ -0,0 +1,16 @@
main:
image:
path: t14x.unix7.org/mstore
name: "@PACKAGE_NAME@"
tag: "@PACKAGE_VERSION@"
imagePullPolicy: Always
service:
type: LoadBalancer
port: 1025
# x509Cert: xxx
# x509Key: xxx
hostname: "localhost"
daemon: false
storageClass: local-path
storageSize: 10Gi
Vendored Executable
+1815
View File
File diff suppressed because it is too large Load Diff
Vendored Executable
+2354
View File
File diff suppressed because it is too large Load Diff
Vendored Executable
+5141
View File
File diff suppressed because it is too large Load Diff
+258
View File
@@ -0,0 +1,258 @@
AC_INIT([mbase],[0.2.5])
AM_INIT_AUTOMAKE([foreign subdir-objects tar-pax])
AC_PREFIX_DEFAULT(/usr/local)
PACKAGE=mstore
AC_CHECK_PROG(HAVE_GO, go, true, false)
if test "x$HAVE_GO" = "xfalse"; then
AC_MSG_ERROR([Requested program go not found])
fi
AC_PATH_PROG([GO],[go])
AC_PATH_PROG([FIND],[find])
AC_PATH_PROG([XARGS],[xargs])
AC_PATH_PROG([SORT],[sort])
AC_PATH_PROG([UNIQ],[uniq])
AC_PATH_PROGS([SUDO],[sudo false])
AC_PATH_PROGS([CPIO],[cpio false])
AC_PATH_PROGS([BASENAME],[basename])
AC_PATH_PROGS([PODMAN],[podman true])
AC_PATH_PROGS([HELM],[helm true])
AC_PATH_PROGS([DBUILDPACKAGE],[dpkg-buildpackage true])
AC_PATH_PROGS([DPKGSOURCE],[dpkg-source true])
AC_PATH_PROGS([SWAG],[swag true])
AC_PATH_PROGS([CP],[gcp cp])
if test -z "$CP"; then
AC_MSG_ERROR([Requested program cp not found])
fi
AC_PROG_INSTALL
AC_PROG_MKDIR_P
AC_CANONICAL_HOST
case $host_os in
*freebsd* )
AC_SUBST(ROOT_GROUP, "wheel")
AM_CONDITIONAL(FREEBSD_OS, true)
AM_CONDITIONAL(LINUX_OS, false)
OSNAME=freebsd
ROOT_GROUP=wheel
;;
*linux* )
AC_SUBST(ROOT_GROUP, "root")
AM_CONDITIONAL(FREEBSD_OS, false)
AM_CONDITIONAL(LINUX_OS, true)
OSNAME=linux
ROOT_GROUP=root
;;
esac
AM_CONDITIONAL(SYSTEMD, false)
if test -d /lib/systemd/system; then
AM_CONDITIONAL(SYSTEMD, true)
fi
test "x$prefix" == "xNONE" && prefix=$ac_default_prefix
test "x$libexecdir" == "xNONE" && libexecdir=${prefix}/lib
dnl --------------------------------------------------------------------------------------
AC_ARG_ENABLE([devel-mode],
AS_HELP_STRING([--enable-devel-mode], [Enable developmend mode]))
AC_DEFINE_UNQUOTED(srv_devel_mode, "false", [developmend mode])
AC_SUBST(srv_devel_mode, "false")
AS_IF([test "x$enable_devel_mode" = "xyes"], [
SRCDIR=`pwd`
enable_devel_mode=yes
])
dnl --------------------------------------------------------------------------------------
SRV_CONFDIR="${prefix}/etc/${PACKAGE}"
AC_ARG_WITH(confdir,
AS_HELP_STRING([--with-confdir=PATH],[set configuration dir to PATH (default: $SRV_CONFDIR)]),
[ if test ! -z "$withval" ; then
case $withval in
/*)
SRV_CONFDIR="$withval"
;;
*)
AC_MSG_ERROR(You must specify an absolute path to --with-confdir=PATH)
;;
esac
fi ])
AS_IF([test "x$enable_devel_mode" = "xyes"], [
SRV_CONFDIR="${SRCDIR}/etc/${PACKAGE}"
sysconfdir="${SRCDIR}/etc/${PACKAGE}"
], [
test "x$SRV_CONFDIR" == "x/usr/etc/${PACKAGE}" && SRV_CONFDIR="/etc/${PACKAGE}"
test "x$prefix" == "x/usr" && sysconfdir="/etc"
])
AC_DEFINE_UNQUOTED(SRV_CONFDIR, "$SRV_CONFDIR", [location of configuration files for ${PACKAGE}])
AC_SUBST(srv_confdir, "$SRV_CONFDIR")
AC_MSG_NOTICE(srv_confdir set as ${SRV_CONFDIR})
dnl --------------------------------------------------------------------------------------
SRV_LOGDIR="/var/log/${PACKAGE}"
AC_ARG_WITH(logdir,
AS_HELP_STRING([--with-logdir=PATH],[set path for logdir (default: $SRV_LOGDIR)]),
[ if test ! -z "$withval" ; then
case $withval in
/*)
SRV_LOGDIR="$withval"
;;
*)
AC_MSG_ERROR(You must specify an absolute path to --with-logdir=PATH)
;;
esac
fi])
AS_IF([test "x$enable_devel_mode" = "xyes"], [
SRV_LOGDIR="${SRCDIR}/tmp/log"
])
AC_DEFINE_UNQUOTED(SRV_LOGDIR, "$SRV_LOGDIR", [location of ${PACKAGE} logdir])
AC_SUBST(srv_logdir, "$SRV_LOGDIR")
AC_MSG_NOTICE(srv_logdir set as ${SRV_LOGDIR})
dnl --------------------------------------------------------------------------------------
SRV_RUNDIR="/var/run/${PACKAGE}"
AC_ARG_WITH(rundir,
AS_HELP_STRING([--with-rundir=PATH],[set path for rundir (default: $SRV_RUNDIR)]),
[ if test ! -z "$withval" ; then
case $withval in
/*)
SRV_RUNDIR="$withval"
;;
*)
AC_MSG_ERROR(You must specify an absolute path to --with-rundir=PATH)
;;
esac
fi ])
AS_IF([test "x$enable_devel_mode" = "xyes"], [
SRV_RUNDIR="${SRCDIR}/tmp/run"
])
AC_DEFINE_UNQUOTED(SRV_RUNDIR, "$SRV_RUNDIR", [location of rundir])
AC_SUBST(srv_rundir, "$SRV_RUNDIR")
AC_MSG_NOTICE(srv_rundir set as ${SRV_RUNDIR})
dnl --------------------------------------------------------------------------------------
SRV_SHAREDIR="${prefix}/share/$PACKAGE"
AC_ARG_WITH(sharedir,
AS_HELP_STRING([--with-sharedir=PATH],[set share directory (default: $SRV_SHAREDIR)]),
[ if test ! -z "$with_sharedir" ; then
case $with_sharedir in
/*)
SRV_SHAREDIR="$withval"
;;
*)
AC_MSG_ERROR(You must specify an absolute path to --with-sharedir=PATH)
;;
esac
fi ])
AS_IF([test "x$enable_devel_mode" = "xyes"], [
SRV_SHAREDIR="${SRCDIR}/share"
])
AC_DEFINE_UNQUOTED(SRV_SHAREDIR, "$SRV_SHAREDIR", [location of share dir])
AC_SUBST(srv_sharedir, "$SRV_SHAREDIR")
AC_MSG_NOTICE(srv_sharedir set as ${SRV_SHAREDIR})
dnl --------------------------------------------------------------------------------------
SRV_LIBDIR="${prefix}/lib/$PACKAGE"
AC_ARG_WITH(libdir,
AS_HELP_STRING([--with-libdir=PATH],[set lib directory (default: $SRV_LIBDIR)]),
[ if test ! -z "$withval" ; then
case $withval in
/*)
SRV_LIBDIR="$withval"
;;
*)
AC_MSG_ERROR(You must specify an absolute path to --with-libdir=PATH)
;;
esac
fi ])
AS_IF([test "x$enable_devel_mode" = "xyes"], [
SRV_LIBDIR="${SRCDIR}/sysagent"
])
AC_DEFINE_UNQUOTED(SRV_LIBDIR, "$SRV_LIBDIR", [location of lib dir])
AC_SUBST(srv_libdir, "$SRV_LIBDIR")
AC_MSG_NOTICE(srv_libdir set as ${SRV_LIBDIR})
dnl --------------------------------------------------------------------------------------
SRV_DATADIR="/var/lib/${PACKAGE}"
AC_ARG_WITH(datadir,
AS_HELP_STRING([--with-datadir=PATH],[set path for datadir (default: $SRV_DATADIR)]),
[ if test ! -z "$withval" ; then
case $withval in
/*)
SRV_DATADIR="$withval"
;;
*)
AC_MSG_ERROR(You must specify an absolute path to --with-datadir=PATH)
;;
esac
fi])
AS_IF([test "x$enable_devel_mode" = "xyes"], [
SRV_DATADIR="${SRCDIR}/tmp/data"
])
AC_DEFINE_UNQUOTED(SRV_DATADIR, "$SRV_DATADIR", [location of ${PACKAGE} datadir])
AC_SUBST(srv_datadir, "$SRV_DATADIR")
AC_MSG_NOTICE(srv_datadir set as ${SRV_DATADIR})
dnl --------------------------------------------------------------------------------------
AM_EXTRA_RECURSIVE_TARGETS([docs])
AC_CONFIG_FILES([
Makefile
app/config/variant.go
initrc/mstored.service
initrc/mstored
chart/Chart.yaml
chart/values.yaml
debian/control
debian/changelog
mans/Makefile
])
AC_OUTPUT
+4
View File
@@ -0,0 +1,4 @@
changelog
control
*~
tmp
+1
View File
@@ -0,0 +1 @@
mbase for Debian
+5
View File
@@ -0,0 +1,5 @@
mbase (@PACKAGE_VERSION@-1) unstable; urgency=low
* Release.
-- Oleg Borodin <borodin@unix7.org> Sun, 29 Oct 2017 18:57:56 +0200
+1
View File
@@ -0,0 +1 @@
10
+15
View File
@@ -0,0 +1,15 @@
Source: mbase
Section: utils
Priority: extra
Maintainer: borodin@unix7.org
Build-Depends: debhelper (>=8)
Standards-Version: @PACKAGE_VERSION@
Homepage: http://wiki.unix7.org
Package: mbase-control
Description: mbase
Architecture: amd64
Package: mbase-service
Description: mbase
Architecture: amd64
+5
View File
@@ -0,0 +1,5 @@
mstore-control-dbgsym_2.11.0-1_amd64.deb debug optional automatic=yes
mstore-control_2.11.0-1_amd64.deb utils extra
mstore-service-dbgsym_2.11.0-1_amd64.deb debug optional automatic=yes
mstore-service_2.11.0-1_amd64.deb utils extra
mstore_2.11.0-1_amd64.buildinfo utils extra
+2
View File
@@ -0,0 +1,2 @@
usr/bin/mstorectl
+2
View File
@@ -0,0 +1,2 @@
#!/bin/sh -e
Vendored Executable
+2
View File
@@ -0,0 +1,2 @@
#!/bin/sh -e
Vendored Executable
+20
View File
@@ -0,0 +1,20 @@
#!/bin/sh
set -e
case "$1" in
install|upgrade)
;;
abort-upgrade)
;;
*)
echo "preinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
#DEBHELPER#
exit 0
Vendored Executable
+2
View File
@@ -0,0 +1,2 @@
#!/bin/sh -e
+6
View File
@@ -0,0 +1,6 @@
usr/sbin/mstored
lib/systemd/system/mstored.service
var/run/mstore
var/log/mstore
etc/mstore
+6
View File
@@ -0,0 +1,6 @@
#!/bin/sh -e
if [ "$1" = "configure" ] ; then
# Initial installation
systemctl preset mstored.service >/dev/null 2>&1 || :
fi
Vendored Executable
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh -e
systemctl daemon-reload >/dev/null 2>&1 || :
Vendored Executable
+20
View File
@@ -0,0 +1,20 @@
#!/bin/sh
set -e
case "$1" in
install|upgrade)
;;
abort-upgrade)
;;
*)
echo "preinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
#DEBHELPER#
exit 0
Vendored Executable
+7
View File
@@ -0,0 +1,7 @@
#!/bin/sh -e
if [ "$1" = "remove" ] || [ "$1" = "purge" ]; then
# Package removal, not upgrade
systemctl --no-reload disable mstored.service > /dev/null 2>&1 || :
systemctl stop mstored.service > /dev/null 2>&1 || :
fi
+1
View File
@@ -0,0 +1 @@
# You must remove unused comment lines for the released package.
Vendored Executable
+13
View File
@@ -0,0 +1,13 @@
#!/usr/bin/make -f
export GO111MODULE=on
export GOPROXY=https://proxy.golang.org
%:
dh $@
override_dh_auto_configure:
./configure --prefix=/usr
#EOF
+1
View File
@@ -0,0 +1 @@
3.0 (quilt)
View File
+1
View File
@@ -0,0 +1 @@
version=3
+22
View File
@@ -0,0 +1,22 @@
MimeType: application/vnd.oci.image.manifest.v1+json
{
"schemaVersion": 2,
"config": {
"mediaType": "application/vnd.cncf.helm.config.v1+json",
"digest": "sha256:33e3759271a3c6dc018541cfb73653c267df6f8ee28ce68e38c72e22196a1d87",
"size": 126
},
"layers": [{
"mediaType": "application/vnd.cncf.helm.chart.content.v1.tar+gzip",
"digest": "sha256:93dc97d49c453a7ce3af622cf7ef698548045e0c09907c6a021018f6eac6c31a",
"size": 1528
}],
"annotations": {
"org.opencontainers.image.created": "2026-02-25T19:59:44+02:00",
"org.opencontainers.image.description": "mStore service",
"org.opencontainers.image.title": "mstore",
"org.opencontainers.image.version": "0.2.0"
}
}
+85
View File
@@ -0,0 +1,85 @@
<mxfile host="Electron" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/27.0.9 Chrome/134.0.6998.205 Electron/35.4.0 Safari/537.36" version="27.0.9">
<diagram name="Page-1" id="bilFRRFyBUGthtbaxYIo">
<mxGraphModel dx="1880" dy="566" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" background="#ffffff" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="t6Klk0bs08zzC_b7PXua-28" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#F0FDFF;glass=0;strokeColor=#0e8088;" vertex="1" parent="1">
<mxGeometry x="-10" y="55" width="120" height="335" as="geometry" />
</mxCell>
<mxCell id="t6Klk0bs08zzC_b7PXua-26" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#F0FDFF;glass=0;strokeColor=#0e8088;" vertex="1" parent="1">
<mxGeometry x="120" y="50" width="630" height="350" as="geometry" />
</mxCell>
<mxCell id="t6Klk0bs08zzC_b7PXua-5" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;curved=0;" edge="1" parent="1" source="t6Klk0bs08zzC_b7PXua-1" target="t6Klk0bs08zzC_b7PXua-2">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="t6Klk0bs08zzC_b7PXua-30" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;curved=0;exitX=0.75;exitY=0;exitDx=0;exitDy=0;" edge="1" parent="1" source="t6Klk0bs08zzC_b7PXua-2" target="t6Klk0bs08zzC_b7PXua-29">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="t6Klk0bs08zzC_b7PXua-2" value="service" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="250" y="230" width="80" height="60" as="geometry" />
</mxCell>
<mxCell id="t6Klk0bs08zzC_b7PXua-11" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;curved=0;strokeColor=#000099;exitX=0.75;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="1" source="t6Klk0bs08zzC_b7PXua-3" target="t6Klk0bs08zzC_b7PXua-8">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="t6Klk0bs08zzC_b7PXua-19" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;strokeColor=#0000CC;curved=0;" edge="1" parent="1" source="t6Klk0bs08zzC_b7PXua-20" target="t6Klk0bs08zzC_b7PXua-17">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="t6Klk0bs08zzC_b7PXua-3" value="handler" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#b1ddf0;strokeColor=#10739e;" vertex="1" parent="1">
<mxGeometry x="390" y="150" width="90" height="60" as="geometry" />
</mxCell>
<mxCell id="t6Klk0bs08zzC_b7PXua-7" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.75;entryDx=0;entryDy=0;curved=0;strokeColor=#000099;" edge="1" parent="1" source="t6Klk0bs08zzC_b7PXua-6" target="t6Klk0bs08zzC_b7PXua-2">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="t6Klk0bs08zzC_b7PXua-6" value="client" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#fad7ac;strokeColor=#b46504;" vertex="1" parent="1">
<mxGeometry x="10" y="240" width="90" height="60" as="geometry" />
</mxCell>
<mxCell id="t6Klk0bs08zzC_b7PXua-12" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;curved=0;strokeColor=#000099;" edge="1" parent="1" source="t6Klk0bs08zzC_b7PXua-8" target="t6Klk0bs08zzC_b7PXua-9">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="t6Klk0bs08zzC_b7PXua-18" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;curved=0;" edge="1" parent="1" source="t6Klk0bs08zzC_b7PXua-8" target="t6Klk0bs08zzC_b7PXua-17">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="t6Klk0bs08zzC_b7PXua-23" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;curved=0;" edge="1" parent="1" source="t6Klk0bs08zzC_b7PXua-8" target="t6Klk0bs08zzC_b7PXua-10">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="t6Klk0bs08zzC_b7PXua-8" value="operator" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#b1ddf0;strokeColor=#10739e;" vertex="1" parent="1">
<mxGeometry x="500" y="230" width="90" height="60" as="geometry" />
</mxCell>
<mxCell id="t6Klk0bs08zzC_b7PXua-9" value="storage" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#b0e3e6;strokeColor=#0e8088;" vertex="1" parent="1">
<mxGeometry x="570" y="300" width="90" height="50" as="geometry" />
</mxCell>
<mxCell id="t6Klk0bs08zzC_b7PXua-10" value="lockers" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#bac8d3;strokeColor=#23445d;" vertex="1" parent="1">
<mxGeometry x="550" y="150" width="70" height="40" as="geometry" />
</mxCell>
<mxCell id="t6Klk0bs08zzC_b7PXua-16" value="starter" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1">
<mxGeometry x="140" y="110" width="80" height="50" as="geometry" />
</mxCell>
<mxCell id="t6Klk0bs08zzC_b7PXua-33" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=1;exitY=0.25;exitDx=0;exitDy=0;curved=0;" edge="1" parent="1" source="t6Klk0bs08zzC_b7PXua-1" target="t6Klk0bs08zzC_b7PXua-32">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="t6Klk0bs08zzC_b7PXua-1" value="server" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1">
<mxGeometry x="150" y="145" width="80" height="55" as="geometry" />
</mxCell>
<mxCell id="t6Klk0bs08zzC_b7PXua-17" value="database" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#b0e3e6;strokeColor=#0e8088;" vertex="1" parent="1">
<mxGeometry x="630" y="220" width="90" height="50" as="geometry" />
</mxCell>
<mxCell id="t6Klk0bs08zzC_b7PXua-20" value="auth" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#b1ddf0;strokeColor=#10739e;" vertex="1" parent="1">
<mxGeometry x="420" y="120" width="50" height="40" as="geometry" />
</mxCell>
<mxCell id="t6Klk0bs08zzC_b7PXua-31" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;curved=0;" edge="1" parent="1" source="t6Klk0bs08zzC_b7PXua-29" target="t6Klk0bs08zzC_b7PXua-3">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="t6Klk0bs08zzC_b7PXua-29" value="router" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#b1ddf0;strokeColor=#10739e;" vertex="1" parent="1">
<mxGeometry x="290" y="140" width="70" height="50" as="geometry" />
</mxCell>
<mxCell id="t6Klk0bs08zzC_b7PXua-32" value="config" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#b0e3e6;strokeColor=#0e8088;" vertex="1" parent="1">
<mxGeometry x="290" y="70" width="70" height="40" as="geometry" />
</mxCell>
<mxCell id="t6Klk0bs08zzC_b7PXua-34" value="Feb 2026&lt;div&gt;O Borodin&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeColor=default;" vertex="1" parent="1">
<mxGeometry x="510" y="430" width="110" height="40" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

+32
View File
@@ -0,0 +1,32 @@
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:2f99832d1fc23ae7c8a35d5aa0b9d7a78a1876d69f9453d5d6e8701ac86c171c",
"size": 1357
},
"layers": [{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:7319607d119c259e90382101fb3ed98ad62e29ed817338183b3775dcfb358140",
"size": 3731518
}, {
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:1fccc5cb3c84a43c9dec4d89f1383471b41e1c4b9d4f271e3c952839f76634f5",
"size": 15791440
}, {
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:8cc3f2f15e1a38f6b733038dd9b3b27a88d4882a78a3235416f792dd9deca7af",
"size": 173
}],
"annotations": {
"com.docker.official-images.bashbrew.arch": "amd64",
"org.opencontainers.image.base.digest": "sha256:b0cb30c51c47cdfde647364301758b14c335dea2fddc9490d4f007d67ecb2538",
"org.opencontainers.image.base.name": "docker.io/library/alpine:3.20",
"org.opencontainers.image.created": "2026-01-28T01:18:13Z",
"org.opencontainers.image.revision": "b3f87708e5052e29737a251b2e9865e182dafe0c",
"org.opencontainers.image.source": "https://github.com/alpinelinux/docker-alpine.git#b3f87708e5052e29737a251b2e9865e182dafe0c:x86_64",
"org.opencontainers.image.url": "https://hub.docker.com/_/alpine",
"org.opencontainers.image.version": "3.20.9"
}
}
+54
View File
@@ -0,0 +1,54 @@
-----BEGIN CERTIFICATE-----
MIIEojCCBCmgAwIBAgISBpbPr7WFeCJ3U8QDioKGHkQiMAoGCCqGSM49BAMDMDIx
CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQDEwJF
ODAeFw0yNjAyMTIwOTExMDVaFw0yNjA1MTMwOTExMDRaMBgxFjAUBgNVBAMTDWRl
di51bml4Ny5vcmcwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATZnNwUA6M5kqmq
36oRkUTC+fSPlLiLDs3Vz6fPdYBWIokPKG+UqAS4WjfXXQEPwqGeoef0b5elB7dg
6GjwOhJio4IDNzCCAzMwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUF
BwMBMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFBme/mBwOaT6f4yzzCBc4i/NPQBY
MB8GA1UdIwQYMBaAFI8NE6L2Ln7RUGwzGDhdWY4jcpHKMDIGCCsGAQUFBwEBBCYw
JDAiBggrBgEFBQcwAoYWaHR0cDovL2U4LmkubGVuY3Iub3JnLzCCATUGA1UdEQSC
ASwwggEogg1haXIudW5peDcub3JnghJhaXJmb3JjZS51bml4Ny5vcmeCDWRldi51
bml4Ny5vcmeCDWVkdS51bml4Ny5vcmeCEGVkdW1heC51bml4Ny5vcmeCDWdpdC51
bml4Ny5vcmeCDWhhbS51bml4Ny5vcmeCDmhhc2gudW5peDcub3JnggxoZC51bml4
Ny5vcmeCDm1haWwudW5peDcub3Jnggxtdy51bml4Ny5vcmeCDG14LnVuaXg3Lm9y
Z4IOcGlraS51bml4Ny5vcmeCDHJtLnVuaXg3Lm9yZ4IQc3RvcmV4LnVuaXg3Lm9y
Z4INdzEyLnVuaXg3Lm9yZ4INdzEzLnVuaXg3Lm9yZ4IOd2lraS51bml4Ny5vcmeC
DXd3dy51bml4Ny5vcmcwEwYDVR0gBAwwCjAIBgZngQwBAgEwLQYDVR0fBCYwJDAi
oCCgHoYcaHR0cDovL2U4LmMubGVuY3Iub3JnLzM1LmNybDCCAQsGCisGAQQB1nkC
BAIEgfwEgfkA9wB1AGQRxGykEuyniRyiAi4AvKtPKAfUHjUnq+r+1QPJfc3wAAAB
nFFT2U0AAAQDAEYwRAIgbuNiYUqwgxi9Oycm2D6h8sclWLcBXHtqFWpwJT3dwbgC
IBBjMRcAmxZKE9PIgFqMvosPNQ7AAkbEfudVtqNkfwXJAH4Apcl4kl1XRheChw3Y
iWYLXFVki30AQPLsB2hR0YhpGfcAAAGcUVPcHQAIAAAFADH4c7UEAwBHMEUCIQDz
p9aYuw9jAZjxM8U3Qq7M4vFpp0GZDm80tpTdKYNjjwIgQ/e/lWg6uqQsjBytcaOG
IVXPEyB0n8fflOf+Ekh51DUwCgYIKoZIzj0EAwMDZwAwZAIwRrEAdYBcRU46Pold
w4VjUGnGsYEUqlsjrmn4awY01YGWknPaX5sTAI3hTuA32eOUAjA1dSANAAyO+icj
1ZiPZUGFjaMdkEu6Y/Ge8aueAUItNj9eF0ahlgZEnSlhHrswoZo=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEVjCCAj6gAwIBAgIQY5WTY8JOcIJxWRi/w9ftVjANBgkqhkiG9w0BAQsFADBP
MQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFy
Y2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTAeFw0yNDAzMTMwMDAwMDBa
Fw0yNzAzMTIyMzU5NTlaMDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBF
bmNyeXB0MQswCQYDVQQDEwJFODB2MBAGByqGSM49AgEGBSuBBAAiA2IABNFl8l7c
S7QMApzSsvru6WyrOq44ofTUOTIzxULUzDMMNMchIJBwXOhiLxxxs0LXeb5GDcHb
R6EToMffgSZjO9SNHfY9gjMy9vQr5/WWOrQTZxh7az6NSNnq3u2ubT6HTKOB+DCB
9TAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMB
MBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFI8NE6L2Ln7RUGwzGDhdWY4j
cpHKMB8GA1UdIwQYMBaAFHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEB
BCYwJDAiBggrBgEFBQcwAoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzATBgNVHSAE
DDAKMAgGBmeBDAECATAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8veDEuYy5sZW5j
ci5vcmcvMA0GCSqGSIb3DQEBCwUAA4ICAQBnE0hGINKsCYWi0Xx1ygxD5qihEjZ0
RI3tTZz1wuATH3ZwYPIp97kWEayanD1j0cDhIYzy4CkDo2jB8D5t0a6zZWzlr98d
AQFNh8uKJkIHdLShy+nUyeZxc5bNeMp1Lu0gSzE4McqfmNMvIpeiwWSYO9w82Ob8
otvXcO2JUYi3svHIWRm3+707DUbL51XMcY2iZdlCq4Wa9nbuk3WTU4gr6LY8MzVA
aDQG2+4U3eJ6qUF10bBnR1uuVyDYs9RhrwucRVnfuDj29CMLTsplM5f5wSV5hUpm
Uwp/vV7M4w4aGunt74koX71n4EdagCsL/Yk5+mAQU0+tue0JOfAV/R6t1k+Xk9s2
HMQFeoxppfzAVC04FdG9M+AC2JWxmFSt6BCuh3CEey3fE52Qrj9YM75rtvIjsm/1
Hl+u//Wqxnu1ZQ4jpa+VpuZiGOlWrqSP9eogdOhCGisnyewWJwRQOqK16wiGyZeR
xs/Bekw65vwSIaVkBruPiTfMOo0Zh4gVa8/qJgMbJbyrwwG97z/PRgmLKCDl8z3d
tA0Z7qq7fta0Gl24uyuB05dqI5J1LvAzKuWdIjT1tP8qCoxSE/xpix8hX2dt3h+/
jujUgFPFZ0EVZ0xSyBNRF3MboGZnYXFUxpNjTWPKpagDHJQmqrAcDmWJnMsFY3jS
u1igv3OefnWjSQ==
-----END CERTIFICATE-----
+5
View File
@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIOqwKQF1VWp3lSiIgD5E1ZNGXf/IlBLwEp8F+YooRiuGoAoGCCqGSM49
AwEHoUQDQgAE2ZzcFAOjOZKpqt+qEZFEwvn0j5S4iw7N1c+nz3WAViKJDyhvlKgE
uFo3110BD8KhnqHn9G+XpQe3YOho8DoSYg==
-----END EC PRIVATE KEY-----
+4
View File
@@ -0,0 +1,4 @@
certpath: mbased.crt
keypath: mbased.key
+118
View File
@@ -0,0 +1,118 @@
module mbase
go 1.25.7
require (
github.com/dustin/go-humanize v1.0.1
github.com/google/go-containerregistry v0.21.3
github.com/google/uuid v1.6.0
github.com/jmoiron/sqlx v1.4.0
github.com/mattn/go-sqlite3 v1.14.38
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.1
github.com/spf13/cobra v1.10.2
github.com/spf13/viper v1.21.0
github.com/stretchr/testify v1.11.1
go.yaml.in/yaml/v4 v4.0.0-rc.4
helm.sh/helm/v4 v4.1.3
sigs.k8s.io/yaml v1.6.0
)
require (
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/Masterminds/semver/v3 v3.4.0 // indirect
github.com/ProtonMail/go-crypto v1.3.0 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/cloudflare/circl v1.6.3 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.18.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/docker/cli v29.3.0+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.9.3 // indirect
github.com/dylibso/observe-sdk/go v0.0.0-20240819160327-2d926c5d788a // indirect
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
github.com/extism/go-sdk v1.7.1 // indirect
github.com/fluxcd/cli-utils v0.37.2-flux.1 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/go-errors/errors v1.5.1 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-openapi/jsonpointer v0.21.1 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/swag v0.23.1 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/gofrs/flock v0.13.0 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/gnostic-models v0.7.0 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
github.com/ianlancetaylor/demangle v0.0.0-20240805132620-81f5be970eca // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.18.4 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/moby/term v0.5.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.11.0 // indirect
github.com/sirupsen/logrus v1.9.4 // indirect
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 // indirect
github.com/tetratelabs/wazero v1.11.0 // indirect
github.com/vbatts/tar-split v0.12.2 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.47.0 // indirect
golang.org/x/net v0.49.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/term v0.39.0 // indirect
golang.org/x/text v0.33.0 // indirect
golang.org/x/time v0.12.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gotest.tools/v3 v3.5.2 // indirect
k8s.io/api v0.35.1 // indirect
k8s.io/apiextensions-apiserver v0.35.1 // indirect
k8s.io/apimachinery v0.35.1 // indirect
k8s.io/cli-runtime v0.35.1 // indirect
k8s.io/client-go v0.35.1 // indirect
k8s.io/component-base v0.35.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect
k8s.io/kubectl v0.35.1 // indirect
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect
oras.land/oras-go/v2 v2.6.0 // indirect
sigs.k8s.io/controller-runtime v0.23.1 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/kustomize/api v0.21.1 // indirect
sigs.k8s.io/kustomize/kyaml v0.21.1 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482 // indirect
)
+384
View File
@@ -0,0 +1,384 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70=
github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk=
github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA=
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
github.com/containerd/stargz-snapshotter/estargz v0.18.2 h1:yXkZFYIzz3eoLwlTUZKz2iQ4MrckBxJjkmD16ynUTrw=
github.com/containerd/stargz-snapshotter/estargz v0.18.2/go.mod h1:XyVU5tcJ3PRpkA9XS2T5us6Eg35yM0214Y+wvrZTBrY=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/distribution/distribution/v3 v3.0.0 h1:q4R8wemdRQDClzoNNStftB2ZAfqOiN6UX90KJc4HjyM=
github.com/distribution/distribution/v3 v3.0.0/go.mod h1:tRNuFoZsUdyRVegq8xGNeds4KLjwLCRin/tTo6i1DhU=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/cli v29.3.0+incompatible h1:z3iWveU7h19Pqx7alZES8j+IeFQZ1lhTwb2F+V9SVvk=
github.com/docker/cli v29.3.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/dylibso/observe-sdk/go v0.0.0-20240819160327-2d926c5d788a h1:UwSIFv5g5lIvbGgtf3tVwC7Ky9rmMFBp0RMs+6f6YqE=
github.com/dylibso/observe-sdk/go v0.0.0-20240819160327-2d926c5d788a/go.mod h1:C8DzXehI4zAbrdlbtOByKX6pfivJTBiV9Jjqv56Yd9Q=
github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU=
github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4=
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc=
github.com/extism/go-sdk v1.7.1 h1:lWJos6uY+tRFdlIHR+SJjwFDApY7OypS/2nMhiVQ9Sw=
github.com/extism/go-sdk v1.7.1/go.mod h1:IT+Xdg5AZM9hVtpFUA+uZCJMge/hbvshl8bwzLtFyKA=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fluxcd/cli-utils v0.37.2-flux.1 h1:tQ588ghtRN+E+kHq415FddfqA9v4brn/1WWgrP6rQR0=
github.com/fluxcd/cli-utils v0.37.2-flux.1/go.mod h1:LcWSu1NYET8d8U7O326RhEm5JkQXCMK6ITu4G1CT02c=
github.com/foxcpp/go-mockdns v1.2.0 h1:omK3OrHRD1IWJz1FuFBCFquhXslXoF17OvBS6JPzZF0=
github.com/foxcpp/go-mockdns v1.2.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=
github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=
github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-containerregistry v0.21.3 h1:Xr+yt3VvwOOn/5nJzd7UoOhwPGiPkYW0zWDLLUXqAi4=
github.com/google/go-containerregistry v0.21.3/go.mod h1:D5ZrJF1e6dMzvInpBPuMCX0FxURz7GLq2rV3Us9aPkc=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 h1:z2ogiKUYzX5Is6zr/vP9vJGqPwcdqsWjOt+V8J7+bTc=
github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII=
github.com/hashicorp/golang-lru/arc/v2 v2.0.5 h1:l2zaLDubNhW4XO3LnliVj0GXO3+/CGNJAg1dcN2Fpfw=
github.com/hashicorp/golang-lru/arc/v2 v2.0.5/go.mod h1:ny6zBSQZi2JxIeYcv7kt2sH2PXJtirBN7RDhRpxPkxU=
github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvHgwfx2Vm4=
github.com/hashicorp/golang-lru/v2 v2.0.5/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/ianlancetaylor/demangle v0.0.0-20240805132620-81f5be970eca h1:T54Ema1DU8ngI+aef9ZhAhNGQhcRTrWxVeG07F+c/Rw=
github.com/ianlancetaylor/demangle v0.0.0-20240805132620-81f5be970eca/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.38 h1:tDUzL85kMvOrvpCt8P64SbGgVFtJB11GPi2AdmITgb4=
github.com/mattn/go-sqlite3 v1.14.38/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/onsi/ginkgo/v2 v2.28.1 h1:S4hj+HbZp40fNKuLUQOYLDgZLwNUVn19N3Atb98NCyI=
github.com/onsi/ginkgo/v2 v2.28.1/go.mod h1:CLtbVInNckU3/+gC8LzkGUb9oF+e8W8TdUsxPwvdOgE=
github.com/onsi/gomega v1.39.0 h1:y2ROC3hKFmQZJNFeGAMeHZKkjBL65mIZcvrLQBF9k6Q=
github.com/onsi/gomega v1.39.0/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
github.com/prometheus/otlptranslator v1.0.0 h1:s0LJW/iN9dkIH+EnhiD3BlkkP5QVIUVEoIwkU+A6qos=
github.com/prometheus/otlptranslator v1.0.0/go.mod h1:vRYWnXvI6aWGpsdY/mOT/cbeVRBlPWtBNDb7kGR3uKM=
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 h1:EaDatTxkdHG+U3Bk4EUr+DZ7fOGwTfezUiUJMaIcaho=
github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5/go.mod h1:fyalQWdtzDBECAQFBJuQe5bzQ02jGd5Qcbgb97Flm7U=
github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb27yVE+gIAfeqp8LUCc=
github.com/redis/go-redis/extra/redisotel/v9 v9.0.5/go.mod h1:WZjPDy7VNzn77AAfnAfVjZNvfJTYfPetfZk5yoSTLaQ=
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 h1:ZF+QBjOI+tILZjBaFj3HgFonKXUcwgJ4djLb6i42S3Q=
github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834/go.mod h1:m9ymHTgNSEjuxvw8E7WWe4Pl4hZQHXONY8wE6dMLaRk=
github.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA=
github.com/tetratelabs/wazero v1.11.0/go.mod h1:eV28rsN8Q+xwjogd7f4/Pp4xFxO7uOGbLcD/LzB1wiU=
github.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CPa4=
github.com/vbatts/tar-split v0.12.2/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/bridges/prometheus v0.65.0 h1:I/7S/yWobR3QHFLqHsJ8QOndoiFsj1VgHpQiq43KlUI=
go.opentelemetry.io/contrib/bridges/prometheus v0.65.0/go.mod h1:jPF6gn3y1E+nozCAEQj3c6NZ8KY+tvAgSVfvoOJUFac=
go.opentelemetry.io/contrib/exporters/autoexport v0.65.0 h1:2gApdml7SznX9szEKFjKjM4qGcGSvAybYLBY319XG3g=
go.opentelemetry.io/contrib/exporters/autoexport v0.65.0/go.mod h1:0QqAGlbHXhmPYACG3n5hNzO5DnEqqtg4VcK5pr22RI0=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0=
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.16.0 h1:ZVg+kCXxd9LtAaQNKBxAvJ5NpMf7LpvEr4MIZqb0TMQ=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.16.0/go.mod h1:hh0tMeZ75CCXrHd9OXRYxTlCAdxcXioWHFIpYw2rZu8=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.16.0 h1:djrxvDxAe44mJUrKataUbOhCKhR3F8QCyWucO16hTQs=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.16.0/go.mod h1:dt3nxpQEiSoKvfTVxp3TUg5fHPLhKtbcnN3Z1I1ePD0=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.40.0 h1:NOyNnS19BF2SUDApbOKbDtWZ0IK7b8FJ2uAGdIWOGb0=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.40.0/go.mod h1:VL6EgVikRLcJa9ftukrHu/ZkkhFBSo1lzvdBC9CF1ss=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.40.0 h1:9y5sHvAxWzft1WQ4BwqcvA+IFVUJ1Ya75mSAUnFEVwE=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.40.0/go.mod h1:eQqT90eR3X5Dbs1g9YSM30RavwLF725Ris5/XSXWvqE=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 h1:DvJDOPmSWQHWywQS6lKL+pb8s3gBLOZUtw4N+mavW1I=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0/go.mod h1:EtekO9DEJb4/jRyN4v4Qjc2yA7AtfCBuz2FynRUWTXs=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 h1:wVZXIWjQSeSmMoxF74LzAnpVQOAFDo3pPji9Y4SOFKc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0/go.mod h1:khvBS2IggMFNwZK/6lEeHg/W57h/IX6J4URh57fuI40=
go.opentelemetry.io/otel/exporters/prometheus v0.62.0 h1:krvC4JMfIOVdEuNPTtQ0ZjCiXrybhv+uOHMfHRmnvVo=
go.opentelemetry.io/otel/exporters/prometheus v0.62.0/go.mod h1:fgOE6FM/swEnsVQCqCnbOfRV4tOnWPg7bVeo4izBuhQ=
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.16.0 h1:ivlbaajBWJqhcCPniDqDJmRwj4lc6sRT+dCAVKNmxlQ=
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.16.0/go.mod h1:u/G56dEKDDwXNCVLsbSrllB2o8pbtFLUC4HpR66r2dc=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.40.0 h1:ZrPRak/kS4xI3AVXy8F7pipuDXmDsrO8Lg+yQjBLjw0=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.40.0/go.mod h1:3y6kQCWztq6hyW8Z9YxQDDm0Je9AJoFar2G0yDcmhRk=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0 h1:MzfofMZN8ulNqobCmCAVbqVL5syHw+eB2qPRkCMA/fQ=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0/go.mod h1:E73G9UFtKRXrxhBsHtG00TB5WxX57lpsQzogDkqBTz8=
go.opentelemetry.io/otel/log v0.16.0 h1:DeuBPqCi6pQwtCK0pO4fvMB5eBq6sNxEnuTs88pjsN4=
go.opentelemetry.io/otel/log v0.16.0/go.mod h1:rWsmqNVTLIA8UnwYVOItjyEZDbKIkMxdQunsIhpUMes=
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
go.opentelemetry.io/otel/sdk/log v0.16.0 h1:e/b4bdlQwC5fnGtG3dlXUrNOnP7c8YLVSpSfEBIkTnI=
go.opentelemetry.io/otel/sdk/log v0.16.0/go.mod h1:JKfP3T6ycy7QEuv3Hj8oKDy7KItrEkus8XJE6EoSzw4=
go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
go.yaml.in/yaml/v4 v4.0.0-rc.4 h1:UP4+v6fFrBIb1l934bDl//mmnoIZEDK0idg1+AIvX5U=
go.yaml.in/yaml/v4 v4.0.0-rc.4/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M=
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo=
gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
helm.sh/helm/v4 v4.1.3 h1:Abfmb+oJUtxoaXDyB2Jhw1zRk3hT6aFfHta+AXb8Lno=
helm.sh/helm/v4 v4.1.3/go.mod h1:5dSo8rRgn3OTkDAc/k0Ipw5/Q+BlqKIKZwa0XwSiINI=
k8s.io/api v0.35.1 h1:0PO/1FhlK/EQNVK5+txc4FuhQibV25VLSdLMmGpDE/Q=
k8s.io/api v0.35.1/go.mod h1:28uR9xlXWml9eT0uaGo6y71xK86JBELShLy4wR1XtxM=
k8s.io/apiextensions-apiserver v0.35.1 h1:p5vvALkknlOcAqARwjS20kJffgzHqwyQRM8vHLwgU7w=
k8s.io/apiextensions-apiserver v0.35.1/go.mod h1:2CN4fe1GZ3HMe4wBr25qXyJnJyZaquy4nNlNmb3R7AQ=
k8s.io/apimachinery v0.35.1 h1:yxO6gV555P1YV0SANtnTjXYfiivaTPvCTKX6w6qdDsU=
k8s.io/apimachinery v0.35.1/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns=
k8s.io/cli-runtime v0.35.1 h1:uKcXFe8J7AMAM4Gm2JDK4mp198dBEq2nyeYtO+JfGJE=
k8s.io/cli-runtime v0.35.1/go.mod h1:55/hiXIq1C8qIJ3WBrWxEwDLdHQYhBNRdZOz9f7yvTw=
k8s.io/client-go v0.35.1 h1:+eSfZHwuo/I19PaSxqumjqZ9l5XiTEKbIaJ+j1wLcLM=
k8s.io/client-go v0.35.1/go.mod h1:1p1KxDt3a0ruRfc/pG4qT/3oHmUj1AhSHEcxNSGg+OA=
k8s.io/component-base v0.35.1 h1:XgvpRf4srp037QWfGBLFsYMUQJkE5yMa94UsJU7pmcE=
k8s.io/component-base v0.35.1/go.mod h1:HI/6jXlwkiOL5zL9bqA3en1Ygv60F03oEpnuU1G56Bs=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE=
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ=
k8s.io/kubectl v0.35.1 h1:zP3Er8C5i1dcAFUMh9Eva0kVvZHptXIn/+8NtRWMxwg=
k8s.io/kubectl v0.35.1/go.mod h1:cQ2uAPs5IO/kx8R5s5J3Ihv3VCYwrx0obCXum0CvnXo=
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck=
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
oras.land/oras-go/v2 v2.6.0 h1:X4ELRsiGkrbeox69+9tzTu492FMUu7zJQW6eJU+I2oc=
oras.land/oras-go/v2 v2.6.0/go.mod h1:magiQDfG6H1O9APp+rOsvCPcW1GD2MM7vgnKY0Y+u1o=
sigs.k8s.io/controller-runtime v0.23.1 h1:TjJSM80Nf43Mg21+RCy3J70aj/W6KyvDtOlpKf+PupE=
sigs.k8s.io/controller-runtime v0.23.1/go.mod h1:B6COOxKptp+YaUT5q4l6LqUJTRpizbgf9KSRNdQGns0=
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/kustomize/api v0.21.1 h1:lzqbzvz2CSvsjIUZUBNFKtIMsEw7hVLJp0JeSIVmuJs=
sigs.k8s.io/kustomize/api v0.21.1/go.mod h1:f3wkKByTrgpgltLgySCntrYoq5d3q7aaxveSagwTlwI=
sigs.k8s.io/kustomize/kyaml v0.21.1 h1:IVlbmhC076nf6foyL6Taw4BkrLuEsXUXNpsE+ScX7fI=
sigs.k8s.io/kustomize/kyaml v0.21.1/go.mod h1:hmxADesM3yUN2vbA5z1/YTBnzLJ1dajdqpQonwBL1FQ=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482 h1:2WOzJpHUBVrrkDjU4KBT8n5LDcj824eX0I5UKcgeRUs=
sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
+3
View File
@@ -0,0 +1,3 @@
*~
istored
istored.service
+24
View File
@@ -0,0 +1,24 @@
#!/bin/sh
#
# $Id$
#
# PROVIDE: mbased
# REQUIRE: DAEMON
. /etc/rc.subr
name="mbased"
rcvar="mbased_enable"
pidfile="@srv_rundir@/mbased.pid"
command="@prefix@/sbin/${name}"
command_args="--asDaemon=true"
procname="@prefix@/sbin/${name}"
load_rc_config ${name}
: ${mbased_enable:="NO"}
run_rc_command "$1"
#EOF

Some files were not shown because too many files have changed in this diff Show More