mirror of
https://github.com/lzh-1625/go_process_manager.git
synced 2025-09-26 12:01:16 +08:00
初次提交
This commit is contained in:
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
info.log
|
||||
msm*
|
||||
templates
|
||||
vue-minecraft/node_modules
|
||||
data.db
|
||||
route/template
|
||||
.data*
|
661
LICENSE
Normal file
661
LICENSE
Normal file
@@ -0,0 +1,661 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
33
api/api.go
Normal file
33
api/api.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"msm/consts/ctxflag"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func rOk(ctx *gin.Context, message string, data any) {
|
||||
jsonData := map[string]any{
|
||||
"code": 0,
|
||||
"msg": message,
|
||||
}
|
||||
if data != nil {
|
||||
jsonData["data"] = data
|
||||
}
|
||||
ctx.JSON(http.StatusOK, jsonData)
|
||||
}
|
||||
|
||||
func errCheck(ctx *gin.Context, isErr bool, errData any) {
|
||||
if !isErr {
|
||||
return
|
||||
}
|
||||
if err, ok := errData.(error); ok {
|
||||
ctx.Set(ctxflag.ERR, err)
|
||||
}
|
||||
if err, ok := errData.(string); ok {
|
||||
ctx.Set(ctxflag.ERR, errors.New(err))
|
||||
}
|
||||
panic(0)
|
||||
}
|
94
api/config.go
Normal file
94
api/config.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"msm/config"
|
||||
"msm/dao"
|
||||
"msm/model"
|
||||
"msm/service/es"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type configApi struct{}
|
||||
|
||||
var ConfigApi = new(configApi)
|
||||
|
||||
func (c *configApi) GetSystemConfiguration(ctx *gin.Context) {
|
||||
typeElem := reflect.TypeOf(config.CF).Elem()
|
||||
valueElem := reflect.ValueOf(config.CF).Elem()
|
||||
result := []model.SystemConfigurationResp{}
|
||||
for i := 0; i < typeElem.NumField(); i++ {
|
||||
typeField := typeElem.Field(i)
|
||||
valueField := valueElem.Field(i)
|
||||
var value any
|
||||
switch typeField.Type.Kind() {
|
||||
case reflect.Int64, reflect.Int:
|
||||
value = valueField.Int()
|
||||
case reflect.String:
|
||||
value = valueField.String()
|
||||
case reflect.Bool:
|
||||
value = valueField.Bool()
|
||||
case reflect.Float64:
|
||||
value = valueField.Float()
|
||||
default:
|
||||
continue
|
||||
}
|
||||
result = append(result, model.SystemConfigurationResp{
|
||||
Key: typeField.Name,
|
||||
Value: value,
|
||||
Default: typeField.Tag.Get("default"),
|
||||
Describe: typeField.Tag.Get("describe"),
|
||||
})
|
||||
}
|
||||
rOk(ctx, "获取系统配置成功", result)
|
||||
}
|
||||
|
||||
func (c *configApi) SetSystemConfiguration(ctx *gin.Context) {
|
||||
data := map[string]string{}
|
||||
errCheck(ctx, ctx.ShouldBindJSON(&data) != nil, "请求参数错误")
|
||||
typeElem := reflect.TypeOf(config.CF).Elem()
|
||||
valueElem := reflect.ValueOf(config.CF).Elem()
|
||||
for i := 0; i < typeElem.NumField(); i++ {
|
||||
typeField := typeElem.Field(i)
|
||||
valueField := valueElem.Field(i)
|
||||
for k, v := range data {
|
||||
if typeField.Name == k {
|
||||
var err error
|
||||
switch typeField.Type.Kind() {
|
||||
case reflect.String:
|
||||
valueField.SetString(v)
|
||||
case reflect.Bool:
|
||||
value, errV := strconv.ParseBool(v)
|
||||
err = errV
|
||||
if err == nil {
|
||||
valueField.SetBool(value)
|
||||
}
|
||||
case reflect.Float64:
|
||||
value, errV := strconv.ParseFloat(v, 64)
|
||||
err = errV
|
||||
if err == nil {
|
||||
valueField.SetFloat(value)
|
||||
}
|
||||
case reflect.Int64, reflect.Int:
|
||||
value, errV := strconv.ParseInt(v, 10, 64)
|
||||
err = errV
|
||||
if err == nil {
|
||||
valueField.SetInt(value)
|
||||
}
|
||||
default:
|
||||
continue
|
||||
}
|
||||
errCheck(ctx, err != nil, k+"类似错误")
|
||||
errCheck(ctx, dao.ConfigDao.SetConfigValue(k, v) != nil, "修改配置失败")
|
||||
}
|
||||
}
|
||||
}
|
||||
rOk(ctx, "修改配置成功", nil)
|
||||
}
|
||||
|
||||
func (c *configApi) EsConfigReload(ctx *gin.Context) {
|
||||
errCheck(ctx, !es.InitEs(), "es连接失败,请检查是否启用es或账号密码是否存在错误")
|
||||
rOk(ctx, "已连接上es", nil)
|
||||
}
|
34
api/file.go
Normal file
34
api/file.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
FileService "msm/service/file"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type file struct{}
|
||||
|
||||
var FileApi = new(file)
|
||||
|
||||
func (f *file) FilePathHandler(ctx *gin.Context) {
|
||||
data, err := FileService.FileService.GetFileAndDirByPath(ctx.Query("path"))
|
||||
errCheck(ctx, err != nil, "文件路径查询失败")
|
||||
rOk(ctx, "文件路径查询成功", data)
|
||||
}
|
||||
|
||||
func (f *file) FileWriteHandler(ctx *gin.Context) {
|
||||
path := ctx.PostForm("filePath")
|
||||
fi, err := ctx.FormFile("data")
|
||||
errCheck(ctx, err != nil, "文件读取失败")
|
||||
fiReader, _ := fi.Open()
|
||||
err = FileService.FileService.UpdateFileData(path, fiReader, fi.Size)
|
||||
errCheck(ctx, err != nil, "文件数据更新失败")
|
||||
rOk(ctx, "文件更新成功", nil)
|
||||
}
|
||||
|
||||
func (f *file) FileReadHandler(ctx *gin.Context) {
|
||||
path := ctx.Query("filePath")
|
||||
bytes, err := FileService.FileService.ReadFileFromPath(path)
|
||||
errCheck(ctx, err != nil, "文件数据读取失败")
|
||||
rOk(ctx, "文件数据读取成功", string(bytes))
|
||||
}
|
20
api/log.go
Normal file
20
api/log.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"msm/config"
|
||||
"msm/model"
|
||||
"msm/service/es"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type logApi struct{}
|
||||
|
||||
var LogApi = new(logApi)
|
||||
|
||||
func (a *logApi) GetLog(ctx *gin.Context) {
|
||||
req := model.GetLogReq{}
|
||||
errCheck(ctx, !config.CF.EsEnable, "elasticsearch未启用或账号密码错误")
|
||||
errCheck(ctx, ctx.ShouldBindJSON(&req) != nil, "请求体格式错误")
|
||||
rOk(ctx, "查询成功", es.EsService.Search(req))
|
||||
}
|
27
api/permission.go
Normal file
27
api/permission.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"msm/model"
|
||||
|
||||
"msm/dao"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var PermissionApi = new(permissionApi)
|
||||
|
||||
type permissionApi struct{}
|
||||
|
||||
func (p *permissionApi) EditPermssion(ctx *gin.Context) {
|
||||
per := model.Permission{}
|
||||
err := ctx.ShouldBindJSON(&per)
|
||||
errCheck(ctx, err != nil, err)
|
||||
err = dao.PermissionDao.EditPermssion(per)
|
||||
errCheck(ctx, err != nil, err)
|
||||
rOk(ctx, "权限修改成功", nil)
|
||||
}
|
||||
|
||||
func (p *permissionApi) GetPermissionList(ctx *gin.Context) {
|
||||
result := dao.PermissionDao.GetPermssionList(ctx.Query("account"))
|
||||
rOk(ctx, "查询成功", result)
|
||||
}
|
101
api/proc.go
Normal file
101
api/proc.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"msm/consts/ctxflag"
|
||||
"msm/consts/role"
|
||||
"msm/dao"
|
||||
"msm/model"
|
||||
"msm/service/process"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type procApi struct{}
|
||||
|
||||
var ProcApi = new(procApi)
|
||||
|
||||
func (p *procApi) CreateNewProcess(ctx *gin.Context) {
|
||||
req := model.Process{}
|
||||
ctx.ShouldBindJSON(&req)
|
||||
index, err := dao.ProcessDao.AddProcessConfig(req)
|
||||
errCheck(ctx, err != nil, err)
|
||||
req.Uuid = index
|
||||
proc, err := process.RunNewProcess(req)
|
||||
errCheck(ctx, err != nil, err)
|
||||
process.ProcessCtlService.AddProcess(req.Uuid, proc)
|
||||
rOk(ctx, "创建成功", gin.H{
|
||||
"id": req.Uuid,
|
||||
})
|
||||
}
|
||||
|
||||
func (p *procApi) DeleteNewProcess(ctx *gin.Context) {
|
||||
uuid, err := strconv.Atoi(ctx.Query("uuid"))
|
||||
errCheck(ctx, err != nil, "参数有误")
|
||||
process.ProcessCtlService.KillProcess(uuid)
|
||||
process.ProcessCtlService.DeleteProcess(uuid)
|
||||
err = dao.ProcessDao.DeleteProcessConfig(uuid)
|
||||
errCheck(ctx, err != nil, err)
|
||||
rOk(ctx, "删除成功", nil)
|
||||
}
|
||||
|
||||
func (p *procApi) KillProcess(ctx *gin.Context) {
|
||||
uuid, err := strconv.Atoi(ctx.Query("uuid"))
|
||||
errCheck(ctx, err != nil, "参数有误")
|
||||
err = process.ProcessCtlService.KillProcess(uuid)
|
||||
errCheck(ctx, err != nil, err)
|
||||
rOk(ctx, "成功", nil)
|
||||
}
|
||||
|
||||
func (p *procApi) StartProcess(ctx *gin.Context) {
|
||||
uuid, err := strconv.Atoi(ctx.Query("uuid"))
|
||||
errCheck(ctx, err != nil, "参数有误")
|
||||
prod, err := process.ProcessCtlService.GetProcess(uuid)
|
||||
if err != nil { // 进程不存在则创建
|
||||
proc, err := process.RunNewProcess(dao.ProcessDao.GetProcessConfigById(uuid))
|
||||
errCheck(ctx, err != nil, err)
|
||||
process.ProcessCtlService.AddProcess(uuid, proc)
|
||||
rOk(ctx, "成功", nil)
|
||||
return
|
||||
}
|
||||
errCheck(ctx, prod.GetStateState() == 1, "进程还在运行中")
|
||||
prod.ResetRestartTimes()
|
||||
prod.ReStart()
|
||||
// dao.UpdateServerAutoStart(uuid, true)
|
||||
rOk(ctx, "成功", nil)
|
||||
}
|
||||
|
||||
func (p *procApi) GetProcessList(ctx *gin.Context) {
|
||||
if ctx.GetInt(ctxflag.ROLE) < int(role.USER) {
|
||||
rOk(ctx, "进程列表获取成功", process.ProcessCtlService.GetProcessList())
|
||||
} else {
|
||||
rOk(ctx, "进程列表获取成功", process.ProcessCtlService.GetProcessListByUser(ctx.GetString(ctxflag.USER_NAME)))
|
||||
}
|
||||
}
|
||||
|
||||
func (p *procApi) UpdateProcessConfig(ctx *gin.Context) {
|
||||
req := model.Process{}
|
||||
ctx.ShouldBindJSON(&req)
|
||||
process.ProcessCtlService.UpdateProcessConfig(req)
|
||||
err := dao.ProcessDao.UpdateProcessConfig(req)
|
||||
errCheck(ctx, err != nil, err)
|
||||
rOk(ctx, "更改配置成功", nil)
|
||||
}
|
||||
|
||||
func (p *procApi) GetProcessConfig(ctx *gin.Context) {
|
||||
uuid, err := strconv.Atoi(ctx.Query("uuid"))
|
||||
errCheck(ctx, err != nil, "参数有误")
|
||||
data := dao.ProcessDao.GetProcessConfigById(uuid)
|
||||
errCheck(ctx, data.Uuid == 0, "未查询到信息")
|
||||
rOk(ctx, "success", data)
|
||||
}
|
||||
|
||||
func (p *procApi) ProcessControl(ctx *gin.Context) {
|
||||
user := ctx.GetString(ctxflag.USER_NAME)
|
||||
uuid, err := strconv.Atoi(ctx.Query("uuid"))
|
||||
errCheck(ctx, err != nil, "参数有误")
|
||||
proc, err := process.ProcessCtlService.GetProcess(uuid)
|
||||
errCheck(ctx, err != nil, "进程控制权获取失败")
|
||||
proc.ProcessControl(user)
|
||||
rOk(ctx, "获取进程控权成功", nil)
|
||||
}
|
50
api/push.go
Normal file
50
api/push.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"msm/model"
|
||||
"strconv"
|
||||
|
||||
"msm/dao"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type pushApi struct{}
|
||||
|
||||
var PushApi = new(pushApi)
|
||||
|
||||
func (p *pushApi) GetPushList(ctx *gin.Context) {
|
||||
rOk(ctx, "查询成功", dao.PushDao.GetPushList())
|
||||
}
|
||||
|
||||
func (p *pushApi) GetPushById(ctx *gin.Context) {
|
||||
id, err := strconv.Atoi(ctx.Query("id"))
|
||||
errCheck(ctx, err != nil, err)
|
||||
rOk(ctx, "查询成功", dao.PushDao.GetPushConfigById(id))
|
||||
}
|
||||
|
||||
func (p *pushApi) AddPushConfig(ctx *gin.Context) {
|
||||
data := model.Push{}
|
||||
err := ctx.ShouldBindJSON(&data)
|
||||
errCheck(ctx, err != nil, err)
|
||||
err = dao.PushDao.AddPushConfig(data)
|
||||
errCheck(ctx, err != nil, err)
|
||||
rOk(ctx, "添加成功", nil)
|
||||
}
|
||||
|
||||
func (p *pushApi) UpdatePushConfig(ctx *gin.Context) {
|
||||
data := model.Push{}
|
||||
err := ctx.ShouldBindJSON(&data)
|
||||
errCheck(ctx, err != nil, err)
|
||||
err = dao.PushDao.UpdatePushConfig(data)
|
||||
errCheck(ctx, err != nil, err)
|
||||
rOk(ctx, "更新成功", nil)
|
||||
}
|
||||
|
||||
func (p *pushApi) DeletePushConfig(ctx *gin.Context) {
|
||||
id, err := strconv.Atoi(ctx.Query("id"))
|
||||
errCheck(ctx, err != nil, err)
|
||||
err = dao.PushDao.DeletePushConfig(id)
|
||||
errCheck(ctx, err != nil, err)
|
||||
rOk(ctx, "删除成功", nil)
|
||||
}
|
86
api/user.go
Normal file
86
api/user.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"msm/consts/ctxflag"
|
||||
"msm/consts/role"
|
||||
"msm/dao"
|
||||
"msm/model"
|
||||
"msm/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type userApi struct{}
|
||||
|
||||
var UserApi = new(userApi)
|
||||
|
||||
const DEFAULT_ROOT_PASSWORD = "root"
|
||||
|
||||
func (u *userApi) LoginHandler(ctx *gin.Context) {
|
||||
info := map[string]string{}
|
||||
ctx.ShouldBindJSON(&info)
|
||||
account := info["account"]
|
||||
password := info["password"]
|
||||
errCheck(ctx, !u.checkLoginInfo(account, password), "登入失败,账号或密码错误")
|
||||
token, err := utils.GenToken(account)
|
||||
errCheck(ctx, err != nil, err)
|
||||
ctx.JSON(200, gin.H{
|
||||
"code": 0,
|
||||
"msg": "登入成功!",
|
||||
"token": token,
|
||||
"username": account,
|
||||
"role": dao.UserDao.GetUserByName(account).Role,
|
||||
})
|
||||
}
|
||||
|
||||
func (u *userApi) CreateUser(ctx *gin.Context) {
|
||||
user := model.User{}
|
||||
err := ctx.ShouldBindJSON(&user)
|
||||
errCheck(ctx, err != nil, err)
|
||||
errCheck(ctx, user.Role == int(role.ROOT), "不能添加root账号")
|
||||
err = dao.UserDao.CreateUser(user)
|
||||
errCheck(ctx, err != nil, err)
|
||||
rOk(ctx, "注册成功", nil)
|
||||
}
|
||||
|
||||
func (u *userApi) ChangePassword(ctx *gin.Context) {
|
||||
user := model.User{}
|
||||
err := ctx.ShouldBindJSON(&user)
|
||||
errCheck(ctx, err != nil, err)
|
||||
reqUser := ctx.GetString(ctxflag.USER_NAME)
|
||||
errCheck(ctx, ctx.GetInt(ctxflag.ROLE) != int(role.ROOT) && user.Account != "", "参数错误")
|
||||
var userName string
|
||||
if user.Account != "" {
|
||||
userName = user.Account
|
||||
} else {
|
||||
userName = reqUser
|
||||
}
|
||||
err = dao.UserDao.UpdatePassword(userName, user.Password)
|
||||
errCheck(ctx, err != nil, err)
|
||||
rOk(ctx, "修改密码成功", nil)
|
||||
|
||||
}
|
||||
|
||||
func (u *userApi) DeleteUser(ctx *gin.Context) {
|
||||
errCheck(ctx, ctx.Query("account") == "root", "无法删除root账户")
|
||||
err := dao.UserDao.DeleteUser(ctx.Query("account"))
|
||||
errCheck(ctx, err != nil, "无法删除root账户")
|
||||
rOk(ctx, "删除成功", nil)
|
||||
}
|
||||
|
||||
func (u *userApi) GetUserList(ctx *gin.Context) {
|
||||
rOk(ctx, "查询成功", dao.UserDao.GetUserList())
|
||||
}
|
||||
|
||||
func (u *userApi) checkLoginInfo(account, password string) bool {
|
||||
user := dao.UserDao.GetUserByName(account)
|
||||
if account == "root" && user.Account == "" {
|
||||
dao.UserDao.CreateUser(model.User{
|
||||
Account: "root",
|
||||
Password: DEFAULT_ROOT_PASSWORD,
|
||||
Role: int(role.ROOT),
|
||||
})
|
||||
return password == DEFAULT_ROOT_PASSWORD
|
||||
}
|
||||
return user.Password == utils.Md5(password)
|
||||
}
|
96
api/ws.go
Normal file
96
api/ws.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"msm/consts/ctxflag"
|
||||
"msm/log"
|
||||
"msm/service/process"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type wsApi struct{}
|
||||
|
||||
var WsApi = new(wsApi)
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
func (w *wsApi) WebsocketHandle(ctx *gin.Context) {
|
||||
reqUser := ctx.GetString(ctxflag.USER_NAME)
|
||||
uuid, err := strconv.Atoi(ctx.Query("uuid"))
|
||||
errCheck(ctx, err != nil, "参数有误")
|
||||
proc, err := process.ProcessCtlService.GetProcess(uuid)
|
||||
errCheck(ctx, err != nil, "进程获取失败")
|
||||
errCheck(ctx, proc.GetStateState() != 1, "进程未运行")
|
||||
errCheck(ctx, proc.GetControlController() != reqUser && !proc.VerifyControl(), "进程权限不足")
|
||||
errCheck(ctx, !proc.TryLock(), "进程已被占用")
|
||||
proc.SetWhoUsing(reqUser)
|
||||
conn, err := upgrader.Upgrade(ctx.Writer, ctx.Request, nil)
|
||||
errCheck(ctx, err != nil, "ws升级失败")
|
||||
log.Logger.Infow("ws连接成功", "进程名称", proc.GetName(), "连接者", proc.GetWhoUsing())
|
||||
proc.SetControlController("")
|
||||
wsCtx, cancel := context.WithCancel(context.Background())
|
||||
w.startWsConnect(conn, proc, cancel)
|
||||
proc.SetWsConn(conn)
|
||||
proc.SetIsUsing(true)
|
||||
close := func(err string) {
|
||||
proc.SetWhoUsing("")
|
||||
proc.SetIsUsing(false)
|
||||
proc.SetWsConn(nil)
|
||||
conn.Close()
|
||||
proc.Unlock()
|
||||
log.Logger.Infow("ws连接断开", "操作类型", err, "进程名称", proc.GetName())
|
||||
}
|
||||
conn.SetCloseHandler(func(_ int, _ string) error {
|
||||
proc.ChangControlChan() <- 1
|
||||
close("ws连接被断开")
|
||||
return nil
|
||||
})
|
||||
select {
|
||||
case signal := <-proc.ChangControlChan():
|
||||
{
|
||||
if signal == 0 {
|
||||
close("强制断开ws连接")
|
||||
}
|
||||
}
|
||||
case <-proc.StopChan():
|
||||
{
|
||||
close("进程已停止,强制断开ws连接")
|
||||
}
|
||||
case <-time.After(time.Minute * 10):
|
||||
{
|
||||
close("连接时间超过最大时长限制")
|
||||
}
|
||||
case <-wsCtx.Done():
|
||||
{
|
||||
close("tcp连接建立已被关闭")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (w *wsApi) startWsConnect(conn *websocket.Conn, proc process.Process, cancel context.CancelFunc) {
|
||||
proc.ReadCache(conn)
|
||||
log.Logger.Debugw("ws读取线程已启动")
|
||||
go func() {
|
||||
for {
|
||||
_, b, err := conn.ReadMessage()
|
||||
if err != nil {
|
||||
log.Logger.Debugw("ws读取线程已退出", "info", err)
|
||||
cancel()
|
||||
return
|
||||
}
|
||||
proc.WriteBytes(b)
|
||||
}
|
||||
}()
|
||||
}
|
52
boot/boot.go
Normal file
52
boot/boot.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package boot
|
||||
|
||||
import (
|
||||
"msm/config"
|
||||
"msm/dao"
|
||||
"msm/log"
|
||||
"msm/service/es"
|
||||
"msm/service/process"
|
||||
"msm/utils"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func Boot() {
|
||||
initConfiguration()
|
||||
initEs()
|
||||
initProcess()
|
||||
}
|
||||
|
||||
func initConfiguration() {
|
||||
typeElem := reflect.TypeOf(config.CF).Elem()
|
||||
valueElem := reflect.ValueOf(config.CF).Elem()
|
||||
for i := 0; i < typeElem.NumField(); i++ {
|
||||
typeField := typeElem.Field(i)
|
||||
valueField := valueElem.Field(i)
|
||||
value, err := dao.ConfigDao.GetConfigValue(typeField.Name)
|
||||
if err != nil {
|
||||
value = typeField.Tag.Get("default")
|
||||
}
|
||||
switch typeField.Type.Kind() {
|
||||
case reflect.String:
|
||||
valueField.SetString(value)
|
||||
case reflect.Bool:
|
||||
valueField.SetBool(utils.Unwarp(strconv.ParseBool(value)))
|
||||
case reflect.Float64:
|
||||
valueField.SetFloat(utils.Unwarp(strconv.ParseFloat(value, 64)))
|
||||
case reflect.Int64, reflect.Int:
|
||||
valueField.SetInt(utils.Unwarp(strconv.ParseInt(value, 10, 64)))
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
log.Logger.Debugw("获取配置信息完成", "Configuration", config.CF)
|
||||
}
|
||||
|
||||
func initEs() {
|
||||
es.InitEs()
|
||||
}
|
||||
|
||||
func initProcess() {
|
||||
process.ProcessCtlService.ProcessInit()
|
||||
}
|
4
build.bat
Normal file
4
build.bat
Normal file
@@ -0,0 +1,4 @@
|
||||
SET CGO_ENABLED=0
|
||||
SET GOOS=linux
|
||||
SET GOARCH=amd64
|
||||
go build -o msm main.go
|
8
config.yaml
Normal file
8
config.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
LogLevel: "debug"
|
||||
Listen: ":8797"
|
||||
EsConfig:
|
||||
Enable: true
|
||||
Url: "http://xcon.top:9200"
|
||||
Index: "server_log_v1"
|
||||
Username: "elastic"
|
||||
Password: "1625167628@xcon"
|
27
config/config.go
Normal file
27
config/config.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package config
|
||||
|
||||
var CF = &configuration{
|
||||
LogLevel: "debug",
|
||||
}
|
||||
|
||||
// 只支持 float64、int、int64、bool、string类型
|
||||
type configuration struct {
|
||||
LogLevel string `default:"debug" describe:"日志等级"`
|
||||
Listen string `default:":8797" describe:"监听端口"`
|
||||
EsEnable bool `default:"false" describe:"启用Elasticsearch"`
|
||||
EsUrl string `default:"" describe:"Elasticsearch url"`
|
||||
EsIndex string `default:"server_log_v1" describe:"Elasticsearch index"`
|
||||
EsUsername string `default:"" describe:"Elasticsearch用户名"`
|
||||
EsPassword string `default:"" describe:"Elasticsearch密码"`
|
||||
FileSizeLimit float64 `default:"10.0" describe:"文件大小限制(MB)"`
|
||||
ProcessInputPrefix string `default:">" describe:"进程输入前缀"`
|
||||
ProcessRestartsLimit int `default:"2" describe:"进程重启次数限制"`
|
||||
ProcessMsgCacheLinesLimit int `default:"50" describe:"std进程缓存消息行数"`
|
||||
ProcessMsgCacheBufLimit int `default:"4096" describe:"pty进程缓存消息字节长度"`
|
||||
ProcessExpireTime int64 `default:"60" describe:"进程控制权过期时间(秒)"`
|
||||
PerformanceInfoListLength int `default:"30" describe:"性能信息存储长度"`
|
||||
PerformanceInfoInterval int `default:"1" describe:"监控获取间隔时间(分钟)"`
|
||||
UserPassWordMinLength int `default:"4" describe:"用户密码最小长度"`
|
||||
LogMinLenth int `default:"0" describe:"过滤日志最小长度"`
|
||||
PprofEnable bool `default:"true" describe:"启用pprof分析工具"`
|
||||
}
|
1
consts/consts.go
Normal file
1
consts/consts.go
Normal file
@@ -0,0 +1 @@
|
||||
package consts
|
7
consts/ctxflag/ctxflag.go
Normal file
7
consts/ctxflag/ctxflag.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package ctxflag
|
||||
|
||||
const (
|
||||
USER_NAME = "user"
|
||||
ROLE = "role"
|
||||
ERR = "err"
|
||||
)
|
9
consts/permission/permission.go
Normal file
9
consts/permission/permission.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package permission
|
||||
|
||||
type OprPermission string
|
||||
|
||||
const (
|
||||
START_OPERATION OprPermission = "Start"
|
||||
STOP_OPERATION OprPermission = "Stop"
|
||||
TERMINAL_OPERATION OprPermission = "Terminal"
|
||||
)
|
10
consts/role/role.go
Normal file
10
consts/role/role.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package role
|
||||
|
||||
type Role int
|
||||
|
||||
const (
|
||||
ROOT Role = iota
|
||||
ADMIN
|
||||
USER
|
||||
GUEST
|
||||
)
|
30
dao/config.go
Normal file
30
dao/config.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"msm/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type configDao struct{}
|
||||
|
||||
var ConfigDao = new(configDao)
|
||||
|
||||
func (c *configDao) GetConfigValue(key string) (string, error) {
|
||||
var result string
|
||||
if err := db.Model(&model.Config{}).Select("value").Where("key = ?", key).First(&result).Error; err != nil {
|
||||
return "", err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *configDao) SetConfigValue(key, value string) error {
|
||||
if db.Model(&model.Config{}).Where("key = ?", key).First(nil).Error == gorm.ErrRecordNotFound {
|
||||
return db.Create(&model.Config{
|
||||
Key: key,
|
||||
Value: value,
|
||||
}).Error
|
||||
} else {
|
||||
return db.Model(&model.Config{}).Where("key = ?", key).Updates(model.Config{Value: value}).Error
|
||||
}
|
||||
}
|
40
dao/db.go
Normal file
40
dao/db.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"log"
|
||||
zlog "msm/log"
|
||||
"msm/model"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/glebarez/sqlite"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
var db *gorm.DB
|
||||
|
||||
var defaultConfig = gorm.Session{PrepareStmt: true, SkipDefaultTransaction: true}
|
||||
|
||||
func init() {
|
||||
newLogger := logger.New(
|
||||
log.New(os.Stdout, "\r\n", log.LstdFlags),
|
||||
logger.Config{
|
||||
SlowThreshold: time.Second,
|
||||
LogLevel: logger.Silent,
|
||||
IgnoreRecordNotFoundError: true,
|
||||
ParameterizedQueries: true,
|
||||
Colorful: true,
|
||||
},
|
||||
)
|
||||
gdb, err := gorm.Open(sqlite.Open("data.db"), &gorm.Config{
|
||||
Logger: newLogger,
|
||||
})
|
||||
if err != nil {
|
||||
zlog.Logger.Panicf("sqlite数据库初始化失败!\n错误原因:%v", err)
|
||||
}
|
||||
zlog.Logger.Info("sqlite初始化成功")
|
||||
db = gdb.Session(&defaultConfig)
|
||||
// db = gdb.Session(&defaultConfig).Debug()
|
||||
db.AutoMigrate(&model.Process{}, &model.User{}, &model.Permission{}, &model.Push{}, &model.Config{})
|
||||
}
|
42
dao/permission.go
Normal file
42
dao/permission.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"msm/log"
|
||||
"msm/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type permissionDao struct{}
|
||||
|
||||
var PermissionDao = new(permissionDao)
|
||||
|
||||
func (p *permissionDao) GetPermssionList(account string) []model.PermissionPo {
|
||||
result := []model.PermissionPo{}
|
||||
if err := db.Raw(`SELECT p.name ,p.uuid as pid,p2.owned ,p2."start" ,p2.stop ,p2.terminal
|
||||
FROM users u full join process p left join permission p2 on p2.account == u.account and p2.pid =p.uuid WHERE u.account = ? or u.account ISNULL`, account).Find(&result); err.Error != nil {
|
||||
log.Logger.Warnw("权限查询失败", "err", err)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (p *permissionDao) EditPermssion(data model.Permission) error {
|
||||
if db.Model(&model.Permission{}).Where("account = ? and pid = ?", data.Account, data.Pid).First(nil).Error == gorm.ErrRecordNotFound {
|
||||
db.Omit("name").Create(&model.Permission{
|
||||
Account: data.Account,
|
||||
Pid: data.Pid,
|
||||
})
|
||||
}
|
||||
return db.Debug().Model(&model.Permission{}).Where("account = ? and pid = ?", data.Account, data.Pid).Updates(map[string]interface{}{
|
||||
"owned": data.Owned,
|
||||
"start": data.Start,
|
||||
"stop": data.Stop,
|
||||
"terminal": data.Terminal,
|
||||
}).Error
|
||||
}
|
||||
|
||||
func (p *permissionDao) GetPermission(user string, pid int) (result model.Permission) {
|
||||
db.Debug().Model(&model.Permission{}).Where("account = ? and pid = ?", user, pid).First(&result)
|
||||
return
|
||||
}
|
58
dao/process.go
Normal file
58
dao/process.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"msm/log"
|
||||
"msm/model"
|
||||
)
|
||||
|
||||
type processDao struct{}
|
||||
|
||||
var ProcessDao = new(processDao)
|
||||
|
||||
func (p *processDao) GetAllProcessConfig() []model.Process {
|
||||
result := []model.Process{}
|
||||
|
||||
tx := db.Find(&result)
|
||||
if tx.Error != nil {
|
||||
log.Logger.Error(tx.Error)
|
||||
return []model.Process{}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (p *processDao) GetProcessConfigByUser(username string) []model.Process {
|
||||
result := []model.Process{}
|
||||
tx := db.Debug().Raw(`SELECT p.uuid, p.name FROM permission left join process p where pid =p.uuid and owned = 1 and account = ?`, username).Scan(&result)
|
||||
if tx.Error != nil {
|
||||
log.Logger.Error(tx.Error)
|
||||
return []model.Process{}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (p *processDao) UpdateProcessConfig(process model.Process) error {
|
||||
tx := db.Save(&process)
|
||||
return tx.Error
|
||||
}
|
||||
|
||||
func (p *processDao) AddProcessConfig(process model.Process) (int, error) {
|
||||
tx := db.Create(&process)
|
||||
return process.Uuid, tx.Error
|
||||
}
|
||||
|
||||
func (p *processDao) DeleteProcessConfig(uuid int) error {
|
||||
tx := db.Delete(&model.Process{
|
||||
Uuid: uuid,
|
||||
})
|
||||
return tx.Error
|
||||
}
|
||||
|
||||
func (p *processDao) GetProcessConfigById(uuid int) model.Process {
|
||||
result := model.Process{}
|
||||
tx := db.Where(&model.Process{Uuid: uuid}).First(&result)
|
||||
if tx.Error != nil {
|
||||
log.Logger.Error(tx.Error)
|
||||
return model.Process{}
|
||||
}
|
||||
return result
|
||||
}
|
33
dao/push.go
Normal file
33
dao/push.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"msm/model"
|
||||
)
|
||||
|
||||
type pushDao struct{}
|
||||
|
||||
var PushDao = new(pushDao)
|
||||
|
||||
func (p *pushDao) GetPushList() (result []model.Push) {
|
||||
db.Find(&result)
|
||||
return
|
||||
}
|
||||
|
||||
func (p *pushDao) GetPushConfigById(id int) (result model.Push) {
|
||||
db.Where("id = ?", id).First(&result)
|
||||
return
|
||||
}
|
||||
|
||||
func (p *pushDao) UpdatePushConfig(data model.Push) error {
|
||||
return db.Save(&data).Error
|
||||
}
|
||||
|
||||
func (p *pushDao) AddPushConfig(data model.Push) error {
|
||||
return db.Create(&data).Error
|
||||
}
|
||||
|
||||
func (p *pushDao) DeletePushConfig(id int) error {
|
||||
return db.Delete(&model.Push{
|
||||
Id: int64(id),
|
||||
}).Error
|
||||
}
|
51
dao/user.go
Normal file
51
dao/user.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"msm/config"
|
||||
"msm/model"
|
||||
"msm/utils"
|
||||
"time"
|
||||
)
|
||||
|
||||
type userDao struct{}
|
||||
|
||||
var UserDao = new(userDao)
|
||||
|
||||
func (u *userDao) GetUserByName(name string) model.User {
|
||||
var result model.User
|
||||
db.Where("account = ?", name).First(&result)
|
||||
return result
|
||||
}
|
||||
|
||||
func (u *userDao) CreateUser(user model.User) error {
|
||||
if len(user.Password) < config.CF.UserPassWordMinLength {
|
||||
return errors.New("密码小于最小长度")
|
||||
}
|
||||
user.Password = utils.Md5(user.Password)
|
||||
user.CreateTime = time.Now()
|
||||
tx := db.Create(&user)
|
||||
return tx.Error
|
||||
}
|
||||
|
||||
func (u *userDao) UpdatePassword(name string, password string) error {
|
||||
if len(password) < config.CF.UserPassWordMinLength {
|
||||
return errors.New("新密码太短")
|
||||
}
|
||||
tx := db.Model(&model.User{}).Where("account = ?", name).Update("password", utils.Md5(password))
|
||||
return tx.Error
|
||||
}
|
||||
|
||||
func (u *userDao) DeleteUser(name string) error {
|
||||
if err := db.Where("account = ?", name).First(&model.User{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
tx := db.Delete(&model.User{Account: name})
|
||||
return tx.Error
|
||||
}
|
||||
|
||||
func (u *userDao) GetUserList() []model.User {
|
||||
result := []model.User{}
|
||||
db.Find(&result)
|
||||
return result
|
||||
}
|
83
go.mod
Normal file
83
go.mod
Normal file
@@ -0,0 +1,83 @@
|
||||
module msm
|
||||
|
||||
go 1.21.1
|
||||
|
||||
require github.com/gorilla/websocket v1.5.1
|
||||
|
||||
require (
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.13 // indirect
|
||||
github.com/tklauser/numcpus v0.7.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
gorm.io/gorm v1.25.7
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/elastic/elastic-transport-go/v8 v8.5.0 // indirect
|
||||
github.com/glebarez/go-sqlite v1.21.2 // indirect
|
||||
github.com/go-logr/logr v1.3.0 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
go.opentelemetry.io/otel v1.21.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.21.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.21.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||
modernc.org/libc v1.22.5 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.5.0 // indirect
|
||||
modernc.org/sqlite v1.23.1 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.11.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gin-contrib/pprof v1.5.0 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
go.uber.org/multierr v1.10.0 // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/crypto v0.22.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/text v0.15.0 // indirect
|
||||
google.golang.org/protobuf v1.34.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/creack/pty v1.1.21
|
||||
github.com/elastic/go-elasticsearch/v8 v8.13.1
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/glebarez/sqlite v1.11.0
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||
github.com/levigross/grequests v0.0.0-20231203190023-9c307ef1f48d
|
||||
github.com/panjf2000/ants v1.3.0
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible
|
||||
go.uber.org/zap v1.26.0
|
||||
golang.org/x/net v0.24.0 // indirect
|
||||
)
|
212
go.sum
Normal file
212
go.sum
Normal file
@@ -0,0 +1,212 @@
|
||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
||||
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
||||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
|
||||
github.com/creack/pty v1.1.21/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/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/elastic/elastic-transport-go/v8 v8.5.0 h1:v5membAl7lvQgBTexPRDBO/RdnlQX+FM9fUVDyXxvH0=
|
||||
github.com/elastic/elastic-transport-go/v8 v8.5.0/go.mod h1:YLHer5cj0csTzNFXoNQ8qhtGY1GTvSqPnKWKaqQE3Hk=
|
||||
github.com/elastic/go-elasticsearch/v8 v8.13.1 h1:du5F8IzUUyCkzxyHdrO9AtopcG95I/qwi2WK8Kf1xlg=
|
||||
github.com/elastic/go-elasticsearch/v8 v8.13.1/go.mod h1:DIn7HopJs4oZC/w0WoJR13uMUxtHeq92eI5bqv5CRfI=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/gin-contrib/pprof v1.5.0 h1:E/Oy7g+kNw94KfdCy3bZxQFtyDnAX2V7axRS7sNYVrU=
|
||||
github.com/gin-contrib/pprof v1.5.0/go.mod h1:GqFL6LerKoCQ/RSWnkYczkTJ+tOAUVN/8sbnEtaqOKs=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
|
||||
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
|
||||
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
|
||||
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
|
||||
github.com/go-logr/logr v1.3.0/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-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
|
||||
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
|
||||
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
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/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
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/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/levigross/grequests v0.0.0-20231203190023-9c307ef1f48d h1:8fVmm2qScPn4JAF/YdTtqrPP3n58FgZ4GbKTNfaPuRs=
|
||||
github.com/levigross/grequests v0.0.0-20231203190023-9c307ef1f48d/go.mod h1:dFu6nuJHC3u9kCDcyGrEL7LwhK2m6Mt+alyiiIjDrRY=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
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 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/panjf2000/ants v1.3.0 h1:8pQ+8leaLc9lys2viEEr8md0U4RN6uOSUCE9bOYjQ9M=
|
||||
github.com/panjf2000/ants v1.3.0/go.mod h1:AaACblRPzq35m1g3enqYcxspbbiOJJYaxU2wMpm1cXY=
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
|
||||
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4=
|
||||
github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0=
|
||||
github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4=
|
||||
github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc=
|
||||
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
|
||||
go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4=
|
||||
go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM=
|
||||
go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8=
|
||||
go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E=
|
||||
go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc=
|
||||
go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ=
|
||||
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
|
||||
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
|
||||
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
||||
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
|
||||
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
|
||||
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.34.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4=
|
||||
google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
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=
|
||||
gorm.io/gorm v1.25.7 h1:VsD6acwRjz2zFxGO50gPO6AkNs7KKnvfzUjHQhZDz/A=
|
||||
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
|
||||
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
|
||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
||||
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
|
||||
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
44
log/log.go
Normal file
44
log/log.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"log"
|
||||
"msm/config"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
var Logger *zap.SugaredLogger
|
||||
|
||||
func init() {
|
||||
encoderConfig := zapcore.EncoderConfig{
|
||||
TimeKey: "time",
|
||||
LevelKey: "level",
|
||||
NameKey: "logger",
|
||||
CallerKey: "caller",
|
||||
MessageKey: "msg",
|
||||
StacktraceKey: "stacktrace",
|
||||
LineEnding: zapcore.DefaultLineEnding,
|
||||
EncodeLevel: zapcore.CapitalColorLevelEncoder,
|
||||
EncodeTime: zapcore.ISO8601TimeEncoder,
|
||||
EncodeDuration: zapcore.SecondsDurationEncoder,
|
||||
EncodeCaller: zapcore.FullCallerEncoder,
|
||||
}
|
||||
level, err := zapcore.ParseLevel(config.CF.LogLevel)
|
||||
if err != nil {
|
||||
log.Printf("日志等级错误!不存在“%v”日志等级", config.CF.LogLevel)
|
||||
level = zap.DebugLevel
|
||||
}
|
||||
atom := zap.NewAtomicLevelAt(level)
|
||||
zap.NewDevelopmentConfig()
|
||||
config := zap.Config{
|
||||
Level: atom,
|
||||
Development: true,
|
||||
Encoding: "console",
|
||||
EncoderConfig: encoderConfig,
|
||||
OutputPaths: []string{"stdout", "info.log"},
|
||||
ErrorOutputPaths: []string{"stderr"},
|
||||
}
|
||||
log, _ := config.Build()
|
||||
Logger = log.Sugar()
|
||||
}
|
15
main.go
Normal file
15
main.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"msm/boot"
|
||||
"msm/route"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
boot.Boot()
|
||||
// go termui.TermuiInit()
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
route.Route()
|
||||
}
|
18
model/config.go
Normal file
18
model/config.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package model
|
||||
|
||||
type Config struct {
|
||||
Id int `gorm:"column:id;primary_key"`
|
||||
Key string `gorm:"column:key"`
|
||||
Value string `gorm:"column:value"`
|
||||
}
|
||||
|
||||
func (n *Config) TableName() string {
|
||||
return "config"
|
||||
}
|
||||
|
||||
type SystemConfigurationResp struct {
|
||||
Key string `json:"key"`
|
||||
Value any `json:"value"`
|
||||
Default string `json:"default"`
|
||||
Describe string `json:"describe"`
|
||||
}
|
103
model/es.go
Normal file
103
model/es.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package model
|
||||
|
||||
type EsResult struct {
|
||||
Took int `json:"took"`
|
||||
TimedOut bool `json:"timed_out"`
|
||||
Shards Shards `json:"_shards"`
|
||||
Hits Hits `json:"hits"`
|
||||
}
|
||||
type Shards struct {
|
||||
Total int `json:"total"`
|
||||
Successful int `json:"successful"`
|
||||
Skipped int `json:"skipped"`
|
||||
Failed int `json:"failed"`
|
||||
}
|
||||
type Total struct {
|
||||
Value int `json:"value"`
|
||||
Relation string `json:"relation"`
|
||||
}
|
||||
type Source struct {
|
||||
Log string `json:"log"`
|
||||
Name string `json:"name"`
|
||||
Time int64 `json:"time"`
|
||||
Using string `json:"using"`
|
||||
}
|
||||
type HitsItem struct {
|
||||
Index string `json:"_index"`
|
||||
ID string `json:"_id"`
|
||||
Score interface{} `json:"_score"`
|
||||
Source Source `json:"_source"`
|
||||
Sort []int64 `json:"sort"`
|
||||
}
|
||||
type Hits struct {
|
||||
Total Total `json:"total"`
|
||||
MaxScore interface{} `json:"max_score"`
|
||||
Hits []HitsItem `json:"hits"`
|
||||
}
|
||||
|
||||
type GetLogReq struct {
|
||||
Match struct {
|
||||
Log string `json:"log"`
|
||||
Name string `json:"name"`
|
||||
Using string `json:"using"`
|
||||
} `json:"match"`
|
||||
TimeRange struct {
|
||||
StartTime int64 `json:"startTime"`
|
||||
EndTime int64 `json:"endTime"`
|
||||
} `json:"time"`
|
||||
Page struct {
|
||||
From int `json:"from"`
|
||||
Size int `json:"size"`
|
||||
} `json:"page"`
|
||||
Sort string `json:"sort"`
|
||||
}
|
||||
|
||||
type EsResp struct {
|
||||
Took int `json:"took"`
|
||||
TimedOut bool `json:"timed_out"`
|
||||
Shards struct {
|
||||
Total int `json:"total"`
|
||||
Successful int `json:"successful"`
|
||||
Skipped int `json:"skipped"`
|
||||
Failed int `json:"failed"`
|
||||
} `json:"_shards"`
|
||||
Hits struct {
|
||||
Total struct {
|
||||
Value int `json:"value"`
|
||||
Relation string `json:"relation"`
|
||||
} `json:"total"`
|
||||
MaxScore int `json:"max_score"`
|
||||
Hits []struct {
|
||||
Index string `json:"_index"`
|
||||
ID string `json:"_id"`
|
||||
Score int `json:"_score"`
|
||||
Source struct {
|
||||
Log string `json:"log"`
|
||||
Name string `json:"name"`
|
||||
Time int64 `json:"time"`
|
||||
Using string `json:"using"`
|
||||
} `json:"_source"`
|
||||
} `json:"hits"`
|
||||
} `json:"hits"`
|
||||
}
|
||||
|
||||
type LogResp struct {
|
||||
Total int `json:"total"`
|
||||
Data []Eslog `json:"data"`
|
||||
}
|
||||
|
||||
type Eslog struct {
|
||||
Log string `json:"log"`
|
||||
Time int64 `json:"time"`
|
||||
Name string `json:"name"`
|
||||
Using string `json:"using"`
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
type QueryBody struct {
|
||||
Query struct {
|
||||
Bool struct {
|
||||
Must []any `json:"must"`
|
||||
} `json:"bool"`
|
||||
} `json:"query"`
|
||||
}
|
6
model/file.go
Normal file
6
model/file.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package model
|
||||
|
||||
type FileStruct struct {
|
||||
Name string `json:"name"`
|
||||
IsDir bool `json:"isDir"`
|
||||
}
|
8
model/message.go
Normal file
8
model/message.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package model
|
||||
|
||||
|
||||
|
||||
type WsMessage struct {
|
||||
MessageType string `json:"messageType"`
|
||||
Content string `json:"content"`
|
||||
}
|
26
model/permission.go
Normal file
26
model/permission.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package model
|
||||
|
||||
type Permission struct {
|
||||
Id int64 `gorm:"column:id;NOT NULL" json:"id"`
|
||||
Account string `gorm:"column:account;NOT NULL" json:"account"`
|
||||
Pid int32 `gorm:"column:pid;NOT NULL" json:"pid"`
|
||||
Owned bool `gorm:"column:owned;NOT NULL" json:"owned"`
|
||||
Start bool `gorm:"column:start;NOT NULL" json:"start"`
|
||||
Stop bool `gorm:"column:stop;NOT NULL" json:"stop"`
|
||||
Terminal bool `gorm:"column:terminal;NOT NULL" json:"terminal"`
|
||||
}
|
||||
|
||||
func (*Permission) TableName() string {
|
||||
return "permission"
|
||||
}
|
||||
|
||||
type PermissionPo struct {
|
||||
Id int64 `gorm:"column:id" json:"id"`
|
||||
Account string `gorm:"column:account" json:"account"`
|
||||
Name string `gorm:"column:name" json:"name"`
|
||||
Pid int32 `gorm:"column:pid" json:"pid"`
|
||||
Owned bool `gorm:"column:owned" json:"owned"`
|
||||
Start bool `gorm:"column:start" json:"start"`
|
||||
Stop bool `gorm:"column:stop" json:"stop"`
|
||||
Terminal bool `gorm:"column:terminal" json:"terminal"`
|
||||
}
|
22
model/proc.go
Normal file
22
model/proc.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package model
|
||||
|
||||
type ProcessInfo struct {
|
||||
Name string `json:"name"`
|
||||
Uuid int `json:"uuid"`
|
||||
StartTime string `json:"startTime"`
|
||||
User string `json:"user"`
|
||||
Usage Usage `json:"usage"`
|
||||
State State `json:"state"`
|
||||
TermType string `json:"termType"`
|
||||
}
|
||||
|
||||
type Usage struct {
|
||||
Cpu []float64 `json:"cpu"`
|
||||
Mem []float64 `json:"mem"`
|
||||
Time []string `json:"time"`
|
||||
}
|
||||
|
||||
type State struct {
|
||||
State uint8 `json:"state"`
|
||||
Info string `json:"info"`
|
||||
}
|
16
model/process.go
Normal file
16
model/process.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package model
|
||||
|
||||
type Process struct {
|
||||
Uuid int `gorm:"primaryKey;autoIncrement;column:uuid" json:"uuid"`
|
||||
Name string `gorm:"column:name" json:"name"`
|
||||
Cmd string `gorm:"column:args" json:"cmd"`
|
||||
Cwd string `gorm:"column:cwd" json:"cwd"`
|
||||
AutoRestart bool `gorm:"column:auto_restart" json:"autoRestart"`
|
||||
Push bool `gorm:"column:push" json:"push"`
|
||||
LogReport bool `gorm:"column:log_report" json:"logReport"`
|
||||
TermType string `gorm:"column:term_type" json:"termType"`
|
||||
}
|
||||
|
||||
func (*Process) TableName() string {
|
||||
return "process"
|
||||
}
|
14
model/push_msg.go
Normal file
14
model/push_msg.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package model
|
||||
|
||||
type Push struct {
|
||||
Id int64 `gorm:"column:id;NOT NULL" json:"id"`
|
||||
Method string `gorm:"column:method;NOT NULL" json:"method"`
|
||||
Url string `gorm:"column:url;NOT NULL" json:"url"`
|
||||
Body string `gorm:"column:body;NOT NULL" json:"body"`
|
||||
Remark string `gorm:"column:remark;NOT NULL" json:"remark"`
|
||||
Enable bool `gorm:"column:enable;NOT NULL" json:"enable"`
|
||||
}
|
||||
|
||||
func (*Push) TableName() string {
|
||||
return "push"
|
||||
}
|
15
model/user.go
Normal file
15
model/user.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
type User struct {
|
||||
Account string `json:"account" gorm:"primaryKey;column:account" `
|
||||
Password string `json:"password" gorm:"column:password" `
|
||||
Role int `json:"role" gorm:"column:role" `
|
||||
CreateTime time.Time `json:"createTime" gorm:"column:create_time" `
|
||||
Remark string `json:"remark" gorm:"column:remark" `
|
||||
}
|
||||
|
||||
func (*User) TableName() string {
|
||||
return "users"
|
||||
}
|
26
route/middle/panic.go
Normal file
26
route/middle/panic.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package middle
|
||||
|
||||
import (
|
||||
"msm/consts/ctxflag"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func PanicMiddle() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
defer func() {
|
||||
if err := recover(); err == 0 {
|
||||
if err, ok := c.Get(ctxflag.ERR); ok {
|
||||
rErr(c, -1, err.(error).Error(), err.(error))
|
||||
} else {
|
||||
rErr(c, -1, "内部错误", nil)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
c.Next()
|
||||
}
|
||||
}
|
44
route/middle/permission.go
Normal file
44
route/middle/permission.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package middle
|
||||
|
||||
import (
|
||||
"msm/consts/ctxflag"
|
||||
"msm/consts/permission"
|
||||
"msm/consts/role"
|
||||
"msm/dao"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func RolePermission(needPermission role.Role) func(ctx *gin.Context) {
|
||||
return func(ctx *gin.Context) {
|
||||
if r := ctx.GetInt(ctxflag.ROLE); r > int(needPermission) {
|
||||
rErr(ctx, -1, "角色权限不足", nil)
|
||||
ctx.Abort()
|
||||
return
|
||||
}
|
||||
ctx.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func OprPermission(op permission.OprPermission) func(ctx *gin.Context) {
|
||||
return func(ctx *gin.Context) {
|
||||
uuid, err := strconv.Atoi(ctx.Query("uuid"))
|
||||
if err != nil {
|
||||
rErr(ctx, -1, "参数有误", nil)
|
||||
ctx.Abort()
|
||||
return
|
||||
}
|
||||
if ctx.GetInt(ctxflag.ROLE) < int(role.USER) {
|
||||
ctx.Next()
|
||||
return
|
||||
}
|
||||
if !reflect.ValueOf(dao.PermissionDao.GetPermission(ctx.GetString(ctxflag.USER_NAME), uuid)).FieldByName(string(op)).Bool() {
|
||||
rErr(ctx, -1, "操作权限不足", nil)
|
||||
ctx.Abort()
|
||||
return
|
||||
}
|
||||
ctx.Next()
|
||||
}
|
||||
}
|
73
route/middle/token.go
Normal file
73
route/middle/token.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package middle
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"msm/consts/ctxflag"
|
||||
"msm/dao"
|
||||
"msm/log"
|
||||
"msm/utils"
|
||||
"slices"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// code -1为失败,-2为token失效
|
||||
func rErr(ctx *gin.Context, code int, message string, err error) {
|
||||
var statusCode int
|
||||
switch code {
|
||||
case -1:
|
||||
statusCode = 500
|
||||
case -2:
|
||||
statusCode = 401
|
||||
default:
|
||||
statusCode = 200
|
||||
}
|
||||
log.Logger.Warn(err)
|
||||
ctx.JSON(statusCode, map[string]any{
|
||||
"code": code,
|
||||
"msg": message,
|
||||
})
|
||||
ctx.Abort()
|
||||
}
|
||||
|
||||
func CheckToken() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
whiteList := []string{
|
||||
"/api/user/login",
|
||||
"/api/user/register/admin",
|
||||
}
|
||||
if !slices.Contains(whiteList, c.Request.URL.Path) {
|
||||
var token string
|
||||
if c.Request.Header.Get("token") != "" {
|
||||
token = c.Request.Header.Get("token")
|
||||
} else {
|
||||
token = c.Query("token")
|
||||
}
|
||||
if _, err := utils.ParseToken(token); err != nil {
|
||||
rErr(c, -2, "token校验失败", err)
|
||||
return
|
||||
}
|
||||
if username, err := getUser(c); err != nil {
|
||||
rErr(c, -1, "无法获取user信息", err)
|
||||
} else {
|
||||
c.Set(ctxflag.USER_NAME, username)
|
||||
c.Set(ctxflag.ROLE, dao.UserDao.GetUserByName(username).Role)
|
||||
}
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func getUser(ctx *gin.Context) (string, error) {
|
||||
var token string
|
||||
if ctx.Request.Header.Get("token") != "" {
|
||||
token = ctx.Request.Header.Get("token")
|
||||
} else {
|
||||
token = ctx.Query("token")
|
||||
}
|
||||
if mc, err := utils.ParseToken(token); err == nil && mc != nil {
|
||||
return mc.UserName, nil
|
||||
} else {
|
||||
return "", errors.Join(errors.New("用户信息获取失败"), err)
|
||||
}
|
||||
}
|
111
route/route.go
Normal file
111
route/route.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"io"
|
||||
"msm/api"
|
||||
"msm/config"
|
||||
"msm/consts/permission"
|
||||
"msm/consts/role"
|
||||
"msm/log"
|
||||
"msm/route/middle"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-contrib/pprof"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func Route() {
|
||||
r := gin.Default()
|
||||
gin.DefaultWriter = io.Discard
|
||||
gin.SetMode(gin.DebugMode)
|
||||
routePathInit(r)
|
||||
staticInit(r)
|
||||
pprofInit(r)
|
||||
r.Run(config.CF.Listen)
|
||||
}
|
||||
|
||||
func staticInit(r *gin.Engine) {
|
||||
r.NoRoute(func(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "index.html", gin.H{})
|
||||
})
|
||||
r.Static("/js", "templates/js")
|
||||
r.Static("/css", "templates/css")
|
||||
r.Static("/media", "templates/media")
|
||||
r.Static("/fonts", "templates/fonts")
|
||||
r.LoadHTMLFiles("templates/index.html")
|
||||
}
|
||||
|
||||
func pprofInit(r *gin.Engine) {
|
||||
if config.CF.PprofEnable {
|
||||
pprof.Register(r)
|
||||
log.Logger.Info("启用 pprof")
|
||||
}
|
||||
}
|
||||
|
||||
func routePathInit(r *gin.Engine) {
|
||||
apiGroup := r.Group("/api")
|
||||
apiGroup.Use(middle.CheckToken())
|
||||
apiGroup.Use(middle.PanicMiddle())
|
||||
{
|
||||
apiGroup.GET("/ws", middle.OprPermission(permission.TERMINAL_OPERATION), api.WsApi.WebsocketHandle)
|
||||
|
||||
processGroup := apiGroup.Group("/process")
|
||||
{
|
||||
processGroup.DELETE("", middle.OprPermission(permission.STOP_OPERATION), api.ProcApi.KillProcess)
|
||||
processGroup.GET("", api.ProcApi.GetProcessList)
|
||||
processGroup.PUT("", middle.OprPermission(permission.START_OPERATION), api.ProcApi.StartProcess)
|
||||
processGroup.GET("/control", middle.RolePermission(role.ADMIN), api.ProcApi.ProcessControl)
|
||||
|
||||
proConfigGroup := processGroup.Group("/config")
|
||||
{
|
||||
proConfigGroup.POST("", middle.RolePermission(role.ROOT), api.ProcApi.CreateNewProcess)
|
||||
proConfigGroup.DELETE("", middle.RolePermission(role.ROOT), api.ProcApi.DeleteNewProcess)
|
||||
proConfigGroup.PUT("", middle.RolePermission(role.ROOT), api.ProcApi.UpdateProcessConfig)
|
||||
proConfigGroup.GET("", middle.RolePermission(role.ADMIN), api.ProcApi.GetProcessConfig)
|
||||
}
|
||||
}
|
||||
|
||||
userGroup := apiGroup.Group("/user")
|
||||
{
|
||||
userGroup.POST("/login", api.UserApi.LoginHandler)
|
||||
userGroup.POST("", middle.RolePermission(role.ROOT), api.UserApi.CreateUser)
|
||||
userGroup.PUT("/password", middle.RolePermission(role.USER), api.UserApi.ChangePassword)
|
||||
userGroup.DELETE("", middle.RolePermission(role.ROOT), api.UserApi.DeleteUser)
|
||||
userGroup.GET("", middle.RolePermission(role.ROOT), api.UserApi.GetUserList)
|
||||
}
|
||||
|
||||
pushGroup := apiGroup.Group("/push").Use(middle.RolePermission(role.ADMIN))
|
||||
{
|
||||
pushGroup.GET("/list", api.PushApi.GetPushList)
|
||||
pushGroup.GET("", api.PushApi.GetPushById)
|
||||
pushGroup.POST("", api.PushApi.AddPushConfig)
|
||||
pushGroup.PUT("", api.PushApi.UpdatePushConfig)
|
||||
pushGroup.DELETE("", api.PushApi.DeletePushConfig)
|
||||
}
|
||||
|
||||
fileGroup := apiGroup.Group("/file").Use(middle.RolePermission(role.ADMIN))
|
||||
{
|
||||
fileGroup.GET("/list", api.FileApi.FilePathHandler)
|
||||
fileGroup.PUT("", api.FileApi.FileWriteHandler)
|
||||
fileGroup.GET("", api.FileApi.FileReadHandler)
|
||||
}
|
||||
|
||||
permissionGroup := apiGroup.Group("/permission").Use(middle.RolePermission(role.ROOT))
|
||||
{
|
||||
permissionGroup.GET("/list", api.PermissionApi.GetPermissionList)
|
||||
permissionGroup.PUT("", api.PermissionApi.EditPermssion)
|
||||
}
|
||||
|
||||
logGroup := apiGroup.Group("/log").Use(middle.RolePermission(role.ADMIN))
|
||||
{
|
||||
logGroup.POST("", api.LogApi.GetLog)
|
||||
}
|
||||
|
||||
configGroup := apiGroup.Group("/config").Use(middle.RolePermission(role.ROOT))
|
||||
{
|
||||
configGroup.GET("", api.ConfigApi.GetSystemConfiguration)
|
||||
configGroup.PUT("", api.ConfigApi.SetSystemConfiguration)
|
||||
configGroup.PUT("/es", api.ConfigApi.EsConfigReload)
|
||||
}
|
||||
}
|
||||
}
|
128
server_config.json
Normal file
128
server_config.json
Normal file
@@ -0,0 +1,128 @@
|
||||
{
|
||||
"user": [
|
||||
{
|
||||
"account": "admin",
|
||||
"password": "91cd3b960f31c06fc4048ff44e6654c1"
|
||||
},
|
||||
{
|
||||
"account": "user",
|
||||
"password": "5f4dcc3b5aa765d61d8327deb882cf99"
|
||||
}
|
||||
],
|
||||
"server": [
|
||||
{
|
||||
"name": "bungeecord",
|
||||
"args": [
|
||||
"/usr/lib/jvm/java-17-openjdk-amd64/bin/java",
|
||||
"-Xmx580M",
|
||||
"-Xms100M",
|
||||
"-jar",
|
||||
"waterfall-1.19-510.jar"
|
||||
],
|
||||
"cwd": "/MCS/BungeeCord",
|
||||
"autoRestart": true,
|
||||
"push": true,
|
||||
"logReport": true
|
||||
},
|
||||
{
|
||||
"name": "lobby",
|
||||
"args": [
|
||||
"java",
|
||||
"-jar",
|
||||
"-server",
|
||||
"-Xmx1000M",
|
||||
"paper-1.16.5-794.jar"
|
||||
],
|
||||
"cwd": "/MCS/lobby",
|
||||
"autoRestart": true,
|
||||
"push": true,
|
||||
"logReport": true
|
||||
},
|
||||
{
|
||||
"name": "main",
|
||||
"args": [
|
||||
"java",
|
||||
"-jar",
|
||||
"-server",
|
||||
"-Xmx8000M",
|
||||
"launcher-airplane.jar"
|
||||
],
|
||||
"cwd": "/MCS",
|
||||
"autoRestart": true,
|
||||
"push": true,
|
||||
"logReport": true
|
||||
},
|
||||
{
|
||||
"name": "s1",
|
||||
"args": [
|
||||
"java",
|
||||
"-jar",
|
||||
"-server",
|
||||
"-Xmx5000M",
|
||||
"launcher-airplane.jar"
|
||||
],
|
||||
"cwd": "/MCS/server1",
|
||||
"autoRestart": true,
|
||||
"push": true,
|
||||
"logReport": true
|
||||
},
|
||||
{
|
||||
"name": "起床战争bungeecord",
|
||||
"args": [
|
||||
"java",
|
||||
"-jar",
|
||||
"BB.jar"
|
||||
],
|
||||
"cwd": "/MCS/bed/[25565]BungeeCord",
|
||||
"autoRestart": true
|
||||
},
|
||||
{
|
||||
"name": "起床战争looby",
|
||||
"args": [
|
||||
"java",
|
||||
"-jar",
|
||||
"paper-1.16.5-794.jar"
|
||||
],
|
||||
"cwd": "/MCS/bed/[25566]Lobby",
|
||||
"autoRestart": true
|
||||
},
|
||||
{
|
||||
"name": "起床战争入侵",
|
||||
"args": [
|
||||
"/usr/lib/jvm/java-8-openjdk-amd64/bin/java",
|
||||
"-jar",
|
||||
"RQS.jar"
|
||||
],
|
||||
"cwd": "/MCS/bed/[20003]BedWars-入侵",
|
||||
"autoRestart": true
|
||||
},
|
||||
{
|
||||
"name": "起床战争蘑菇",
|
||||
"args": [
|
||||
"/usr/lib/jvm/java-8-openjdk-amd64/bin/java",
|
||||
"-jar",
|
||||
"PaperSpigot-1.8.8.jar"
|
||||
],
|
||||
"cwd": "/MCS/bed/[10002]BedWarsXP-蘑菇",
|
||||
"autoRestart": true
|
||||
}
|
||||
],
|
||||
"mq": {
|
||||
"enable": true,
|
||||
"mqurl": "amqp://admin:1625167628%40xcon@xcon.top:5672/",
|
||||
"queue_name": "log_queue_publisher",
|
||||
"exchange": "log_exchange_publisher",
|
||||
"routing_key": "server_log"
|
||||
},
|
||||
"push": {
|
||||
"feishu": {
|
||||
"enable": true,
|
||||
"webhook": "https://open.feishu.cn/open-apis/bot/v2/hook/86f491ba-6c7a-413b-86e6-d72420934bd9"
|
||||
},
|
||||
"wechat":{
|
||||
"enable": true,
|
||||
"webhook": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=5f3fae11-da5c-45a7-8ae9-ceaca2dc1495"
|
||||
}
|
||||
},
|
||||
"logLevel": "debug"
|
||||
}
|
155
service/es/es.go
Normal file
155
service/es/es.go
Normal file
@@ -0,0 +1,155 @@
|
||||
package es
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"msm/config"
|
||||
"msm/log"
|
||||
"msm/model"
|
||||
|
||||
"github.com/elastic/go-elasticsearch/v8"
|
||||
"github.com/elastic/go-elasticsearch/v8/esapi"
|
||||
)
|
||||
|
||||
var esClient *elasticsearch.Client
|
||||
|
||||
type esService struct{}
|
||||
|
||||
var EsService = new(esService)
|
||||
|
||||
func InitEs() bool {
|
||||
if config.CF.EsEnable {
|
||||
cfg := elasticsearch.Config{
|
||||
Addresses: []string{
|
||||
config.CF.EsUrl,
|
||||
},
|
||||
Username: config.CF.EsUsername,
|
||||
Password: config.CF.EsPassword,
|
||||
}
|
||||
var err error
|
||||
esClient, err = elasticsearch.NewClient(cfg)
|
||||
if err != nil {
|
||||
log.Logger.Fatalln("Failed to connect to es")
|
||||
}
|
||||
_, err = esClient.Info()
|
||||
if err != nil {
|
||||
log.Logger.Error("es启动失败", err)
|
||||
config.CF.EsEnable = false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
log.Logger.Debug("不使用es")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// idx 为空,默认随机唯一字符串
|
||||
func (e *esService) Index(index, idx string, doc map[string]interface{}) {
|
||||
var buf bytes.Buffer
|
||||
if err := json.NewEncoder(&buf).Encode(doc); err != nil {
|
||||
log.Logger.Error(err, "Error encoding doc")
|
||||
return
|
||||
}
|
||||
res, err := esClient.Index(
|
||||
index,
|
||||
&buf,
|
||||
esClient.Index.WithDocumentID(idx),
|
||||
esClient.Index.WithRefresh("true"),
|
||||
)
|
||||
if err != nil {
|
||||
log.Logger.Error(err, "Error create response")
|
||||
}
|
||||
defer res.Body.Close()
|
||||
}
|
||||
|
||||
func (e *esService) Insert(log string, processName string, using string, ts int64) {
|
||||
doc := map[string]interface{}{
|
||||
"log": log,
|
||||
"name": processName,
|
||||
"using": using,
|
||||
"time": ts,
|
||||
}
|
||||
e.Index(config.CF.EsIndex, "", doc)
|
||||
}
|
||||
|
||||
func (e *esService) Search(req model.GetLogReq) model.LogResp {
|
||||
query := []func(*esapi.SearchRequest){
|
||||
esClient.Search.WithIndex(config.CF.EsIndex),
|
||||
esClient.Search.WithContext(context.Background()),
|
||||
esClient.Search.WithPretty(),
|
||||
esClient.Search.WithTrackTotalHits(true),
|
||||
esClient.Search.WithFrom(req.Page.From),
|
||||
esClient.Search.WithSize(req.Page.Size),
|
||||
}
|
||||
if req.Sort == "asc" {
|
||||
query = append(query, esClient.Search.WithSort("time:asc"))
|
||||
}
|
||||
if req.Sort == "desc" {
|
||||
query = append(query, esClient.Search.WithSort("time:desc"))
|
||||
}
|
||||
body := e.buildQueryBody(req)
|
||||
var buf bytes.Buffer
|
||||
if err := json.NewEncoder(&buf).Encode(body); err != nil {
|
||||
log.Logger.Error(err)
|
||||
return model.LogResp{}
|
||||
}
|
||||
query = append(query, esClient.Search.WithBody(&buf))
|
||||
res, err := esClient.Search(query...)
|
||||
if err != nil {
|
||||
log.Logger.Error(err)
|
||||
return model.LogResp{}
|
||||
}
|
||||
resp := model.EsResp{}
|
||||
json.NewDecoder(res.Body).Decode(&resp)
|
||||
res.Body.Close()
|
||||
result := model.LogResp{}
|
||||
for _, v := range resp.Hits.Hits {
|
||||
result.Data = append(result.Data, model.Eslog{
|
||||
Log: v.Source.Log,
|
||||
Name: v.Source.Name,
|
||||
Using: v.Source.Using,
|
||||
Time: v.Source.Time,
|
||||
Id: v.ID,
|
||||
})
|
||||
}
|
||||
result.Total = resp.Hits.Total.Value
|
||||
return result
|
||||
}
|
||||
|
||||
func (e *esService) buildQueryBody(req model.GetLogReq) model.QueryBody {
|
||||
result := model.QueryBody{}
|
||||
if req.TimeRange.EndTime != 0 || req.TimeRange.StartTime != 0 {
|
||||
result.Query.Bool.Must = append(result.Query.Bool.Must, map[string]any{
|
||||
"range": map[string]any{
|
||||
"time": map[string]any{
|
||||
"gte": req.TimeRange.StartTime,
|
||||
"lte": req.TimeRange.EndTime,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
if req.Match.Log != "" {
|
||||
result.Query.Bool.Must = append(result.Query.Bool.Must, map[string]any{
|
||||
"match": map[string]any{
|
||||
"log": req.Match.Log,
|
||||
},
|
||||
})
|
||||
}
|
||||
if req.Match.Name != "" {
|
||||
result.Query.Bool.Must = append(result.Query.Bool.Must, map[string]any{
|
||||
"match": map[string]any{
|
||||
"name": req.Match.Name,
|
||||
},
|
||||
})
|
||||
}
|
||||
if req.Match.Using != "" {
|
||||
result.Query.Bool.Must = append(result.Query.Bool.Must, map[string]any{
|
||||
"match": map[string]any{
|
||||
"using": req.Match.Using,
|
||||
},
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
76
service/file/file.go
Normal file
76
service/file/file.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"msm/config"
|
||||
"msm/log"
|
||||
"msm/model"
|
||||
"os"
|
||||
)
|
||||
|
||||
type fileService struct{}
|
||||
|
||||
var FileService = new(fileService)
|
||||
|
||||
func (f *fileService) ReadFileFromPath(path string) (result []byte, err error) {
|
||||
fi, err := os.Open(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer fi.Close()
|
||||
fileInfo, err := fi.Stat()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if size := float64(fileInfo.Size()) / 1e6; size > config.CF.FileSizeLimit {
|
||||
err = fmt.Errorf("写入数据大小%vMB,超过%vMB限制", size, config.CF.FileSizeLimit)
|
||||
return
|
||||
}
|
||||
result, err = io.ReadAll(fi)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
log.Logger.Debugw("文件写入成功", "path", path)
|
||||
return
|
||||
}
|
||||
|
||||
func (f *fileService) UpdateFileData(filePath string, file io.Reader, size int64) error {
|
||||
if size := float64(size) / 1e6; size > config.CF.FileSizeLimit {
|
||||
return fmt.Errorf("写入数据大小%vMB,超过%vMB限制", size, config.CF.FileSizeLimit)
|
||||
}
|
||||
fi, err := os.OpenFile(filePath, os.O_RDWR|os.O_TRUNC, 0777)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fi.Close()
|
||||
if _, err = io.Copy(fi, file); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Logger.Debugw("文件写入成功", "path", filePath)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fileService) GetFileAndDirByPath(srcPath string) ([]model.FileStruct, error) {
|
||||
result := []model.FileStruct{}
|
||||
files, err := os.ReadDir(srcPath)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
for _, file := range files {
|
||||
result = append(result, model.FileStruct{
|
||||
Name: file.Name(),
|
||||
IsDir: file.IsDir(),
|
||||
})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (f *fileService) CreateNewDir(path string, name string) error {
|
||||
_, err := os.Create(path + name)
|
||||
return err
|
||||
}
|
||||
|
||||
func (f *fileService) CreateNewFile(path string, name string) error {
|
||||
return os.MkdirAll(path+name, os.ModeDir)
|
||||
}
|
40
service/log/loghandler.go
Normal file
40
service/log/loghandler.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package loghandler
|
||||
|
||||
import (
|
||||
"msm/log"
|
||||
"msm/model"
|
||||
"msm/service/es"
|
||||
"time"
|
||||
|
||||
"github.com/panjf2000/ants"
|
||||
)
|
||||
|
||||
type loghandler struct{}
|
||||
|
||||
var (
|
||||
antsPool *ants.PoolWithFunc
|
||||
Loghandler = new(loghandler)
|
||||
|
||||
logHanleFunc = func(i interface{}) {
|
||||
esLog, ok := i.(model.Eslog)
|
||||
if !ok {
|
||||
log.Logger.Panicw("传入错误参数", "data", esLog)
|
||||
return
|
||||
}
|
||||
es.EsService.Insert(esLog.Log, esLog.Name, esLog.Using, esLog.Time)
|
||||
}
|
||||
|
||||
panicHanlderFunc = func(i interface{}) {
|
||||
log.Logger.Error("es消息储存失败")
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
antsPool, _ = ants.NewPoolWithFunc(1000, logHanleFunc, ants.WithPanicHandler(panicHanlderFunc), ants.WithExpiryDuration(time.Second*10))
|
||||
}
|
||||
|
||||
func (l *loghandler) AddLog(data model.Eslog) {
|
||||
if err := antsPool.Invoke(data); err != nil {
|
||||
log.Logger.Errorw("协程池添加任务失败", "err", err, "当前运行数量", antsPool.Running())
|
||||
}
|
||||
}
|
324
service/process/proccess.go
Normal file
324
service/process/proccess.go
Normal file
@@ -0,0 +1,324 @@
|
||||
package process
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"msm/config"
|
||||
"msm/log"
|
||||
"msm/model"
|
||||
loghandler "msm/service/log"
|
||||
"msm/service/push"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
pu "github.com/shirou/gopsutil/process"
|
||||
)
|
||||
|
||||
type Process interface {
|
||||
ReadCache(*websocket.Conn)
|
||||
GetName() string
|
||||
SetName(string)
|
||||
GetTermType() string
|
||||
SetTermType(string)
|
||||
SetIsUsing(bool)
|
||||
GetWhoUsing() string
|
||||
SetWhoUsing(string)
|
||||
SetStartCommand([]string)
|
||||
GetControlController() string
|
||||
SetControlController(string)
|
||||
ChangControlChan() chan int
|
||||
StopChan() chan struct{}
|
||||
SetConfigLogReport(bool)
|
||||
SetConfigStatuPush(bool)
|
||||
SetConfigAutoRestart(bool)
|
||||
GetStateInfo() string
|
||||
GetStateState() uint8
|
||||
Kill() error
|
||||
SetWsConn(*websocket.Conn)
|
||||
Write(string) error
|
||||
WriteBytes([]byte) error
|
||||
GetStartTimeFormat() string
|
||||
VerifyControl() bool
|
||||
ResetRestartTimes()
|
||||
InitPerformanceStatus()
|
||||
ProcessControl(string)
|
||||
AddCpuUsage(float64)
|
||||
AddMemUsage(float64)
|
||||
AddRecordTime()
|
||||
GetTimeRecord() []string
|
||||
GetMemUsage() []float64
|
||||
GetCpuUsage() []float64
|
||||
monitorHanler()
|
||||
initPsutil()
|
||||
SetAutoRestart(bool)
|
||||
TryLock() bool
|
||||
Unlock()
|
||||
ReStart()
|
||||
}
|
||||
|
||||
type ProcessBase struct {
|
||||
Name string
|
||||
termType string
|
||||
Pid int
|
||||
cmd *exec.Cmd
|
||||
IsUsing atomic.Bool
|
||||
StartCommand []string
|
||||
Lock sync.Mutex
|
||||
WhoUsing string
|
||||
stopChan chan struct{}
|
||||
Control struct {
|
||||
Controller string
|
||||
changControlChan chan int
|
||||
changControlTime time.Time
|
||||
}
|
||||
ws struct {
|
||||
wsConnect *websocket.Conn
|
||||
wsMux sync.RWMutex
|
||||
}
|
||||
Config struct {
|
||||
AutoRestart bool
|
||||
statuPush bool
|
||||
logReport bool
|
||||
}
|
||||
State struct {
|
||||
startTime time.Time
|
||||
Info string
|
||||
State uint8 //0 为未运行,1为运作中,2为异常状态
|
||||
restartTimes int
|
||||
}
|
||||
performanceStatus struct {
|
||||
cpu []float64
|
||||
mem []float64
|
||||
time []string
|
||||
}
|
||||
monitor struct {
|
||||
enable bool
|
||||
ProcessBase *pu.Process
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ProcessBase) GetTermType() string {
|
||||
return p.termType
|
||||
}
|
||||
|
||||
func (p *ProcessBase) SetTermType(s string) {
|
||||
p.termType = s
|
||||
}
|
||||
|
||||
func (p *ProcessBase) GetStateInfo() string {
|
||||
return p.State.Info
|
||||
}
|
||||
|
||||
func (p *ProcessBase) GetStateState() uint8 {
|
||||
return p.State.State
|
||||
}
|
||||
|
||||
func (p *ProcessBase) SetAutoRestart(data bool) {
|
||||
p.Config.AutoRestart = data
|
||||
}
|
||||
|
||||
func (p *ProcessBase) GetWhoUsing() string {
|
||||
return p.WhoUsing
|
||||
}
|
||||
|
||||
func (p *ProcessBase) GetControlController() string {
|
||||
return p.Control.Controller
|
||||
}
|
||||
|
||||
func (p *ProcessBase) SetControlController(c string) {
|
||||
p.Control.Controller = c
|
||||
}
|
||||
|
||||
func (p *ProcessBase) SetWsConn(ws *websocket.Conn) {
|
||||
p.ws.wsConnect = ws
|
||||
}
|
||||
|
||||
func (p *ProcessBase) logReportHandler(log string) {
|
||||
if config.CF.EsEnable && p.Config.logReport && len([]rune(log)) > config.CF.LogMinLenth {
|
||||
loghandler.Loghandler.AddLog(model.Eslog{
|
||||
Log: log,
|
||||
Using: p.WhoUsing,
|
||||
Name: p.Name,
|
||||
Time: time.Now().UnixMilli(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ProcessBase) GetStartTimeFormat() string {
|
||||
return p.State.startTime.Format(time.DateTime)
|
||||
}
|
||||
|
||||
func (p *ProcessBase) ProcessControl(name string) {
|
||||
p.Control.changControlTime = time.Now()
|
||||
p.Control.Controller = name
|
||||
if p.State.State == 1 && p.IsUsing.Load() {
|
||||
p.Control.changControlChan <- 0
|
||||
}
|
||||
}
|
||||
|
||||
// 没人在使用或控制时间过期
|
||||
func (p *ProcessBase) VerifyControl() bool {
|
||||
return p.Control.Controller == "" || p.Control.changControlTime.Unix() < time.Now().Unix()-config.CF.ProcessExpireTime
|
||||
}
|
||||
|
||||
func (p *ProcessBase) setProcessConfig(pconfig model.Process) {
|
||||
p.Config.AutoRestart = pconfig.AutoRestart
|
||||
p.Config.logReport = pconfig.LogReport
|
||||
p.Config.statuPush = pconfig.Push
|
||||
}
|
||||
|
||||
func (p *ProcessBase) ResetRestartTimes() {
|
||||
p.State.restartTimes = 0
|
||||
}
|
||||
|
||||
func (p *ProcessBase) push(message string) {
|
||||
if p.Config.statuPush {
|
||||
messagePlaceholders := map[string]string{
|
||||
"{$name}": p.Name,
|
||||
"{$user}": p.WhoUsing,
|
||||
"{$message}": message,
|
||||
"{$status}": strconv.Itoa(int(p.State.State)),
|
||||
}
|
||||
push.PushService.Push(messagePlaceholders)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ProcessBase) InitPerformanceStatus() {
|
||||
p.performanceStatus.cpu = make([]float64, config.CF.PerformanceInfoListLength)
|
||||
p.performanceStatus.mem = make([]float64, config.CF.PerformanceInfoListLength)
|
||||
p.performanceStatus.time = make([]string, config.CF.PerformanceInfoListLength)
|
||||
}
|
||||
|
||||
func (p *ProcessBase) AddCpuUsage(usage float64) {
|
||||
p.performanceStatus.cpu = append(p.performanceStatus.cpu[1:], usage)
|
||||
}
|
||||
|
||||
func (p *ProcessBase) AddMemUsage(usage float64) {
|
||||
p.performanceStatus.mem = append(p.performanceStatus.mem[1:], usage)
|
||||
}
|
||||
|
||||
func (p *ProcessBase) AddRecordTime() {
|
||||
p.performanceStatus.time = append(p.performanceStatus.time[1:], time.Now().Format(time.DateTime))
|
||||
}
|
||||
func (p *ProcessBase) GetCpuUsage() []float64 {
|
||||
return p.performanceStatus.cpu
|
||||
}
|
||||
|
||||
func (p *ProcessBase) GetMemUsage() []float64 {
|
||||
return p.performanceStatus.mem
|
||||
}
|
||||
|
||||
func (p *ProcessBase) GetTimeRecord() []string {
|
||||
return p.performanceStatus.time
|
||||
}
|
||||
|
||||
func (p *ProcessBase) monitorHanler() {
|
||||
defer log.Logger.Infow("性能监控结束", "name", p.Name, "pid", p.Pid)
|
||||
for {
|
||||
if !p.monitor.enable {
|
||||
return
|
||||
}
|
||||
select {
|
||||
case <-time.After(time.Minute * time.Duration(config.CF.PerformanceInfoInterval)):
|
||||
if p.State.State != 1 {
|
||||
log.Logger.Debugw("进程状态异常,跳过监控数据获取", "name", p.Name)
|
||||
p.AddCpuUsage(0)
|
||||
p.AddMemUsage(0)
|
||||
p.AddRecordTime()
|
||||
continue
|
||||
}
|
||||
ProcessBase := p.monitor.ProcessBase
|
||||
cpuPercent, err := ProcessBase.CPUPercent()
|
||||
if err != nil {
|
||||
log.Logger.Errorw("CPU使用率获取失败", "err", err)
|
||||
return
|
||||
}
|
||||
memInfo, err := ProcessBase.MemoryInfo()
|
||||
if err != nil {
|
||||
log.Logger.Errorw("内存使用率获取失败", "err", err)
|
||||
return
|
||||
}
|
||||
p.AddRecordTime()
|
||||
p.AddCpuUsage(cpuPercent)
|
||||
p.AddMemUsage(float64(memInfo.RSS / 1000))
|
||||
log.Logger.Debugw("进程资源使用率获取成功", "pid", p.Pid, "name", p.Name, "cpu", cpuPercent, "mem", memInfo.RSS)
|
||||
case <-p.stopChan:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ProcessBase) initPsutil() {
|
||||
pup, err := pu.NewProcess(int32(p.Pid))
|
||||
if err != nil {
|
||||
p.monitor.enable = false
|
||||
log.Logger.Debug("pu进程获取失败")
|
||||
} else {
|
||||
p.monitor.enable = true
|
||||
log.Logger.Debug("pu进程获取成功")
|
||||
p.monitor.ProcessBase = pup
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ProcessBase) SetConfigLogReport(b bool) {
|
||||
p.Config.logReport = b
|
||||
}
|
||||
|
||||
func (p *ProcessBase) SetConfigAutoRestart(b bool) {
|
||||
p.Config.AutoRestart = b
|
||||
}
|
||||
|
||||
func (p *ProcessBase) SetConfigStatuPush(b bool) {
|
||||
p.Config.statuPush = b
|
||||
}
|
||||
|
||||
func (p *ProcessBase) SetName(s string) {
|
||||
p.Name = s
|
||||
}
|
||||
|
||||
func (p *ProcessBase) SetStartCommand(cmd []string) {
|
||||
p.StartCommand = cmd
|
||||
}
|
||||
|
||||
func (p *ProcessBase) ChangControlChan() chan int {
|
||||
return p.Control.changControlChan
|
||||
}
|
||||
|
||||
func (p *ProcessBase) SetIsUsing(b bool) {
|
||||
p.IsUsing.Store(b)
|
||||
}
|
||||
|
||||
func (p *ProcessBase) GetName() string {
|
||||
return p.Name
|
||||
}
|
||||
|
||||
func (p *ProcessBase) SetWhoUsing(s string) {
|
||||
p.WhoUsing = s
|
||||
}
|
||||
|
||||
func (p *ProcessBase) StopChan() chan struct{} {
|
||||
return p.stopChan
|
||||
}
|
||||
|
||||
func (p *ProcessBase) TryLock() bool {
|
||||
return p.Lock.TryLock()
|
||||
}
|
||||
|
||||
func (p *ProcessBase) Unlock() {
|
||||
p.Lock.Unlock()
|
||||
}
|
||||
|
||||
func RunNewProcess(config model.Process) (proc Process, err error) {
|
||||
switch config.TermType {
|
||||
case "std":
|
||||
proc, err = RunNewProcessStd(config)
|
||||
case "pty":
|
||||
proc, err = RunNewProcessPty(config)
|
||||
default:
|
||||
err = errors.New("终端类型错误")
|
||||
}
|
||||
return
|
||||
}
|
169
service/process/process_pty.go
Normal file
169
service/process/process_pty.go
Normal file
@@ -0,0 +1,169 @@
|
||||
package process
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"msm/config"
|
||||
"msm/log"
|
||||
"msm/model"
|
||||
"msm/utils"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/creack/pty"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type ProcessPty struct {
|
||||
ProcessBase
|
||||
cacheBytesBuf *bytes.Buffer
|
||||
pty *os.File
|
||||
}
|
||||
|
||||
func (p *ProcessPty) Kill() error {
|
||||
if err := p.cmd.Process.Kill(); err != nil {
|
||||
log.Logger.Errorw("进程杀死失败", "err", err, "state", p.State.State)
|
||||
return err
|
||||
}
|
||||
return p.pty.Close()
|
||||
}
|
||||
|
||||
func (p *ProcessPty) watchDog() {
|
||||
state, _ := p.cmd.Process.Wait()
|
||||
close(p.stopChan)
|
||||
p.State.State = 0
|
||||
p.pty.Close()
|
||||
if state.ExitCode() != 0 {
|
||||
log.Logger.Infow("进程停止", "进程名称", p.Name, "exitCode", state.ExitCode(), "进程类型", "pty")
|
||||
p.push(fmt.Sprintf("进程停止,退出码 %d", state.ExitCode()))
|
||||
if p.Config.AutoRestart {
|
||||
p.ReStart()
|
||||
}
|
||||
} else {
|
||||
log.Logger.Infow("进程正常退出", "进程名称", p.Name)
|
||||
p.push("进程正常退出")
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ProcessPty) ReStart() {
|
||||
if p.State.restartTimes > config.CF.ProcessRestartsLimit {
|
||||
log.Logger.Warnw("重启次数达到上限", "name", p.Name, "limit", config.CF.ProcessRestartsLimit)
|
||||
p.State.State = 2
|
||||
p.State.Info = "重启次数异常"
|
||||
p.push("进程重启次数达到上限")
|
||||
return
|
||||
}
|
||||
cmd := exec.Command(p.StartCommand[0], p.StartCommand[1:]...)
|
||||
cmd.Dir = p.cmd.Dir
|
||||
pf, err := pty.Start(cmd)
|
||||
if err != nil || p.cmd.Process == nil {
|
||||
log.Logger.Error("进程启动出错:", err)
|
||||
return
|
||||
}
|
||||
pty.Setsize(pf, &pty.Winsize{
|
||||
Rows: 100,
|
||||
Cols: 100,
|
||||
})
|
||||
p.pty = pf
|
||||
p.State.restartTimes++
|
||||
log.Logger.Infow("进程启动成功", "进程名称", p.Name, "重启次数", p.State.restartTimes)
|
||||
p.cmd = cmd
|
||||
p.pInit()
|
||||
p.push("进程启动成功")
|
||||
}
|
||||
|
||||
func (p *ProcessPty) WriteBytes(input []byte) (err error) {
|
||||
p.logReportHandler(config.CF.ProcessInputPrefix + string(input))
|
||||
_, err = p.pty.Write(input)
|
||||
return
|
||||
}
|
||||
|
||||
func (p *ProcessPty) Write(input string) (err error) {
|
||||
p.logReportHandler(config.CF.ProcessInputPrefix + input)
|
||||
_, err = p.pty.Write([]byte(input))
|
||||
return
|
||||
}
|
||||
|
||||
func (p *ProcessPty) readInit() {
|
||||
log.Logger.Debugw("stdout读取线程已启动", "进程名", p.Name, "使用者", p.WhoUsing)
|
||||
buf := make([]byte, 1024)
|
||||
for {
|
||||
select {
|
||||
case <-p.stopChan:
|
||||
{
|
||||
p.IsUsing.Store(false)
|
||||
p.WhoUsing = ""
|
||||
log.Logger.Debugw("stdout读取线程已退出", "进程名", p.Name, "使用者", p.WhoUsing)
|
||||
return
|
||||
}
|
||||
default:
|
||||
{
|
||||
n, _ := p.pty.Read(buf)
|
||||
p.bufHanle(buf[:n])
|
||||
if p.IsUsing.Load() {
|
||||
p.ws.wsMux.Lock()
|
||||
p.ws.wsConnect.WriteMessage(websocket.TextMessage, buf[:n])
|
||||
p.ws.wsMux.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ProcessPty) ReadCache(ws *websocket.Conn) {
|
||||
ws.WriteMessage(websocket.TextMessage, p.cacheBytesBuf.Bytes())
|
||||
}
|
||||
|
||||
func (p *ProcessPty) bufHanle(b []byte) {
|
||||
log := strings.TrimSpace(string(b))
|
||||
if utils.RemoveANSI(log) != "" {
|
||||
p.logReportHandler(log)
|
||||
}
|
||||
p.cacheBytesBuf.Write(b)
|
||||
p.cacheBytesBuf.Next(len(b))
|
||||
}
|
||||
|
||||
func (p *ProcessPty) pInit() {
|
||||
p.SetTermType("pty")
|
||||
p.Control.changControlChan = make(chan int)
|
||||
p.stopChan = make(chan struct{})
|
||||
p.State.State = 1
|
||||
p.Pid = p.cmd.Process.Pid
|
||||
p.State.startTime = time.Now()
|
||||
p.cacheBytesBuf = bytes.NewBuffer(make([]byte, config.CF.ProcessMsgCacheBufLimit))
|
||||
p.InitPerformanceStatus()
|
||||
p.initPsutil()
|
||||
go p.readInit()
|
||||
go p.monitorHanler()
|
||||
go p.watchDog()
|
||||
}
|
||||
|
||||
func RunNewProcessPty(pconfig model.Process) (*ProcessPty, error) {
|
||||
args := strings.Split(pconfig.Cmd, " ")
|
||||
cmd := exec.Command(args[0], args[1:]...) // 替换为你要执行的命令及参数
|
||||
|
||||
processPty := ProcessPty{
|
||||
ProcessBase: ProcessBase{
|
||||
Name: pconfig.Name,
|
||||
StartCommand: args,
|
||||
},
|
||||
}
|
||||
cmd.Dir = pconfig.Cwd
|
||||
pf, err := pty.Start(cmd)
|
||||
if err != nil || cmd.Process == nil {
|
||||
log.Logger.Error("进程启动出错:", err)
|
||||
return nil, err
|
||||
}
|
||||
pty.Setsize(pf, &pty.Winsize{
|
||||
Rows: 100,
|
||||
Cols: 100,
|
||||
})
|
||||
processPty.pty = pf
|
||||
processPty.cmd = cmd
|
||||
log.Logger.Infow("创建进程成功")
|
||||
processPty.setProcessConfig(pconfig)
|
||||
processPty.pInit()
|
||||
return &processPty, nil
|
||||
}
|
182
service/process/process_std.go
Normal file
182
service/process/process_std.go
Normal file
@@ -0,0 +1,182 @@
|
||||
package process
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"msm/config"
|
||||
"msm/log"
|
||||
"msm/model"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type ProcessStd struct {
|
||||
ProcessBase
|
||||
cacheLine []string
|
||||
stdin io.WriteCloser
|
||||
stdout *bufio.Scanner
|
||||
}
|
||||
|
||||
func (p *ProcessStd) Kill() error {
|
||||
return p.cmd.Process.Kill()
|
||||
}
|
||||
|
||||
func (p *ProcessStd) watchDog() {
|
||||
state, _ := p.cmd.Process.Wait()
|
||||
close(p.stopChan)
|
||||
p.State.State = 0
|
||||
if state.ExitCode() != 0 {
|
||||
log.Logger.Infow("进程停止", "进程名称", p.Name, "exitCode", state.ExitCode(), "进程类型", "std")
|
||||
p.push(fmt.Sprintf("进程停止,退出码 %d", state.ExitCode()))
|
||||
if p.Config.AutoRestart {
|
||||
p.ReStart()
|
||||
}
|
||||
} else {
|
||||
log.Logger.Infow("进程正常退出", "进程名称", p.Name)
|
||||
p.push("进程正常退出")
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ProcessStd) WriteBytes(input []byte) (err error) {
|
||||
p.logReportHandler(config.CF.ProcessInputPrefix + string(input))
|
||||
_, err = p.stdin.Write(append(input, '\n'))
|
||||
return
|
||||
}
|
||||
|
||||
func (p *ProcessStd) Write(input string) (err error) {
|
||||
p.logReportHandler(config.CF.ProcessInputPrefix + input)
|
||||
_, err = p.stdin.Write([]byte(input + "\n"))
|
||||
return
|
||||
}
|
||||
|
||||
func (p *ProcessStd) ReStart() {
|
||||
if p.State.restartTimes > config.CF.ProcessRestartsLimit {
|
||||
log.Logger.Warnw("重启次数达到上限", "name", p.Name, "limit", config.CF.ProcessRestartsLimit)
|
||||
p.State.State = 2
|
||||
p.State.Info = "重启次数异常"
|
||||
p.push("进程重启次数达到上限")
|
||||
return
|
||||
}
|
||||
cmd := exec.Command(p.StartCommand[0], p.StartCommand[1:]...) // 替换为你要执行的命令及参数
|
||||
cmd.Dir = p.cmd.Dir
|
||||
out, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
log.Logger.Errorw("重启失败,输出管道获取失败", "err", err)
|
||||
p.Config.AutoRestart = false
|
||||
return
|
||||
}
|
||||
p.stdout = bufio.NewScanner(out)
|
||||
p.stdin, err = cmd.StdinPipe()
|
||||
if err != nil {
|
||||
log.Logger.Errorw("重启失败,输入管道获取失败", "err", err)
|
||||
p.Config.AutoRestart = false
|
||||
return
|
||||
}
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
log.Logger.Errorw("重启失败,进程启动出错:", "err", err)
|
||||
p.Config.AutoRestart = false
|
||||
return
|
||||
}
|
||||
p.State.restartTimes++
|
||||
log.Logger.Infow("进程启动成功", "进程名称", p.Name, "重启次数", p.State.restartTimes)
|
||||
p.cmd = cmd
|
||||
p.pInit()
|
||||
p.push("进程启动成功")
|
||||
|
||||
}
|
||||
|
||||
func (p *ProcessStd) pInit() {
|
||||
log.Logger.Infow("创建进程成功")
|
||||
p.Control.changControlChan = make(chan int)
|
||||
p.stopChan = make(chan struct{})
|
||||
p.State.State = 1
|
||||
p.Pid = p.cmd.Process.Pid
|
||||
p.State.startTime = time.Now()
|
||||
p.cacheLine = make([]string, config.CF.ProcessMsgCacheLinesLimit)
|
||||
p.InitPerformanceStatus()
|
||||
p.initPsutil()
|
||||
go p.watchDog()
|
||||
go p.readInit()
|
||||
go p.monitorHanler()
|
||||
}
|
||||
|
||||
func (p *ProcessStd) ReadCache(ws *websocket.Conn) {
|
||||
for _, line := range p.cacheLine {
|
||||
ws.WriteMessage(websocket.TextMessage, []byte(line))
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ProcessStd) readInit() {
|
||||
var output string
|
||||
log.Logger.Debugw("stdout读取线程已启动", "进程名", p.Name, "使用者", p.WhoUsing)
|
||||
for {
|
||||
select {
|
||||
case <-p.stopChan:
|
||||
{
|
||||
p.IsUsing.Store(false)
|
||||
p.WhoUsing = ""
|
||||
log.Logger.Debugw("stdout读取线程已退出", "进程名", p.Name, "使用者", p.WhoUsing)
|
||||
return
|
||||
}
|
||||
default:
|
||||
{
|
||||
output = p.Read()
|
||||
if p.IsUsing.Load() && output != "" {
|
||||
p.ws.wsMux.Lock()
|
||||
p.ws.wsConnect.WriteMessage(websocket.TextMessage, []byte(output))
|
||||
p.ws.wsMux.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
func (p *ProcessStd) Read() string {
|
||||
if p.stdout.Scan() {
|
||||
output := p.stdout.Text()
|
||||
p.logReportHandler(output)
|
||||
p.cacheLine = p.cacheLine[1:]
|
||||
p.cacheLine = append(p.cacheLine, output)
|
||||
return output
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func RunNewProcessStd(pconfig model.Process) (*ProcessStd, error) {
|
||||
args := strings.Split(pconfig.Cmd, " ")
|
||||
cmd := exec.Command(args[0], args[1:]...) // 替换为你要执行的命令及参数
|
||||
|
||||
processStd := ProcessStd{
|
||||
ProcessBase: ProcessBase{
|
||||
Name: pconfig.Name,
|
||||
StartCommand: args,
|
||||
},
|
||||
}
|
||||
cmd.Dir = pconfig.Cwd
|
||||
out, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
log.Logger.Errorw("输出管道获取失败", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
processStd.stdout = bufio.NewScanner(out)
|
||||
processStd.stdin, err = cmd.StdinPipe()
|
||||
if err != nil {
|
||||
log.Logger.Errorw("输入管道获取失败", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
err = cmd.Start()
|
||||
if err != nil || cmd.Process == nil {
|
||||
log.Logger.Error("进程启动出错:", err)
|
||||
return nil, err
|
||||
}
|
||||
log.Logger.Infow("创建进程成功", "config", pconfig)
|
||||
processStd.cmd = cmd
|
||||
processStd.SetTermType("std")
|
||||
processStd.pInit()
|
||||
processStd.setProcessConfig(pconfig)
|
||||
return &processStd, nil
|
||||
}
|
124
service/process/service.go
Normal file
124
service/process/service.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package process
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"msm/dao"
|
||||
"msm/log"
|
||||
"msm/model"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type processCtlService struct{}
|
||||
|
||||
var processMap sync.Map = sync.Map{}
|
||||
var ProcessCtlService = new(processCtlService)
|
||||
|
||||
func (p *processCtlService) AddProcess(uuid int, prcess Process) {
|
||||
processMap.Store(uuid, prcess)
|
||||
// processMap.Store("111", prcess)
|
||||
// return "111"
|
||||
}
|
||||
|
||||
func (p *processCtlService) KillProcess(uuid int) error {
|
||||
value, ok := processMap.Load(uuid)
|
||||
if !ok {
|
||||
return errors.New("进程不存在")
|
||||
}
|
||||
result, ok := value.(Process)
|
||||
if !ok {
|
||||
return errors.New("进程类型错误")
|
||||
}
|
||||
result.SetAutoRestart(false)
|
||||
return result.Kill()
|
||||
}
|
||||
|
||||
func (p *processCtlService) GetProcess(uuid int) (Process, error) {
|
||||
process, ok := processMap.Load(uuid)
|
||||
if !ok {
|
||||
return nil, errors.New("进程获取失败")
|
||||
|
||||
}
|
||||
result, ok := process.(Process)
|
||||
if !ok {
|
||||
return nil, errors.New("进程类型错误")
|
||||
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (p *processCtlService) KillAllProcess() {
|
||||
processMap.Range(func(key, value any) bool {
|
||||
value.(Process).Kill()
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func (p *processCtlService) DeleteProcess(uuid int) {
|
||||
processMap.Delete(uuid)
|
||||
}
|
||||
|
||||
func (p *processCtlService) GetProcessList() []model.ProcessInfo {
|
||||
processConfiglist := dao.ProcessDao.GetAllProcessConfig()
|
||||
return p.getProcessInfoList(processConfiglist)
|
||||
}
|
||||
|
||||
func (p *processCtlService) GetProcessListByUser(username string) []model.ProcessInfo {
|
||||
processConfiglist := dao.ProcessDao.GetProcessConfigByUser(username)
|
||||
return p.getProcessInfoList(processConfiglist)
|
||||
}
|
||||
|
||||
func (p *processCtlService) getProcessInfoList(processConfiglist []model.Process) []model.ProcessInfo {
|
||||
processInfoList := []model.ProcessInfo{}
|
||||
for _, v := range processConfiglist {
|
||||
pi := model.ProcessInfo{
|
||||
Name: v.Name,
|
||||
Uuid: v.Uuid,
|
||||
}
|
||||
if value, ok := processMap.Load(v.Uuid); ok {
|
||||
process := value.(Process)
|
||||
pi.State.Info = process.GetStateInfo()
|
||||
pi.State.State = process.GetStateState()
|
||||
pi.StartTime = process.GetStartTimeFormat()
|
||||
pi.User = process.GetWhoUsing()
|
||||
pi.Usage.Cpu = process.GetCpuUsage()
|
||||
pi.Usage.Mem = process.GetMemUsage()
|
||||
pi.Usage.Time = process.GetTimeRecord()
|
||||
pi.TermType = process.GetTermType()
|
||||
}
|
||||
processInfoList = append(processInfoList, pi)
|
||||
}
|
||||
return processInfoList
|
||||
}
|
||||
|
||||
func (p *processCtlService) ProcessInit() {
|
||||
config := dao.ProcessDao.GetAllProcessConfig()
|
||||
for _, v := range config {
|
||||
if !v.AutoRestart {
|
||||
continue
|
||||
}
|
||||
proc, err := RunNewProcess(v)
|
||||
if err != nil {
|
||||
log.Logger.Warnw("初始化启动进程失败", v.Name, "name", "err", err)
|
||||
continue
|
||||
}
|
||||
p.AddProcess(v.Uuid, proc)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *processCtlService) UpdateProcessConfig(config model.Process) error {
|
||||
process, ok := processMap.Load(config.Uuid)
|
||||
if !ok {
|
||||
return errors.New("进程获取失败")
|
||||
}
|
||||
result, ok := process.(Process)
|
||||
if !ok {
|
||||
return errors.New("进程类型错误")
|
||||
}
|
||||
result.SetConfigLogReport(config.LogReport)
|
||||
result.SetConfigStatuPush(config.Push)
|
||||
result.SetConfigAutoRestart(config.AutoRestart)
|
||||
result.SetStartCommand(strings.Split(config.Cmd, " "))
|
||||
result.SetName(config.Name)
|
||||
return nil
|
||||
}
|
35
service/push/push.go
Normal file
35
service/push/push.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package push
|
||||
|
||||
import (
|
||||
"msm/dao"
|
||||
"strings"
|
||||
|
||||
"github.com/levigross/grequests"
|
||||
)
|
||||
|
||||
type pushService struct{}
|
||||
|
||||
var PushService = new(pushService)
|
||||
|
||||
func (p *pushService) Push(placeholders map[string]string) {
|
||||
pl := dao.PushDao.GetPushList()
|
||||
for _, v := range pl {
|
||||
if v.Enable {
|
||||
if v.Method == "GET" {
|
||||
grequests.Get(p.getReplaceMessage(placeholders, v.Url), nil)
|
||||
}
|
||||
if v.Method == "POST" {
|
||||
grequests.Post(v.Url, &grequests.RequestOptions{
|
||||
JSON: p.getReplaceMessage(placeholders, v.Body),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *pushService) getReplaceMessage(placeholders map[string]string, message string) string {
|
||||
for k, v := range placeholders {
|
||||
message = strings.ReplaceAll(message, k, v)
|
||||
}
|
||||
return message
|
||||
}
|
58
termui/tui.go
Normal file
58
termui/tui.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package termui
|
||||
|
||||
// func TermuiInit() {
|
||||
// // time.Sleep(2 * time.Second)
|
||||
// err := termbox.Init()
|
||||
// if err != nil {
|
||||
// log.Logger.Errorw("termui初始化失败", "err", err)
|
||||
// return
|
||||
// }
|
||||
// homeUi()
|
||||
// }
|
||||
|
||||
// func homeUi() {
|
||||
// termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
|
||||
// termbox.Flush()
|
||||
// list := process.ProcessCtlService.GetProcessList()
|
||||
// fmt.Println()
|
||||
// for i, v := range list {
|
||||
// if v.User != "" {
|
||||
// fmt.Printf(" [%v] %v %v <%v>\n", i, v.Name, v.StartTime, v.User)
|
||||
// } else {
|
||||
// fmt.Printf(" [%v] %v %v\n", i, v.Name, v.StartTime)
|
||||
// }
|
||||
// }
|
||||
// input := ""
|
||||
// fmt.Scan(&input)
|
||||
// for i, v := range list {
|
||||
// if input == strconv.Itoa(i) {
|
||||
// // prcessUi(v.Uuid)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// func prcessUi(uuid int) {
|
||||
// termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
|
||||
// termbox.Flush()
|
||||
// proc, err := process.ProcessCtlService.GetProcess(uuid)
|
||||
// if err != nil {
|
||||
// log.Logger.Errorw("进程获取失败", "err", err)
|
||||
// return
|
||||
// }
|
||||
// proc.SetControl("")
|
||||
// go func() {
|
||||
// for {
|
||||
// if output := proc.Read(); output != "" {
|
||||
// fmt.Println(output)
|
||||
// }
|
||||
// }
|
||||
// }()
|
||||
// go func() {
|
||||
// input := ""
|
||||
// for {
|
||||
// fmt.Scan(&input)
|
||||
// proc.Write(input + "\n")
|
||||
// }
|
||||
// }()
|
||||
|
||||
// }
|
111
test/test.go
Normal file
111
test/test.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
|
||||
"github.com/creack/pty"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/ws", handleWebSocket)
|
||||
log.Println("Server started at :8080")
|
||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||
}
|
||||
|
||||
func handleWebSocket(w http.ResponseWriter, r *http.Request) {
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Println("Upgrade:", err)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
// 启动 Minecraft 服务器
|
||||
cmd := exec.Command("java", "-jar", "launcher-airplane.jar")
|
||||
|
||||
// 创建伪终端
|
||||
ptmx, err := pty.Start(cmd)
|
||||
if err != nil {
|
||||
log.Println("Start pty:", err)
|
||||
return
|
||||
}
|
||||
defer func() { _ = ptmx.Close() }() // 最后关闭伪终端
|
||||
|
||||
// 创建通道用于读取子程序的输出
|
||||
outputChan := make(chan string)
|
||||
|
||||
go func() {
|
||||
defer close(outputChan)
|
||||
readOutput(ptmx, outputChan)
|
||||
}()
|
||||
|
||||
// 将子程序的输出发送到 WebSocket 客户端
|
||||
go func() {
|
||||
for output := range outputChan {
|
||||
if err := conn.WriteMessage(websocket.TextMessage, []byte(output)); err != nil {
|
||||
log.Println("WriteMessage:", err)
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// 从 WebSocket 客户端读取消息并发送到子程序的标准输入
|
||||
for {
|
||||
_, message, err := conn.ReadMessage()
|
||||
if err != nil {
|
||||
log.Println("ReadMessage:", err)
|
||||
break
|
||||
}
|
||||
// 检查是否是发送 Tab 键的命令
|
||||
if string(message) == "SEND_TAB" {
|
||||
_, err := ptmx.Write([]byte{9}) // Tab 键的 ASCII 码是 9
|
||||
if err != nil {
|
||||
log.Println("Write to stdin:", err)
|
||||
}
|
||||
} else if string(message) == "READ_INPUT" {
|
||||
// 读取伪终端当前输入管道内已经存在的内容
|
||||
// 注意:伪终端没有单独的输入缓冲区,输入会立即被处理
|
||||
// 这里假设你想获取当前输出内容
|
||||
if err := conn.WriteMessage(websocket.TextMessage, []byte("Currently no direct way to fetch unsent input. Consider monitoring the terminal buffer.")); err != nil {
|
||||
log.Println("WriteMessage:", err)
|
||||
}
|
||||
} else {
|
||||
_, err := ptmx.Write(append(message, '\n')) // 确保命令后有换行符
|
||||
if err != nil {
|
||||
log.Println("Write to stdin:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 等待子程序结束
|
||||
if err := cmd.Wait(); err != nil {
|
||||
log.Println("Wait:", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func readOutput(reader io.Reader, outputChan chan<- string) {
|
||||
buf := make([]byte, 1024)
|
||||
for {
|
||||
n, err := reader.Read(buf)
|
||||
if n > 0 {
|
||||
outputChan <- string(buf[:n])
|
||||
}
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
log.Println("Read:", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
74
utils/jwt.go
Normal file
74
utils/jwt.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt"
|
||||
)
|
||||
|
||||
var mySecret = []byte("D9&(#$HI$#Y(FD*A))")
|
||||
|
||||
func keyFunc(_ *jwt.Token) (i interface{}, err error) {
|
||||
return mySecret, nil
|
||||
}
|
||||
|
||||
// MyClaims 自定义声明结构体并内嵌 jwt.StandardClaims
|
||||
// jwt包自带的jwt.StandardClaims只包含了官方字段,若需要额外记录其他字段,就可以自定义结构体
|
||||
// 如果想要保存更多信息,都可以添加到这个结构体中
|
||||
|
||||
type MyClaims struct {
|
||||
UserName string `json:"user_name"`
|
||||
jwt.StandardClaims
|
||||
}
|
||||
|
||||
func GenToken(UserName string) (string, error) {
|
||||
// 创建一个我们自己的声明的数据
|
||||
c := MyClaims{
|
||||
UserName,
|
||||
jwt.StandardClaims{
|
||||
ExpiresAt: time.Now().Add(
|
||||
240 * time.Hour).Unix(), // 过期时间
|
||||
Issuer: "jwt", // 签发人
|
||||
},
|
||||
}
|
||||
// 使用指定的签名方法创建签名对象
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
|
||||
// 使用指定的secret签名并获得完整的编码后的字符串token
|
||||
return token.SignedString(mySecret)
|
||||
}
|
||||
|
||||
// ParseToken 解析JWT
|
||||
func ParseToken(tokenString string) (*MyClaims, error) {
|
||||
// 解析token
|
||||
var mc = new(MyClaims)
|
||||
token, err := jwt.ParseWithClaims(tokenString, mc, keyFunc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 校验token
|
||||
if token.Valid {
|
||||
return mc, nil
|
||||
}
|
||||
return nil, errors.New("invalid token")
|
||||
}
|
||||
|
||||
// RefreshToken 刷新AccessToken
|
||||
func RefreshToken(aToken, rToken string) (newAToken, newRToken string, err error) {
|
||||
// refresh token无效直接返回
|
||||
if _, err = jwt.Parse(rToken, keyFunc); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 从旧access token中解析出claims数据
|
||||
var claims MyClaims
|
||||
_, err = jwt.ParseWithClaims(aToken, &claims, keyFunc)
|
||||
v, _ := err.(*jwt.ValidationError)
|
||||
|
||||
// 当access token是过期错误 并且 refresh token没有过期时就创建一个新的access token
|
||||
if v.Errors == jwt.ValidationErrorExpired {
|
||||
token, _ := GenToken(claims.UserName)
|
||||
return token, "", nil
|
||||
}
|
||||
return
|
||||
}
|
12
utils/md5.go
Normal file
12
utils/md5.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
func Md5(str string) string {
|
||||
h := md5.New()
|
||||
h.Write([]byte(str))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
32
utils/unicode.go
Normal file
32
utils/unicode.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func RemoveNotValidUtf8InString(s string) string {
|
||||
ret := s
|
||||
if !utf8.ValidString(s) {
|
||||
v := make([]rune, 0, len(s))
|
||||
for i, r := range s {
|
||||
if r == utf8.RuneError {
|
||||
_, size := utf8.DecodeRuneInString(s[i:])
|
||||
if size == 1 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
v = append(v, r)
|
||||
}
|
||||
ret = string(v)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func RemoveANSI(input string) string {
|
||||
// Define the regular expression to match ANSI escape sequences
|
||||
re := regexp.MustCompile(`\x1b\[[0-9;]*[a-zA-Z]`)
|
||||
// Replace all ANSI escape sequences with an empty string
|
||||
cleanedString := re.ReplaceAllString(input, "")
|
||||
return cleanedString
|
||||
}
|
8
utils/utils.go
Normal file
8
utils/utils.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package utils
|
||||
|
||||
func Unwarp[T any](result T, err error) T {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return result
|
||||
}
|
Reference in New Issue
Block a user