mirror of
https://github.com/certd/certd.git
synced 2026-04-24 04:17:25 +08:00
build: trident-sync prepare
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
logs/
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
node_modules/
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
coverage/
|
||||
!dist/
|
||||
.idea/
|
||||
run/
|
||||
.DS_Store
|
||||
*.sw*
|
||||
*.un~
|
||||
.tsbuildinfo
|
||||
.tsbuildinfo.*
|
||||
/data/db.sqlite
|
||||
@@ -0,0 +1,11 @@
|
||||
# 🎨 editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "./node_modules/mwts/",
|
||||
"ignorePatterns": ["node_modules", "dist", "test", "jest.config.js", "typings"],
|
||||
"env": {
|
||||
"jest": true
|
||||
},
|
||||
"rules": {
|
||||
"@typescript-eslint/ban-ts-ignore": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
logs/
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
node_modules/
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
coverage/
|
||||
dist/
|
||||
.idea/
|
||||
run/
|
||||
.DS_Store
|
||||
*.sw*
|
||||
*.un~
|
||||
.tsbuildinfo
|
||||
.tsbuildinfo.*
|
||||
/data/db.sqlite
|
||||
/pnpm-lock.yaml
|
||||
.serverless
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
...require('mwts/.prettierrc.json')
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
FROM registry.cn-shenzhen.aliyuncs.com/greper/node:15.8.0-alpine
|
||||
|
||||
WORKDIR /home
|
||||
|
||||
COPY . .
|
||||
# 如果各公司有自己的私有源,可以替换registry地址
|
||||
#RUN npm install --registry=https://registry.npmmirror.com
|
||||
RUN npm install -g cnpm
|
||||
RUN cnpm install
|
||||
RUN npm run build:preview
|
||||
|
||||
# 如果端口更换,这边可以更新一下
|
||||
EXPOSE 7001
|
||||
|
||||
CMD ["npm", "run", "online:preview"]
|
||||
@@ -0,0 +1,661 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://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 <https://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
|
||||
<https://www.gnu.org/licenses/>.
|
||||
@@ -0,0 +1,31 @@
|
||||
# fast-server-js
|
||||
|
||||
base on midway
|
||||
|
||||
## QuickStart
|
||||
|
||||
<!-- add docs here for user -->
|
||||
|
||||
see [midway docs][midway] for more detail.
|
||||
|
||||
### Development
|
||||
|
||||
```bash
|
||||
$ npm i
|
||||
$ npm run dev
|
||||
$ open http://localhost:7001/
|
||||
```
|
||||
|
||||
### Deploy
|
||||
|
||||
```bash
|
||||
$ npm start
|
||||
```
|
||||
|
||||
### npm scripts
|
||||
|
||||
- Use `npm run lint` to check code style.
|
||||
- Use `npm test` to run unit test.
|
||||
|
||||
|
||||
[midway]: https://midwayjs.org
|
||||
@@ -0,0 +1,30 @@
|
||||
# fast-server-js
|
||||
|
||||
## 快速入门
|
||||
|
||||
<!-- 在此次添加使用文档 -->
|
||||
|
||||
如需进一步了解,参见 [midway 文档][midway]。
|
||||
|
||||
### 本地开发
|
||||
|
||||
```bash
|
||||
$ npm i
|
||||
$ npm run dev
|
||||
$ open http://localhost:7001/
|
||||
```
|
||||
|
||||
### 部署
|
||||
|
||||
```bash
|
||||
$ npm start
|
||||
```
|
||||
|
||||
### 内置指令
|
||||
|
||||
- 使用 `npm run lint` 来做代码风格检查。
|
||||
- 使用 `npm test` 来执行单元测试。
|
||||
|
||||
|
||||
[midway]: https://midwayjs.org
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// 获取框架
|
||||
const WebFramework = require('@midwayjs/koa').Framework;
|
||||
const { Bootstrap } = require('@midwayjs/bootstrap');
|
||||
|
||||
const DirectoryFileDetector = require( "@midwayjs/core").DirectoryFileDetector;
|
||||
|
||||
const baseDir = process.cwd()
|
||||
const pipelineDir = baseDir +"./node_modules/@certd/pipeline"
|
||||
const customFileDetector = new DirectoryFileDetector({loadDir:[baseDir,pipelineDir]})
|
||||
|
||||
|
||||
module.exports = async () => {
|
||||
// 加载框架并执行
|
||||
await Bootstrap.configure({
|
||||
moduleDetector:customFileDetector
|
||||
}).run();
|
||||
// 获取依赖注入容器
|
||||
const container = Bootstrap.getApplicationContext();
|
||||
// 获取 koa framework
|
||||
const framework = container.get(WebFramework);
|
||||
// 返回 app 对象
|
||||
return framework.getApplication();
|
||||
};
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
const WebFramework = require('@midwayjs/koa').Framework;
|
||||
const web = new WebFramework().configure({
|
||||
port: 7001,
|
||||
});
|
||||
|
||||
const DirectoryFileDetector = require( "@midwayjs/core").DirectoryFileDetector;
|
||||
|
||||
const baseDir = process.cwd()
|
||||
const pipelineDir = baseDir +"./node_modules/@certd/pipeline"
|
||||
const customFileDetector = new DirectoryFileDetector({loadDir:[baseDir,pipelineDir]})
|
||||
|
||||
const { Bootstrap } = require('@midwayjs/bootstrap');
|
||||
Bootstrap.load(web).configure({
|
||||
moduleDetector:customFileDetector
|
||||
}).run();
|
||||
@@ -0,0 +1,77 @@
|
||||
-- 表:sys_permission
|
||||
CREATE TABLE "sys_permission" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar(100) NOT NULL, "permission" varchar(100), "parent_id" integer NOT NULL DEFAULT (-1), "sort" integer NOT NULL, "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP));
|
||||
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (1, '系统管理', 'sys', -1, 1, 1, 1624085863636);
|
||||
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (2, '权限管理', 'sys:auth', 1, 1, 1, 1);
|
||||
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (3, '用户管理', 'sys:auth:user', 2, 1, 1, 1);
|
||||
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (4, '查看', 'sys:auth:user:view', 3, 100, 1, 1624189112333);
|
||||
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (5, '权限管理', 'sys:auth:per', 2, 1, 1, 1);
|
||||
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (6, '查看', 'sys:auth:per:view', 5, 100, 1, 1624189161317);
|
||||
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (7, '角色管理', 'sys:auth:role', 2, 1, 1, 1);
|
||||
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (8, '查看', 'sys:auth:role:view', 7, 1, 1, 1);
|
||||
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (9, '修改', 'sys:auth:user:edit', 3, 300, 1, 1624189127688);
|
||||
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (10, '删除', 'sys:auth:user:remove', 3, 400, 1, 1624189133184);
|
||||
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (11, '添加', 'sys:auth:user:add', 3, 200, 1, 1624189142576);
|
||||
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (12, '修改', 'sys:auth:role:edit', 7, 1, 1, 1);
|
||||
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (13, '删除', 'sys:auth:role:remove', 7, 1, 1, 1);
|
||||
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (14, '添加', 'sys:auth:role:add', 7, 1, 1, 1);
|
||||
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (15, '修改', 'sys:auth:per:edit', 5, 300, 1, 1624189308837);
|
||||
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (16, '删除', 'sys:auth:per:remove', 5, 400, 1, 1624189256926);
|
||||
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (17, '添加', 'sys:auth:per:add', 5, 200, 1, 1624189283766);
|
||||
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (18,'授权','sys:auth:role:authz',7,100,1,1624335712144);
|
||||
|
||||
|
||||
|
||||
-- 表:sys_role
|
||||
CREATE TABLE "sys_role" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(100) NOT NULL, "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP));
|
||||
INSERT INTO sys_role (id, name, create_time, update_time) VALUES (1, '管理员', 1, 1623749138537);
|
||||
INSERT INTO sys_role (id, name, create_time, update_time) VALUES (2, '只读角色', 1, 1623749138537);
|
||||
|
||||
-- 表:sys_role_permission
|
||||
CREATE TABLE "sys_role_permission" ("role_id" integer NOT NULL, "permission_id" integer NOT NULL, PRIMARY KEY ("role_id", "permission_id"));
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 1);
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 2);
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 3);
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 4);
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 5);
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 6);
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 7);
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 8);
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 9);
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 10);
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 11);
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 12);
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 13);
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 14);
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 15);
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 16);
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 17);
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 18);
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, -1);
|
||||
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 4);
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 6);
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 8);
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 1);
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 2);
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 3);
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 5);
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 7);
|
||||
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, -1);
|
||||
|
||||
-- 表:sys_user
|
||||
CREATE TABLE "sys_user" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "username" varchar(100) NOT NULL, "password" varchar(50) NOT NULL, "nick_name" varchar(50), "avatar" varchar(255), "phone_code" varchar(20), "mobile" varchar(20), "email" varchar(100),"remark" varchar(100), "status" integer NOT NULL DEFAULT (1), "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP));
|
||||
INSERT INTO sys_user (id, username, password, nick_name, avatar, phone_code, mobile, email, status, create_time, update_time,remark) VALUES (1, 'admin', 'e10adc3949ba59abbe56e057f20f883e', 'admin', NULL, NULL, NULL, NULL, 1, 2011123132, 123132,NULL);
|
||||
INSERT INTO sys_user (id, username, password, nick_name, avatar, phone_code, mobile, email, status, create_time, update_time,remark) VALUES (2, 'readonly', 'e10adc3949ba59abbe56e057f20f883e', '只读用户', NULL, NULL, NULL, NULL, 1, 2011123132, 123132,'密码:123456');
|
||||
|
||||
-- 表:sys_user_role
|
||||
CREATE TABLE "sys_user_role" ("role_id" integer NOT NULL, "user_id" integer NOT NULL, PRIMARY KEY ("role_id", "user_id"));
|
||||
INSERT INTO sys_user_role (role_id, user_id) VALUES (1, 1);
|
||||
INSERT INTO sys_user_role (role_id, user_id) VALUES (2, 2);
|
||||
|
||||
-- 索引:IDX_223de54d6badbe43a5490450c3
|
||||
CREATE UNIQUE INDEX "IDX_223de54d6badbe43a5490450c3" ON "sys_role" ("name");
|
||||
|
||||
-- 索引:IDX_9e7164b2f1ea1348bc0eb0a7da
|
||||
CREATE UNIQUE INDEX "IDX_9e7164b2f1ea1348bc0eb0a7da" ON "sys_user" ("username");
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
-- for preview 限制演示环境的数据修改
|
||||
update sqlite_sequence set seq = 1000 where name = 'sys_user' ;
|
||||
update sqlite_sequence set seq = 1000 where name = 'sys_permission' ;
|
||||
update sqlite_sequence set seq = 1000 where name = 'sys_role' ;
|
||||
@@ -0,0 +1,24 @@
|
||||
--
|
||||
-- 由SQLiteStudio v3.3.3 产生的文件 周六 7月 3 00:38:02 2021
|
||||
--
|
||||
-- 文本编码:UTF-8
|
||||
--
|
||||
|
||||
-- 表:cd_access
|
||||
CREATE TABLE "cd_access" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "user_id" integer NOT NULL, "name" varchar(100) NOT NULL, "type" varchar(100) NOT NULL, "setting" varchar(1024), "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP));
|
||||
|
||||
-- 表:cd_cert
|
||||
CREATE TABLE "cd_cert" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "user_id" integer NOT NULL, "domains" varchar(2048) NOT NULL, "email" varchar(100) NOT NULL, "cert_issuer_id" integer, "challenge_type" varchar(100), "challenge_dns_type" varchar(100),"challenge_access_id" integer, "country" varchar(100), "state" varchar(100), "locality" varchar(100), "organization" varchar(100), "organization_unit" varchar(100), "remark" varchar(100), "last_history_id" integer, "last_success_id" integer, "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP));
|
||||
|
||||
-- 表:cd_cert_apply_history
|
||||
CREATE TABLE "cd_cert_apply_history" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "user_id" integer NOT NULL, "cert_id" integer NOT NULL, "success" boolean, "result" varchar(1024), "cert_crt" varchar(1024), "cert_key" varchar(1024), "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP));
|
||||
|
||||
-- 表:cd_cert_issuer
|
||||
CREATE TABLE "cd_cert_issuer" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "user_id" integer NOT NULL, "type" varchar(20) NOT NULL, "account" varchar(100) NOT NULL, "private_key" varchar(1024), "setting" varchar, "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP));
|
||||
|
||||
-- 表:cd_task
|
||||
CREATE TABLE "cd_task" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "user_id" integer NOT NULL, "name" varchar(100), "type" varchar(100), "setting" varchar(2048), "cert_id" integer NOT NULL, "last_history_id" integer, "last_success_id" integer, "remark" varchar(100), "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP));
|
||||
|
||||
-- 表:cd_task_history
|
||||
CREATE TABLE "cd_task_history" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "user_id" integer NOT NULL, "task_id" integer NOT NULL, "cert_id" integer NOT NULL, "cert_apply_history_id" integer NOT NULL, "success" boolean, "result" varchar(2048), "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP));
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
service: certd ## 应用发布到云平台的名字,一般指应用名
|
||||
|
||||
provider:
|
||||
name: aliyun ## 发布的云平台,aliyun,tencent 等
|
||||
|
||||
deployType:
|
||||
type: koa ## 部署的应用类型
|
||||
version: 3.0.0
|
||||
custom:
|
||||
customDomain:
|
||||
domainName: auto
|
||||
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
testPathIgnorePatterns: ['<rootDir>/test/fixtures'],
|
||||
coveragePathIgnorePatterns: ['<rootDir>/test/'],
|
||||
};
|
||||
@@ -0,0 +1,91 @@
|
||||
{
|
||||
"name": "@fast-crud/fs-server-js",
|
||||
"version": "0.2.0",
|
||||
"description": "fast-server base midway",
|
||||
"private": true,
|
||||
"midway-integration": {
|
||||
"lifecycle": {
|
||||
"before:package:cleanup": "npm run build"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"deploy": "midway-bin deploy",
|
||||
"start": "NODE_ENV=production node ./bootstrap.js",
|
||||
"online": "NODE_ENV=production node ./bootstrap.js",
|
||||
"online:preview": "NODE_ENV=preview node ./bootstrap.js",
|
||||
"dev": "cross-env NODE_ENV=local midway-bin dev --ts --watchFile='../../core/pipeline/src,../../plugins/'",
|
||||
"dev:preview": "cross-env NODE_ENV=preview midway-bin dev --ts",
|
||||
"dev:syncdb": "cross-env NODE_ENV=syncdb midway-bin dev --ts --watchFile='../../core/pipeline/src'",
|
||||
"test": "midway-bin test --ts",
|
||||
"cov": "midway-bin cov --ts",
|
||||
"lint": "mwts check",
|
||||
"lint:fix": "mwts fix",
|
||||
"ci": "npm run cov",
|
||||
"build": "midway-bin build -c",
|
||||
"build:preview": "cross-env NODE_ENV=preview midway-bin build -c",
|
||||
"check": "luckyeye",
|
||||
"mig": "typeorm migration:create -n name"
|
||||
},
|
||||
"dependencies": {
|
||||
"@alicloud/pop-core": "^1.7.12",
|
||||
"@certd/pipeline": "^0.3.0",
|
||||
"@certd/plugin-all": "^0.3.0",
|
||||
"@koa/cors": "^3.4.3",
|
||||
"@midwayjs/bootstrap": "^3.9.1",
|
||||
"@midwayjs/cache": "^3.9.0",
|
||||
"@midwayjs/core": "^3.9.0",
|
||||
"@midwayjs/decorator": "^3.9.0",
|
||||
"@midwayjs/koa": "^3.9.0",
|
||||
"@midwayjs/logger": "^2.17.0",
|
||||
"@midwayjs/typeorm": "^3.9.5",
|
||||
"@midwayjs/validate": "^3.9.0",
|
||||
"@midwayjs/i18n": "^3.9.0",
|
||||
"@types/cache-manager": "^3.4.3",
|
||||
"cache-manager": "^3.6.3",
|
||||
"dayjs": "^1.11.7",
|
||||
"glob": "^7.2.3",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"lodash": "^4.17.21",
|
||||
"log4js": "^6.7.1",
|
||||
"md5": "^2.3.0",
|
||||
"midway-flyway-js": "^3.0.0",
|
||||
"node-cron": "^3.0.2",
|
||||
"sqlite3": "^5.1.4",
|
||||
"svg-captcha": "^1.4.0",
|
||||
"typeorm": "^0.3.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@midwayjs/cli": "^1.3.21",
|
||||
"@midwayjs/luckyeye": "^1.1.0",
|
||||
"@midwayjs/mock": "^3.9.0",
|
||||
"@midwayjs/mwcc": "^0.8.0",
|
||||
"@types/jest": "^26.0.24",
|
||||
"@types/koa": "2.13.4",
|
||||
"@types/node": "^14.18.35",
|
||||
"cross-env": "^6.0.3",
|
||||
"jest": "^26.6.3",
|
||||
"mwts": "^1.3.0",
|
||||
"ts-jest": "^26.5.6",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsconfig-paths": "^4.1.1",
|
||||
"typescript": "^4.9.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"midway-bin-clean": [
|
||||
".vscode/.tsbuildinfo",
|
||||
"dist"
|
||||
],
|
||||
"midway-luckyeye": {
|
||||
"packages": [
|
||||
"midway_v2"
|
||||
]
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fast-crud/fast-server-js"
|
||||
},
|
||||
"author": "Greper",
|
||||
"license": "MIT"
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Inject } from '@midwayjs/decorator';
|
||||
import { Context } from '@midwayjs/koa';
|
||||
import { Constants } from './constants';
|
||||
|
||||
export abstract class BaseController {
|
||||
@Inject()
|
||||
ctx: Context;
|
||||
|
||||
/**
|
||||
* 成功返回
|
||||
* @param data 返回数据
|
||||
*/
|
||||
ok(data) {
|
||||
const res = {
|
||||
...Constants.res.success,
|
||||
data: undefined,
|
||||
};
|
||||
if (data) {
|
||||
res.data = data;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
/**
|
||||
* 失败返回
|
||||
* @param message
|
||||
*/
|
||||
fail(msg, code) {
|
||||
return {
|
||||
code: code ? code : Constants.res.error.code,
|
||||
msg: msg ? msg : Constants.res.error.code,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
import { Inject } from '@midwayjs/decorator';
|
||||
import { ValidateException } from './exception/validation-exception';
|
||||
import * as _ from 'lodash';
|
||||
import { Context } from '@midwayjs/koa';
|
||||
import { PermissionException } from './exception/permission-exception';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 服务基类
|
||||
*/
|
||||
export abstract class BaseService<T> {
|
||||
@Inject()
|
||||
ctx: Context;
|
||||
|
||||
abstract getRepository(): Repository<T>;
|
||||
|
||||
/**
|
||||
* 获得单个ID
|
||||
* @param id ID
|
||||
* @param infoIgnoreProperty 忽略返回属性
|
||||
*/
|
||||
async info(id, infoIgnoreProperty?): Promise<T | null> {
|
||||
if (!id) {
|
||||
throw new ValidateException('id不能为空');
|
||||
}
|
||||
// @ts-ignore
|
||||
const info = await this.getRepository().findOne({ where: { id } });
|
||||
if (info && infoIgnoreProperty) {
|
||||
for (const property of infoIgnoreProperty) {
|
||||
delete info[property];
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 非分页查询
|
||||
* @param option 查询配置
|
||||
*/
|
||||
async find(options) {
|
||||
return await this.getRepository().find(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param ids 删除的ID集合 如:[1,2,3] 或者 1,2,3
|
||||
*/
|
||||
async delete(ids) {
|
||||
if (ids instanceof Array) {
|
||||
await this.getRepository().delete(ids);
|
||||
} else if (typeof ids === 'string') {
|
||||
await this.getRepository().delete(ids.split(','));
|
||||
} else {
|
||||
//ids是一个condition
|
||||
await this.getRepository().delete(ids);
|
||||
}
|
||||
await this.modifyAfter(ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增|修改
|
||||
* @param param 数据
|
||||
*/
|
||||
async addOrUpdate(param) {
|
||||
await this.getRepository().save(param);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增
|
||||
* @param param 数据
|
||||
*/
|
||||
async add(param) {
|
||||
const now = new Date().getTime();
|
||||
param.createTime = now;
|
||||
param.updateTime = now;
|
||||
await this.addOrUpdate(param);
|
||||
await this.modifyAfter(param);
|
||||
return {
|
||||
id: param.id,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改
|
||||
* @param param 数据
|
||||
*/
|
||||
async update(param) {
|
||||
if (!param.id) throw new ValidateException('no id');
|
||||
param.updateTime = new Date().getTime();
|
||||
await this.addOrUpdate(param);
|
||||
await this.modifyAfter(param);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增|修改|删除 之后的操作
|
||||
* @param data 对应数据
|
||||
*/
|
||||
async modifyAfter(data) {}
|
||||
|
||||
/**
|
||||
* 分页查询
|
||||
* @param query 查询条件 bean
|
||||
* @param page
|
||||
* @param order
|
||||
* @param buildQuery
|
||||
*/
|
||||
async page(query, page = { offset: 0, limit: 20 }, order, buildQuery) {
|
||||
if (page.offset == null) {
|
||||
page.offset = 0;
|
||||
}
|
||||
if (page.limit == null) {
|
||||
page.limit = 20;
|
||||
}
|
||||
const qb = this.getRepository().createQueryBuilder('main');
|
||||
if (order && order.prop) {
|
||||
qb.orderBy('main.' + order.prop, order.asc ? 'ASC' : 'DESC');
|
||||
} else {
|
||||
qb.orderBy('id', 'DESC');
|
||||
}
|
||||
qb.offset(page.offset).limit(page.limit);
|
||||
//根据bean query
|
||||
if (query) {
|
||||
let whereSql = '';
|
||||
let index = 0;
|
||||
_.forEach(query, (value, key) => {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
if (index !== 0) {
|
||||
whereSql += ' and ';
|
||||
}
|
||||
whereSql += ` main.${key} = :${key} `;
|
||||
index++;
|
||||
});
|
||||
if (index > 0) {
|
||||
qb.where(whereSql, query);
|
||||
}
|
||||
}
|
||||
//自定义query
|
||||
if (buildQuery) {
|
||||
buildQuery(qb);
|
||||
}
|
||||
const list = await qb.getMany();
|
||||
const total = await qb.getCount();
|
||||
return {
|
||||
records: list,
|
||||
total,
|
||||
offset: page.offset,
|
||||
limit: page.limit,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询
|
||||
* @param query 查询条件 bean
|
||||
* @param order
|
||||
* @param buildQuery
|
||||
*/
|
||||
async list(query, order, buildQuery) {
|
||||
const qb = this.getRepository().createQueryBuilder('main');
|
||||
if (order && order.prop) {
|
||||
qb.orderBy('main.' + order.prop, order.asc ? 'ASC' : 'DESC');
|
||||
} else {
|
||||
qb.orderBy('id', 'DESC');
|
||||
}
|
||||
//根据bean query
|
||||
if (query) {
|
||||
let whereSql = '';
|
||||
let index = 0;
|
||||
_.forEach(query, (value, key) => {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
if (index !== 0) {
|
||||
whereSql += ' and ';
|
||||
}
|
||||
whereSql += ` main.${key} = :${key} `;
|
||||
index++;
|
||||
});
|
||||
if (index > 0) {
|
||||
qb.where(whereSql, query);
|
||||
}
|
||||
}
|
||||
//自定义query
|
||||
if (buildQuery) {
|
||||
buildQuery(qb);
|
||||
}
|
||||
return await qb.getMany();
|
||||
}
|
||||
|
||||
async checkUserId(id = 0, userId, userKey = 'userId') {
|
||||
// @ts-ignore
|
||||
const res = await this.getRepository().findOne({
|
||||
// @ts-ignore
|
||||
select: { [userKey]: true },
|
||||
where: {
|
||||
// @ts-ignore
|
||||
id,
|
||||
},
|
||||
});
|
||||
// @ts-ignore
|
||||
if (!res || res.userId === userId) {
|
||||
return;
|
||||
}
|
||||
throw new PermissionException('权限不足');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
export const Constants = {
|
||||
res: {
|
||||
error: {
|
||||
code: 1,
|
||||
message: 'error',
|
||||
},
|
||||
success: {
|
||||
code: 0,
|
||||
message: 'success',
|
||||
},
|
||||
validation: {
|
||||
code: 10,
|
||||
message: '参数错误',
|
||||
},
|
||||
auth: {
|
||||
code: 401,
|
||||
message: '您还未登录或token已过期',
|
||||
},
|
||||
permission: {
|
||||
code: 402,
|
||||
message: '您没有权限',
|
||||
},
|
||||
preview: {
|
||||
code: 10001,
|
||||
message: '对不起,预览环境不允许修改此数据',
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
import { ALL, Body, Post, Query } from '@midwayjs/decorator';
|
||||
import { BaseController } from './base-controller';
|
||||
|
||||
export abstract class CrudController extends BaseController {
|
||||
abstract getService();
|
||||
|
||||
@Post('/page')
|
||||
async page(
|
||||
@Body(ALL)
|
||||
body
|
||||
) {
|
||||
const pageRet = await this.getService().page(
|
||||
body?.query,
|
||||
body?.page,
|
||||
body?.sort,
|
||||
null
|
||||
);
|
||||
return this.ok(pageRet);
|
||||
}
|
||||
|
||||
@Post('/list')
|
||||
async list(
|
||||
@Body(ALL)
|
||||
body
|
||||
) {
|
||||
const listRet = await this.getService().list(body, null, null);
|
||||
return this.ok(listRet);
|
||||
}
|
||||
|
||||
@Post('/add')
|
||||
async add(
|
||||
@Body(ALL)
|
||||
bean
|
||||
) {
|
||||
const id = await this.getService().add(bean);
|
||||
return this.ok(id);
|
||||
}
|
||||
|
||||
@Post('/info')
|
||||
async info(
|
||||
@Query('id')
|
||||
id
|
||||
) {
|
||||
const bean = await this.getService().info(id);
|
||||
return this.ok(bean);
|
||||
}
|
||||
|
||||
@Post('/update')
|
||||
async update(
|
||||
@Body(ALL)
|
||||
bean
|
||||
) {
|
||||
await this.getService().update(bean);
|
||||
return this.ok(null);
|
||||
}
|
||||
@Post('/delete')
|
||||
async delete(
|
||||
@Query('id')
|
||||
id
|
||||
) {
|
||||
await this.getService().delete([id]);
|
||||
return this.ok(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export class EnumItem {
|
||||
value: string;
|
||||
label: string;
|
||||
color: string;
|
||||
|
||||
constructor(value, label, color) {
|
||||
this.value = value;
|
||||
this.label = label;
|
||||
this.color = color;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Constants } from '../constants';
|
||||
import { BaseException } from './base-exception';
|
||||
/**
|
||||
* 授权异常
|
||||
*/
|
||||
export class AuthException extends BaseException {
|
||||
constructor(message) {
|
||||
super(
|
||||
'AuthException',
|
||||
Constants.res.auth.code,
|
||||
message ? message : Constants.res.auth.message
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* 异常基类
|
||||
*/
|
||||
export class BaseException extends Error {
|
||||
status: number;
|
||||
constructor(name, code, message) {
|
||||
super(message);
|
||||
this.name = name;
|
||||
this.status = code;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Constants } from '../constants';
|
||||
import { BaseException } from './base-exception';
|
||||
/**
|
||||
* 通用异常
|
||||
*/
|
||||
export class CommonException extends BaseException {
|
||||
constructor(message) {
|
||||
super(
|
||||
'CommonException',
|
||||
Constants.res.error.code,
|
||||
message ? message : Constants.res.error.message
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Constants } from '../constants';
|
||||
import { BaseException } from './base-exception';
|
||||
/**
|
||||
* 授权异常
|
||||
*/
|
||||
export class PermissionException extends BaseException {
|
||||
constructor(message) {
|
||||
super(
|
||||
'PermissionException',
|
||||
Constants.res.permission.code,
|
||||
message ? message : Constants.res.permission.message
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Constants } from '../constants';
|
||||
import { BaseException } from './base-exception';
|
||||
/**
|
||||
* 预览模式
|
||||
*/
|
||||
export class PreviewException extends BaseException {
|
||||
constructor(message) {
|
||||
super(
|
||||
'PreviewException',
|
||||
Constants.res.preview.code,
|
||||
message ? message : Constants.res.preview.message
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Constants } from '../constants';
|
||||
import { BaseException } from './base-exception';
|
||||
/**
|
||||
* 校验异常
|
||||
*/
|
||||
export class ValidateException extends BaseException {
|
||||
constructor(message) {
|
||||
super(
|
||||
'ValidateException',
|
||||
Constants.res.validation.code,
|
||||
message ? message : Constants.res.validation.message
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
export class Result<T> {
|
||||
code: number;
|
||||
msg: string;
|
||||
data: T;
|
||||
constructor(code, msg, data?) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
static error(code = 1, msg) {
|
||||
return new Result(code, msg, null);
|
||||
}
|
||||
|
||||
static success(msg, data?) {
|
||||
return new Result(0, msg, data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import { join } from 'path';
|
||||
import { FlywayHistory } from 'midway-flyway-js/dist/entity';
|
||||
|
||||
import { MidwayConfig } from '@midwayjs/core';
|
||||
import { UserEntity } from '../modules/authority/entity/user';
|
||||
|
||||
export default {
|
||||
// use for cookie sign key, should change to your own and keep security
|
||||
keys: 'certd666',
|
||||
koa: {
|
||||
port: 7001,
|
||||
},
|
||||
cron: {},
|
||||
/**
|
||||
* 演示环境
|
||||
*/
|
||||
preview: {
|
||||
enabled: false,
|
||||
},
|
||||
|
||||
/**
|
||||
* 数据库
|
||||
*/
|
||||
typeorm: {
|
||||
dataSource: {
|
||||
default: {
|
||||
/**
|
||||
* 单数据库实例
|
||||
*/
|
||||
type: 'sqlite',
|
||||
database: join(__dirname, '../../data/db.sqlite'),
|
||||
synchronize: false, // 如果第一次使用,不存在表,有同步的需求可以写 true
|
||||
logging: true,
|
||||
|
||||
// 配置实体模型 或者 entities: '/entity',
|
||||
entities: [
|
||||
'**/modules/*/entity/*.ts',
|
||||
FlywayHistory,
|
||||
UserEntity,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
/**
|
||||
* 自动升级数据库脚本
|
||||
*/
|
||||
flyway: {
|
||||
scriptDir: join(__dirname, '../../db/migration'),
|
||||
},
|
||||
|
||||
biz: {
|
||||
jwt: {
|
||||
secret: 'greper-is-666',
|
||||
expire: 7 * 24 * 60, //单位秒
|
||||
},
|
||||
auth: {
|
||||
ignoreUrls: ['/', '/api/login', '/api/register'],
|
||||
},
|
||||
},
|
||||
} as MidwayConfig;
|
||||
@@ -0,0 +1,10 @@
|
||||
import { MidwayConfig } from '@midwayjs/core';
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 演示环境
|
||||
*/
|
||||
preview: {
|
||||
enabled: true,
|
||||
}
|
||||
} as MidwayConfig;
|
||||
@@ -0,0 +1,10 @@
|
||||
import { MidwayConfig } from '@midwayjs/core';
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 演示环境
|
||||
*/
|
||||
preview: {
|
||||
enabled: true,
|
||||
}
|
||||
} as MidwayConfig;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { MidwayConfig } from '@midwayjs/core';
|
||||
|
||||
export default {
|
||||
typeorm: {
|
||||
dataSource: {
|
||||
default: {
|
||||
synchronize: true, // 如果第一次使用,不存在表,有同步的需求可以写 true
|
||||
},
|
||||
},
|
||||
},
|
||||
} as MidwayConfig;
|
||||
@@ -0,0 +1,60 @@
|
||||
import * as validateComp from '@midwayjs/validate';
|
||||
import * as productionConfig from './config/config.production';
|
||||
import * as previewConfig from './config/config.preview';
|
||||
import * as defaultConfig from './config/config.default';
|
||||
import { Configuration, App } from '@midwayjs/decorator';
|
||||
import * as koa from '@midwayjs/koa';
|
||||
import * as orm from '@midwayjs/typeorm';
|
||||
import * as cache from '@midwayjs/cache';
|
||||
import cors from '@koa/cors';
|
||||
import { join } from 'path';
|
||||
import * as flyway from 'midway-flyway-js';
|
||||
import { ReportMiddleware } from './middleware/report';
|
||||
import { GlobalExceptionMiddleware } from './middleware/global-exception';
|
||||
import { PreviewMiddleware } from './middleware/preview';
|
||||
import { AuthorityMiddleware } from './middleware/authority';
|
||||
import * as pipeline from './plugins/pipeline';
|
||||
import * as cron from './plugins/cron';
|
||||
@Configuration({
|
||||
imports: [koa, orm, cache, flyway, validateComp,pipeline, cron],
|
||||
importConfigs: [
|
||||
{
|
||||
default: defaultConfig,
|
||||
preview: previewConfig,
|
||||
production: productionConfig,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class ContainerConfiguration {}
|
||||
@Configuration({
|
||||
conflictCheck: true,
|
||||
importConfigs: [join(__dirname, './config')],
|
||||
})
|
||||
export class ContainerLifeCycle {
|
||||
@App()
|
||||
app: koa.Application;
|
||||
|
||||
async onReady() {
|
||||
//跨域
|
||||
this.app.use(
|
||||
cors({
|
||||
origin: '*',
|
||||
})
|
||||
);
|
||||
// bodyparser options see https://github.com/koajs/bodyparser
|
||||
//this.app.use(bodyParser());
|
||||
//请求日志打印
|
||||
|
||||
this.app.useMiddleware([
|
||||
ReportMiddleware,
|
||||
//统一异常处理
|
||||
GlobalExceptionMiddleware,
|
||||
//预览模式限制修改id<1000的数据
|
||||
PreviewMiddleware,
|
||||
//授权处理
|
||||
AuthorityMiddleware,
|
||||
]);
|
||||
|
||||
//加载插件
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Controller, Get, Provide } from '@midwayjs/decorator';
|
||||
|
||||
@Provide()
|
||||
@Controller('/')
|
||||
export class HomeController {
|
||||
@Get('/')
|
||||
async home(): Promise<string> {
|
||||
return 'Hello Midwayjs!';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import { Config, Provide } from '@midwayjs/decorator';
|
||||
import {
|
||||
IWebMiddleware,
|
||||
IMidwayKoaContext,
|
||||
IMidwayKoaNext,
|
||||
} from '@midwayjs/koa';
|
||||
import * as _ from 'lodash';
|
||||
import * as jwt from 'jsonwebtoken';
|
||||
import { Constants } from '../basic/constants';
|
||||
|
||||
/**
|
||||
* 权限校验
|
||||
*/
|
||||
@Provide()
|
||||
export class AuthorityMiddleware implements IWebMiddleware {
|
||||
@Config('biz.jwt.secret')
|
||||
private secret: string;
|
||||
@Config('biz.auth.ignoreUrls')
|
||||
private ignoreUrls: string[];
|
||||
|
||||
resolve() {
|
||||
return async (ctx: IMidwayKoaContext, next: IMidwayKoaNext) => {
|
||||
const { url } = ctx;
|
||||
const token = ctx.get('Authorization');
|
||||
// 路由地址为 admin前缀的 需要权限校验
|
||||
// console.log('ctx', ctx);
|
||||
const queryIndex = url.indexOf('?');
|
||||
let uri = url;
|
||||
if (queryIndex >= 0) {
|
||||
uri = url.substring(0, queryIndex);
|
||||
}
|
||||
const yes = this.ignoreUrls.includes(uri);
|
||||
if (yes) {
|
||||
await next();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ctx.user = jwt.verify(token, this.secret);
|
||||
} catch (err) {
|
||||
ctx.status = 401;
|
||||
ctx.body = Constants.res.auth;
|
||||
return;
|
||||
}
|
||||
await next();
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Provide } from '@midwayjs/decorator';
|
||||
import {
|
||||
IWebMiddleware,
|
||||
IMidwayKoaContext,
|
||||
IMidwayKoaNext,
|
||||
} from '@midwayjs/koa';
|
||||
import { logger } from '../utils/logger';
|
||||
import { Result } from '../basic/result';
|
||||
|
||||
@Provide()
|
||||
export class GlobalExceptionMiddleware implements IWebMiddleware {
|
||||
resolve() {
|
||||
return async (ctx: IMidwayKoaContext, next: IMidwayKoaNext) => {
|
||||
const { url } = ctx;
|
||||
const startTime = Date.now();
|
||||
logger.info('请求开始:', url);
|
||||
try {
|
||||
await next();
|
||||
logger.info('请求完成', url, Date.now() - startTime + 'ms');
|
||||
} catch (err) {
|
||||
logger.error('请求异常:', url, Date.now() - startTime + 'ms', err);
|
||||
ctx.status = 200;
|
||||
ctx.body = Result.error(err.code != null ? err.code : 1, err.message);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { Config, Provide } from '@midwayjs/decorator';
|
||||
import {
|
||||
IMidwayKoaContext,
|
||||
IMidwayKoaNext,
|
||||
IWebMiddleware,
|
||||
} from '@midwayjs/koa';
|
||||
import { PreviewException } from '../basic/exception/preview-exception';
|
||||
|
||||
/**
|
||||
* 预览模式
|
||||
*/
|
||||
@Provide()
|
||||
export class PreviewMiddleware implements IWebMiddleware {
|
||||
@Config('preview.enabled')
|
||||
private preview: boolean;
|
||||
|
||||
resolve() {
|
||||
return async (ctx: IMidwayKoaContext, next: IMidwayKoaNext) => {
|
||||
if (!this.preview) {
|
||||
await next();
|
||||
return;
|
||||
}
|
||||
let { url, request } = ctx;
|
||||
const body: any = request.body;
|
||||
let id = body.id || request.query.id;
|
||||
const roleId = body.roleId;
|
||||
if (id == null && roleId != null) {
|
||||
id = roleId;
|
||||
}
|
||||
if (id != null && typeof id === 'string') {
|
||||
id = parseInt(id);
|
||||
}
|
||||
if (url.indexOf('?') !== -1) {
|
||||
url = url.substring(0, url.indexOf('?'));
|
||||
}
|
||||
const isModify =
|
||||
url.endsWith('update') ||
|
||||
url.endsWith('delete') ||
|
||||
url.endsWith('authz');
|
||||
const isPreviewId = id < 1000;
|
||||
if (this.preview && isModify && isPreviewId) {
|
||||
throw new PreviewException(
|
||||
'对不起,预览环境不允许修改此数据,如需体验请添加新数据'
|
||||
);
|
||||
}
|
||||
await next();
|
||||
return;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Provide } from '@midwayjs/decorator';
|
||||
import {
|
||||
IWebMiddleware,
|
||||
IMidwayKoaContext,
|
||||
IMidwayKoaNext,
|
||||
} from '@midwayjs/koa';
|
||||
import { logger } from '../utils/logger';
|
||||
|
||||
@Provide()
|
||||
export class ReportMiddleware implements IWebMiddleware {
|
||||
resolve() {
|
||||
return async (ctx: IMidwayKoaContext, next: IMidwayKoaNext) => {
|
||||
const { url } = ctx;
|
||||
logger.info('请求开始:', url);
|
||||
const startTime = Date.now();
|
||||
await next();
|
||||
if (ctx.status !== 200) {
|
||||
logger.error(
|
||||
'请求失败:',
|
||||
url,
|
||||
ctx.status,
|
||||
Date.now() - startTime + 'ms'
|
||||
);
|
||||
}
|
||||
logger.info('请求完成:', url, ctx.status, Date.now() - startTime + 'ms');
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import {
|
||||
ALL,
|
||||
Body,
|
||||
Controller,
|
||||
Inject,
|
||||
Post,
|
||||
Provide,
|
||||
Query,
|
||||
} from '@midwayjs/decorator';
|
||||
import { CrudController } from '../../../basic/crud-controller';
|
||||
import { PermissionService } from '../service/permission-service';
|
||||
|
||||
/**
|
||||
* 权限资源
|
||||
*/
|
||||
@Provide()
|
||||
@Controller('/api/sys/authority/permission')
|
||||
export class PermissionController extends CrudController {
|
||||
@Inject()
|
||||
service: PermissionService;
|
||||
|
||||
getService() {
|
||||
return this.service;
|
||||
}
|
||||
|
||||
@Post('/page')
|
||||
async page(
|
||||
@Body(ALL)
|
||||
body
|
||||
) {
|
||||
return await super.page(body);
|
||||
}
|
||||
|
||||
@Post('/add')
|
||||
async add(
|
||||
@Body(ALL)
|
||||
bean
|
||||
) {
|
||||
return await super.add(bean);
|
||||
}
|
||||
|
||||
@Post('/update')
|
||||
async update(
|
||||
@Body(ALL)
|
||||
bean
|
||||
) {
|
||||
return await super.update(bean);
|
||||
}
|
||||
@Post('/delete')
|
||||
async delete(
|
||||
@Query('id')
|
||||
id
|
||||
) {
|
||||
return await super.delete(id);
|
||||
}
|
||||
|
||||
@Post('/tree')
|
||||
async tree() {
|
||||
const tree = await this.service.tree({});
|
||||
return this.ok(tree);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import {
|
||||
ALL,
|
||||
Body,
|
||||
Controller,
|
||||
Inject,
|
||||
Post,
|
||||
Provide,
|
||||
Query,
|
||||
} from '@midwayjs/decorator';
|
||||
import { CrudController } from '../../../basic/crud-controller';
|
||||
import { RoleService } from '../service/role-service';
|
||||
|
||||
/**
|
||||
* 系统用户
|
||||
*/
|
||||
@Provide()
|
||||
@Controller('/api/sys/authority/role')
|
||||
export class RoleController extends CrudController {
|
||||
@Inject()
|
||||
service: RoleService;
|
||||
|
||||
getService() {
|
||||
return this.service;
|
||||
}
|
||||
|
||||
@Post('/page')
|
||||
async page(
|
||||
@Body(ALL)
|
||||
body
|
||||
) {
|
||||
return await super.page(body);
|
||||
}
|
||||
|
||||
@Post('/list')
|
||||
async list() {
|
||||
const ret = await this.service.find({});
|
||||
return this.ok(ret);
|
||||
}
|
||||
|
||||
@Post('/add')
|
||||
async add(
|
||||
@Body(ALL)
|
||||
bean
|
||||
) {
|
||||
return await super.add(bean);
|
||||
}
|
||||
|
||||
@Post('/update')
|
||||
async update(
|
||||
@Body(ALL)
|
||||
bean
|
||||
) {
|
||||
return await super.update(bean);
|
||||
}
|
||||
@Post('/delete')
|
||||
async delete(
|
||||
@Query('id')
|
||||
id
|
||||
) {
|
||||
return await super.delete(id);
|
||||
}
|
||||
|
||||
@Post('/getPermissionTree')
|
||||
async getPermissionTree(
|
||||
@Query('id')
|
||||
id
|
||||
) {
|
||||
const ret = await this.service.getPermissionTreeByRoleId(id);
|
||||
return this.ok(ret);
|
||||
}
|
||||
|
||||
@Post('/getPermissionIds')
|
||||
async getPermissionIds(
|
||||
@Query('id')
|
||||
id
|
||||
) {
|
||||
const ret = await this.service.getPermissionIdsByRoleId(id);
|
||||
return this.ok(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* 给角色授予权限
|
||||
* @param id
|
||||
*/
|
||||
@Post('/authz')
|
||||
async authz(
|
||||
@Body('roleId')
|
||||
roleId,
|
||||
@Body('permissionIds')
|
||||
permissionIds
|
||||
) {
|
||||
await this.service.authz(roleId, permissionIds);
|
||||
return this.ok(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
import {
|
||||
Provide,
|
||||
Controller,
|
||||
Post,
|
||||
Inject,
|
||||
Body,
|
||||
Query,
|
||||
ALL,
|
||||
} from '@midwayjs/decorator';
|
||||
import { UserService } from '../service/user-service';
|
||||
import { CrudController } from '../../../basic/crud-controller';
|
||||
import { RoleService } from '../service/role-service';
|
||||
import { PermissionService } from '../service/permission-service';
|
||||
|
||||
/**
|
||||
* 系统用户
|
||||
*/
|
||||
@Provide()
|
||||
@Controller('/api/sys/authority/user')
|
||||
export class UserController extends CrudController {
|
||||
@Inject()
|
||||
service: UserService;
|
||||
|
||||
@Inject()
|
||||
roleService: RoleService;
|
||||
@Inject()
|
||||
permissionService: PermissionService;
|
||||
|
||||
getService() {
|
||||
return this.service;
|
||||
}
|
||||
|
||||
@Post('/page')
|
||||
async page(
|
||||
@Body(ALL)
|
||||
body
|
||||
) {
|
||||
const ret = await super.page(body);
|
||||
|
||||
const users = ret.data.records;
|
||||
|
||||
//获取roles
|
||||
const userIds = users.map(item => item.id);
|
||||
const userRoles = await this.roleService.getByUserIds(userIds);
|
||||
const userRolesMap = new Map();
|
||||
for (const ur of userRoles) {
|
||||
let roles = userRolesMap.get(ur.userId);
|
||||
if (roles == null) {
|
||||
roles = [];
|
||||
userRolesMap.set(ur.userId, roles);
|
||||
}
|
||||
roles.push(ur.roleId);
|
||||
}
|
||||
|
||||
for (const record of users) {
|
||||
//withRoles
|
||||
record.roles = userRolesMap.get(record.id);
|
||||
//删除密码字段
|
||||
delete record.password;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Post('/add')
|
||||
async add(
|
||||
@Body(ALL)
|
||||
bean
|
||||
) {
|
||||
return await super.add(bean);
|
||||
}
|
||||
|
||||
@Post('/update')
|
||||
async update(
|
||||
@Body(ALL)
|
||||
bean
|
||||
) {
|
||||
return await super.update(bean);
|
||||
}
|
||||
@Post('/delete')
|
||||
async delete(
|
||||
@Query('id')
|
||||
id
|
||||
) {
|
||||
return await super.delete(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前登录用户的个人信息
|
||||
*/
|
||||
@Post('/mine')
|
||||
public async mine() {
|
||||
const id = this.ctx.user.id;
|
||||
const info = await this.service.info(id, ['password']);
|
||||
return this.ok(info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前登录用户的权限列表
|
||||
*/
|
||||
@Post('/permissions')
|
||||
public async permissions() {
|
||||
const id = this.ctx.user.id;
|
||||
const permissions = await this.service.getUserPermissions(id);
|
||||
return this.ok(permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前登录用户的权限树形列表
|
||||
*/
|
||||
@Post('/permissionTree')
|
||||
public async permissionTree() {
|
||||
const id = this.ctx.user.id;
|
||||
const permissions = await this.service.getUserPermissions(id);
|
||||
const tree = this.permissionService.buildTree(permissions);
|
||||
return this.ok(tree);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 权限
|
||||
*/
|
||||
@Entity('sys_permission')
|
||||
export class PermissionEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
@Column({ comment: '标题', length: 100 })
|
||||
title: string;
|
||||
/**
|
||||
* 权限代码
|
||||
* 示例:sys:user:read
|
||||
*/
|
||||
@Column({ comment: '权限代码', length: 100, nullable: true })
|
||||
permission: string;
|
||||
|
||||
@Column({ name: 'parent_id', comment: '父节点ID', default: -1 })
|
||||
parentId: number;
|
||||
|
||||
@Column({ comment: '排序号' })
|
||||
sort: number;
|
||||
|
||||
@Column({
|
||||
name: 'create_time',
|
||||
comment: '创建时间',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
})
|
||||
createTime: Date;
|
||||
@Column({
|
||||
name: 'update_time',
|
||||
comment: '修改时间',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
})
|
||||
updateTime: Date;
|
||||
|
||||
// @ManyToMany(type => RoleEntity, res => res.permissions)
|
||||
// roles: RoleEntity[];
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Entity, PrimaryColumn } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 角色权限多对多
|
||||
*/
|
||||
@Entity('sys_role_permission')
|
||||
export class RolePermissionEntity {
|
||||
@PrimaryColumn({ name: 'role_id' })
|
||||
roleId: number;
|
||||
@PrimaryColumn({ name: 'permission_id' })
|
||||
permissionId: number;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 角色
|
||||
*/
|
||||
@Entity('sys_role')
|
||||
export class RoleEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
@Index({ unique: true })
|
||||
@Column({ comment: '角色名称', length: 100 })
|
||||
name: string;
|
||||
|
||||
@Column({
|
||||
name: 'create_time',
|
||||
comment: '创建时间',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
})
|
||||
createTime: Date;
|
||||
@Column({
|
||||
name: 'update_time',
|
||||
comment: '修改时间',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
})
|
||||
updateTime: Date;
|
||||
|
||||
// @ManyToMany(type => PermissionEntity, res => res.roles)
|
||||
// @JoinTable({
|
||||
// name: 'sys_role_resources',
|
||||
// joinColumn: {
|
||||
// name: 'roleId',
|
||||
// referencedColumnName: 'id',
|
||||
// },
|
||||
// inverseJoinColumn: {
|
||||
// name: 'resourceId',
|
||||
// referencedColumnName: 'id',
|
||||
// },
|
||||
// })
|
||||
// resources: PermissionEntity[];
|
||||
|
||||
// @ManyToMany(type => UserEntity, res => res.roles)
|
||||
// users: UserEntity[];
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Entity, PrimaryColumn } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 用户角色多对多
|
||||
*/
|
||||
@Entity('sys_user_role')
|
||||
export class UserRoleEntity {
|
||||
@PrimaryColumn({ name: 'role_id' })
|
||||
roleId: number;
|
||||
@PrimaryColumn({ name: 'user_id' })
|
||||
userId: number;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 系统用户
|
||||
*/
|
||||
@Entity('sys_user')
|
||||
export class UserEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
@Index({ unique: true })
|
||||
@Column({ comment: '用户名', length: 100 })
|
||||
username: string;
|
||||
|
||||
@Column({ comment: '密码', length: 100 })
|
||||
password: string;
|
||||
|
||||
@Column({ name: 'nick_name', comment: '昵称', length: 100, nullable: true })
|
||||
nickName: string;
|
||||
|
||||
@Column({ comment: '头像', length: 255, nullable: true })
|
||||
avatar: string;
|
||||
|
||||
@Column({ name: 'phone_code', comment: '区号', length: 20, nullable: true })
|
||||
phoneCode: string;
|
||||
|
||||
@Column({ comment: '手机', length: 20, nullable: true })
|
||||
mobile: string;
|
||||
|
||||
@Column({ comment: '邮箱', length: 50, nullable: true })
|
||||
email: string;
|
||||
|
||||
@Column({ comment: '备注', length: 100, nullable: true })
|
||||
remark: string;
|
||||
|
||||
@Column({ comment: '状态 0:禁用 1:启用', default: 1, type: 'int' })
|
||||
status: number;
|
||||
@Column({
|
||||
name: 'create_time',
|
||||
comment: '创建时间',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
})
|
||||
createTime: Date;
|
||||
@Column({
|
||||
name: 'update_time',
|
||||
comment: '修改时间',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
})
|
||||
updateTime: Date;
|
||||
|
||||
// @ManyToMany(type => RoleEntity, res => res.users)
|
||||
// @JoinTable({
|
||||
// name: 'sys_user_roles',
|
||||
// joinColumn: {
|
||||
// name: 'userId',
|
||||
// referencedColumnName: 'id',
|
||||
// },
|
||||
// inverseJoinColumn: {
|
||||
// name: 'roleId',
|
||||
// referencedColumnName: 'id',
|
||||
// },
|
||||
// })
|
||||
// roles: RoleEntity[];
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { EnumItem } from '../../../basic/enum-item';
|
||||
import * as _ from 'lodash';
|
||||
class ResourceTypes {
|
||||
MENU = new EnumItem('menu', '菜单', 'blue');
|
||||
BTN = new EnumItem('btn', '按钮', 'green');
|
||||
ROUTE = new EnumItem('route', '路由', 'red');
|
||||
|
||||
names() {
|
||||
const list = [];
|
||||
_.forEach(this, (item, key) => {
|
||||
list.push(item);
|
||||
});
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
export const ResourceTypeEnum = new ResourceTypes();
|
||||
@@ -0,0 +1,52 @@
|
||||
import { Provide } from '@midwayjs/decorator';
|
||||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { BaseService } from '../../../basic/base-service';
|
||||
import { PermissionEntity } from '../entity/permission';
|
||||
|
||||
/**
|
||||
* 权限资源
|
||||
*/
|
||||
@Provide()
|
||||
export class PermissionService extends BaseService<PermissionEntity> {
|
||||
@InjectEntityModel(PermissionEntity)
|
||||
repository: Repository<PermissionEntity>;
|
||||
|
||||
getRepository() {
|
||||
return this.repository;
|
||||
}
|
||||
|
||||
async tree(options: any = {}) {
|
||||
if (options.order == null) {
|
||||
options.order = {
|
||||
sort: 'ASC',
|
||||
};
|
||||
}
|
||||
const list = await this.find(options);
|
||||
return this.buildTree(list);
|
||||
}
|
||||
|
||||
buildTree(list: any) {
|
||||
const idMap = {};
|
||||
const root = [];
|
||||
for (const item of list) {
|
||||
idMap[item.id] = item;
|
||||
if (item.parentId == null || item.parentId <= 0) {
|
||||
root.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
for (const item of list) {
|
||||
if (item.parentId > 0) {
|
||||
const parent = idMap[item.parentId];
|
||||
if (parent) {
|
||||
if (parent.children == null) {
|
||||
parent.children = [];
|
||||
}
|
||||
parent.children.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
return root;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Provide } from '@midwayjs/decorator';
|
||||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { BaseService } from '../../../basic/base-service';
|
||||
import { RolePermissionEntity } from '../entity/role-permission';
|
||||
|
||||
/**
|
||||
* 角色->权限
|
||||
*/
|
||||
@Provide()
|
||||
export class RolePermissionService extends BaseService<RolePermissionEntity> {
|
||||
@InjectEntityModel(RolePermissionEntity)
|
||||
repository: Repository<RolePermissionEntity>;
|
||||
|
||||
getRepository() {
|
||||
return this.repository;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
import { Inject, Provide } from '@midwayjs/decorator';
|
||||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
import { In, Repository } from 'typeorm';
|
||||
import { BaseService } from '../../../basic/base-service';
|
||||
import { RoleEntity } from '../entity/role';
|
||||
import { UserRoleService } from './user-role-service';
|
||||
import { RolePermissionEntity } from '../entity/role-permission';
|
||||
import { PermissionService } from './permission-service';
|
||||
import * as _ from 'lodash';
|
||||
import { RolePermissionService } from './role-permission-service';
|
||||
/**
|
||||
* 角色
|
||||
*/
|
||||
@Provide()
|
||||
export class RoleService extends BaseService<RoleEntity> {
|
||||
@InjectEntityModel(RoleEntity)
|
||||
repository: Repository<RoleEntity>;
|
||||
@Inject()
|
||||
userRoleService: UserRoleService;
|
||||
@Inject()
|
||||
permissionService: PermissionService;
|
||||
@Inject()
|
||||
rolePermissionService: RolePermissionService;
|
||||
|
||||
getRepository() {
|
||||
return this.repository;
|
||||
}
|
||||
|
||||
async getRoleIdsByUserId(id: any) {
|
||||
const userRoles = await this.userRoleService.find({
|
||||
where: { userId: id },
|
||||
});
|
||||
return userRoles.map(item => item.roleId);
|
||||
}
|
||||
async getByUserIds(ids: any) {
|
||||
return await this.userRoleService.find({
|
||||
where: {
|
||||
userId: In(ids),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async getPermissionByRoleIds(roleIds: any) {
|
||||
return await this.permissionService.repository
|
||||
.createQueryBuilder('permission')
|
||||
.innerJoinAndSelect(
|
||||
RolePermissionEntity,
|
||||
'rp',
|
||||
'rp.permissionId = permission.id and rp.roleId in (:...roleIds)',
|
||||
{ roleIds }
|
||||
)
|
||||
.getMany();
|
||||
}
|
||||
|
||||
async addRoles(userId: number, roles) {
|
||||
if (roles == null || roles.length === 0) {
|
||||
return;
|
||||
}
|
||||
for (const roleId of roles) {
|
||||
await this.userRoleService.add({
|
||||
userId,
|
||||
roleId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async updateRoles(userId, roles) {
|
||||
if (roles == null) {
|
||||
return;
|
||||
}
|
||||
const oldRoleIds = await this.getRoleIdsByUserId(userId);
|
||||
if (_.xor(roles, oldRoleIds).length === 0) {
|
||||
//如果两个数组相等,则不修改
|
||||
return;
|
||||
}
|
||||
//先删除所有
|
||||
await this.userRoleService.delete({ userId });
|
||||
//再添加
|
||||
await this.addRoles(userId, roles);
|
||||
}
|
||||
|
||||
async getPermissionTreeByRoleId(id: any) {
|
||||
const list = await this.getPermissionByRoleIds([id]);
|
||||
return this.permissionService.buildTree(list);
|
||||
}
|
||||
|
||||
async getPermissionIdsByRoleId(id: any) {
|
||||
const list = await this.getPermissionByRoleIds([id]);
|
||||
return list.map(item => item.id);
|
||||
}
|
||||
|
||||
async authz(roleId: any, permissionIds: any) {
|
||||
await this.rolePermissionService.delete({ roleId });
|
||||
for (const permissionId of permissionIds) {
|
||||
await this.rolePermissionService.add({
|
||||
roleId,
|
||||
permissionId,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Provide } from '@midwayjs/decorator';
|
||||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { BaseService } from '../../../basic/base-service';
|
||||
import { UserRoleEntity } from '../entity/user-role';
|
||||
|
||||
/**
|
||||
* 用户->角色
|
||||
*/
|
||||
@Provide()
|
||||
export class UserRoleService extends BaseService<UserRoleEntity> {
|
||||
@InjectEntityModel(UserRoleEntity)
|
||||
repository: Repository<UserRoleEntity>;
|
||||
|
||||
getRepository() {
|
||||
return this.repository;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
import { Inject, Provide } from '@midwayjs/decorator';
|
||||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { UserEntity } from '../entity/user';
|
||||
import _ from 'lodash';
|
||||
import md5 from 'md5';
|
||||
import { CommonException } from '../../../basic/exception/common-exception';
|
||||
import { BaseService } from '../../../basic/base-service';
|
||||
import { logger } from '../../../utils/logger';
|
||||
import { RoleService } from './role-service';
|
||||
import { PermissionService } from './permission-service';
|
||||
import { UserRoleService } from './user-role-service';
|
||||
|
||||
/**
|
||||
* 系统用户
|
||||
*/
|
||||
@Provide()
|
||||
export class UserService extends BaseService<UserEntity> {
|
||||
@InjectEntityModel(UserEntity)
|
||||
repository: Repository<UserEntity>;
|
||||
@Inject()
|
||||
roleService: RoleService;
|
||||
@Inject()
|
||||
permissionService: PermissionService;
|
||||
@Inject()
|
||||
userRoleService: UserRoleService;
|
||||
|
||||
getRepository() {
|
||||
return this.repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得个人信息
|
||||
*/
|
||||
async mine() {
|
||||
const info = await this.repository.findOne({
|
||||
where: {
|
||||
id: this.ctx.user.id,
|
||||
},
|
||||
});
|
||||
delete info.password;
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增
|
||||
* @param param
|
||||
*/
|
||||
async add(param) {
|
||||
const exists = await this.repository.findOne({
|
||||
where: {
|
||||
username: param.username,
|
||||
},
|
||||
});
|
||||
if (!_.isEmpty(exists)) {
|
||||
throw new CommonException('用户名已经存在');
|
||||
}
|
||||
const password = param.password ?? '123456';
|
||||
param.password = md5(password); // 默认密码 建议未改密码不能登陆
|
||||
await super.add(param);
|
||||
//添加角色
|
||||
if (param.roles && param.roles.length > 0) {
|
||||
await this.roleService.addRoles(param.id, param.roles);
|
||||
}
|
||||
return param.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改
|
||||
* @param param 数据
|
||||
*/
|
||||
async update(param) {
|
||||
if (param.id == null) {
|
||||
throw new CommonException('id不能为空');
|
||||
}
|
||||
const userInfo = await this.repository.findOne({
|
||||
where: { id: param.id },
|
||||
});
|
||||
if (!userInfo) {
|
||||
throw new CommonException('用户不存在');
|
||||
}
|
||||
|
||||
delete param.username;
|
||||
if (!_.isEmpty(param.password)) {
|
||||
param.password = md5(param.password);
|
||||
} else {
|
||||
delete param.password;
|
||||
}
|
||||
await super.update(param);
|
||||
await this.roleService.updateRoles(param.id, param.roles);
|
||||
}
|
||||
|
||||
async findOne(param) {
|
||||
return this.repository.findOne({
|
||||
where: param,
|
||||
});
|
||||
}
|
||||
|
||||
checkPassword(rawPassword: any, md5Password: any) {
|
||||
logger.info('md5', md5('123456'));
|
||||
return md5(rawPassword) === md5Password;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户的菜单资源列表
|
||||
* @param id
|
||||
*/
|
||||
async getUserPermissions(id: any) {
|
||||
const roleIds = await this.roleService.getRoleIdsByUserId(id);
|
||||
|
||||
return await this.roleService.getPermissionByRoleIds(roleIds);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import { Rule, RuleType } from '@midwayjs/validate';
|
||||
import { ALL, Inject } from '@midwayjs/decorator';
|
||||
import { Body } from '@midwayjs/decorator';
|
||||
import { Controller, Post, Provide } from '@midwayjs/decorator';
|
||||
import { BaseController } from '../../../basic/base-controller';
|
||||
import { CodeService } from '../service/code-service';
|
||||
export class SmsCodeReq {
|
||||
@Rule(RuleType.number().required())
|
||||
phoneCode: number;
|
||||
|
||||
@Rule(RuleType.string().required())
|
||||
mobile: string;
|
||||
|
||||
@Rule(RuleType.string().required().max(10))
|
||||
randomStr: string;
|
||||
|
||||
@Rule(RuleType.number().required().max(4))
|
||||
imgCode: string;
|
||||
}
|
||||
|
||||
// const enumsMap = {};
|
||||
// glob('src/modules/**/enums/*.ts', {}, (err, matches) => {
|
||||
// console.log('matched', matches);
|
||||
// for (const filePath of matches) {
|
||||
// const module = require('/' + filePath);
|
||||
// console.log('modules', module);
|
||||
// }
|
||||
// });
|
||||
|
||||
/**
|
||||
*/
|
||||
@Provide()
|
||||
@Controller('/api/basic')
|
||||
export class BasicController extends BaseController {
|
||||
@Inject()
|
||||
codeService: CodeService;
|
||||
@Post('/sendSmsCode')
|
||||
public sendSmsCode(
|
||||
@Body(ALL)
|
||||
body: SmsCodeReq
|
||||
) {
|
||||
// 设置缓存内容
|
||||
return this.ok(null);
|
||||
}
|
||||
|
||||
@Post('/captcha')
|
||||
public async getCaptcha(
|
||||
@Body()
|
||||
randomStr
|
||||
) {
|
||||
console.assert(randomStr < 10, 'randomStr 过长');
|
||||
const captcha = await this.codeService.generateCaptcha(randomStr);
|
||||
return this.ok(captcha.data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { Inject, Provide } from '@midwayjs/decorator';
|
||||
import { CacheManager } from '@midwayjs/cache';
|
||||
const svgCaptcha = require('svg-captcha');
|
||||
|
||||
// {data: '<svg.../svg>', text: 'abcd'}
|
||||
/**
|
||||
*/
|
||||
@Provide()
|
||||
export class CodeService {
|
||||
@Inject()
|
||||
cache: CacheManager; // 依赖注入CacheManager
|
||||
|
||||
/**
|
||||
*/
|
||||
async generateCaptcha(randomStr) {
|
||||
console.assert(randomStr < 10, 'randomStr 过长');
|
||||
const c = svgCaptcha.create();
|
||||
//{data: '<svg.../svg>', text: 'abcd'}
|
||||
const imgCode = c.text; // = RandomUtil.randomStr(4, true);
|
||||
await this.cache.set('imgCode:' + randomStr, imgCode, {
|
||||
ttl: 2 * 60 * 1000, //过期时间 2分钟
|
||||
});
|
||||
return c;
|
||||
}
|
||||
|
||||
async getCaptchaText(randomStr) {
|
||||
return await this.cache.get('imgCode:' + randomStr);
|
||||
}
|
||||
|
||||
async removeCaptcha(randomStr) {
|
||||
await this.cache.del('imgCode:' + randomStr);
|
||||
}
|
||||
|
||||
async checkCaptcha(randomStr, userCaptcha) {
|
||||
const code = await this.getCaptchaText(randomStr);
|
||||
if (code == null) {
|
||||
throw new Error('验证码已过期');
|
||||
}
|
||||
if (code !== userCaptcha) {
|
||||
throw new Error('验证码不正确');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
*/
|
||||
async sendSms(phoneCode, mobile, smsCode) {
|
||||
console.assert(phoneCode != null && mobile != null, '手机号不能为空');
|
||||
console.assert(smsCode != null, '验证码不能为空');
|
||||
}
|
||||
|
||||
/**
|
||||
* loginBySmsCode
|
||||
*/
|
||||
async loginBySmsCode(user, smsCode) {
|
||||
console.assert(user.mobile != null, '手机号不能为空');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Inject,
|
||||
Post,
|
||||
Provide,
|
||||
ALL,
|
||||
} from '@midwayjs/decorator';
|
||||
import { LoginService } from '../service/login-service';
|
||||
import { BaseController } from '../../../basic/base-controller';
|
||||
|
||||
/**
|
||||
*/
|
||||
@Provide()
|
||||
@Controller('/api/')
|
||||
export class LoginController extends BaseController {
|
||||
@Inject()
|
||||
loginService: LoginService;
|
||||
@Post('/login')
|
||||
public async login(
|
||||
@Body(ALL)
|
||||
user
|
||||
) {
|
||||
const token = await this.loginService.login(user);
|
||||
return this.ok(token);
|
||||
}
|
||||
|
||||
@Post('/logout')
|
||||
public logout() {}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { Config, Inject, Provide } from '@midwayjs/decorator';
|
||||
import { UserService } from '../../authority/service/user-service';
|
||||
import * as jwt from 'jsonwebtoken';
|
||||
import { CommonException } from '../../../basic/exception/common-exception';
|
||||
|
||||
/**
|
||||
* 系统用户
|
||||
*/
|
||||
@Provide()
|
||||
export class LoginService {
|
||||
@Inject()
|
||||
userService: UserService;
|
||||
@Config('biz.jwt')
|
||||
private jwt: any;
|
||||
|
||||
/**
|
||||
* login
|
||||
*/
|
||||
async login(user) {
|
||||
console.assert(user.username != null, '用户名不能为空');
|
||||
const info = await this.userService.findOne({ username: user.username });
|
||||
if (info == null) {
|
||||
throw new CommonException('用户名或密码错误');
|
||||
}
|
||||
const right = this.userService.checkPassword(user.password, info.password);
|
||||
if (!right) {
|
||||
throw new CommonException('用户名或密码错误');
|
||||
}
|
||||
|
||||
return this.generateToken(info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成token
|
||||
* @param user 用户对象
|
||||
*/
|
||||
async generateToken(user) {
|
||||
const tokenInfo = {
|
||||
username: user.username,
|
||||
id: user.id,
|
||||
};
|
||||
const expire = this.jwt.expire;
|
||||
const token = jwt.sign(tokenInfo, this.jwt.secret, {
|
||||
expiresIn: expire,
|
||||
});
|
||||
|
||||
return {
|
||||
token,
|
||||
expire,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Autoload, Init, Inject, Scope, ScopeEnum } from "@midwayjs/decorator";
|
||||
import { PipelineService } from '../service/pipeline-service';
|
||||
import { logger } from '../../../utils/logger';
|
||||
|
||||
@Autoload()
|
||||
@Scope(ScopeEnum.Singleton)
|
||||
export class AutoRegisterCron {
|
||||
@Inject()
|
||||
pipelineService: PipelineService;
|
||||
|
||||
// @Inject()
|
||||
// echoPlugin: EchoPlugin;
|
||||
|
||||
@Init()
|
||||
async init() {
|
||||
logger.info('加载定时trigger开始');
|
||||
await this.pipelineService.onStartup();
|
||||
// logger.info(this.echoPlugin, this.echoPlugin.test);
|
||||
// logger.info('加载定时trigger完成');
|
||||
//
|
||||
// const meta = getClassMetadata(CLASS_KEY, this.echoPlugin);
|
||||
// console.log('meta', meta);
|
||||
// const metas = listPropertyDataFromClass(CLASS_KEY, this.echoPlugin);
|
||||
// console.log('metas', metas);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import {
|
||||
ALL,
|
||||
Body,
|
||||
Controller,
|
||||
Inject,
|
||||
Post,
|
||||
Provide,
|
||||
Query,
|
||||
} from '@midwayjs/decorator';
|
||||
import { CrudController } from '../../../basic/crud-controller';
|
||||
import { AccessService } from '../service/access-service';
|
||||
|
||||
/**
|
||||
* 授权
|
||||
*/
|
||||
@Provide()
|
||||
@Controller('/api/pi/access')
|
||||
export class AccessController extends CrudController {
|
||||
@Inject()
|
||||
service: AccessService;
|
||||
|
||||
getService() {
|
||||
return this.service;
|
||||
}
|
||||
|
||||
@Post('/page')
|
||||
async page(@Body(ALL) body) {
|
||||
body.query = body.query ?? {};
|
||||
body.query.userId = this.ctx.user.id;
|
||||
return super.page(body);
|
||||
}
|
||||
|
||||
@Post('/list')
|
||||
async list(@Body(ALL) body) {
|
||||
body.userId = this.ctx.user.id;
|
||||
return super.list(body);
|
||||
}
|
||||
|
||||
@Post('/add')
|
||||
async add(@Body(ALL) bean) {
|
||||
bean.userId = this.ctx.user.id;
|
||||
return super.add(bean);
|
||||
}
|
||||
|
||||
@Post('/update')
|
||||
async update(@Body(ALL) bean) {
|
||||
await this.service.checkUserId(bean.id, this.ctx.user.id);
|
||||
return super.update(bean);
|
||||
}
|
||||
@Post('/info')
|
||||
async info(@Query('id') id) {
|
||||
await this.service.checkUserId(id, this.ctx.user.id);
|
||||
return super.info(id);
|
||||
}
|
||||
|
||||
@Post('/delete')
|
||||
async delete(@Query('id') id) {
|
||||
await this.service.checkUserId(id, this.ctx.user.id);
|
||||
return super.delete(id);
|
||||
}
|
||||
|
||||
@Post('/define')
|
||||
async define(@Query('type') type) {
|
||||
const provider = this.service.getDefineByType(type);
|
||||
return this.ok(provider);
|
||||
}
|
||||
|
||||
@Post('/accessTypeDict')
|
||||
async getAccessTypeDict() {
|
||||
const list = this.service.getDefineList();
|
||||
const dict = [];
|
||||
for (const item of list) {
|
||||
dict.push({
|
||||
value: item.name,
|
||||
label: item.title,
|
||||
});
|
||||
}
|
||||
return this.ok(dict);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import {
|
||||
ALL,
|
||||
Controller,
|
||||
Inject,
|
||||
Post,
|
||||
Provide,
|
||||
Query,
|
||||
} from '@midwayjs/decorator';
|
||||
import { DnsProviderService } from '../service/dns-provider-service';
|
||||
import { BaseController } from '../../../basic/base-controller';
|
||||
|
||||
/**
|
||||
* 插件
|
||||
*/
|
||||
@Provide()
|
||||
@Controller('/api/pi/dnsProvider')
|
||||
export class DnsProviderController extends BaseController {
|
||||
@Inject()
|
||||
service: DnsProviderService;
|
||||
|
||||
@Post('/list')
|
||||
async list(@Query(ALL) query) {
|
||||
query.userId = this.ctx.user.id;
|
||||
const list = this.service.getList();
|
||||
return this.ok(list);
|
||||
}
|
||||
|
||||
@Post('/dnsProviderTypeDict')
|
||||
async getDnsProviderTypeDict() {
|
||||
const list = this.service.getList();
|
||||
const dict = [];
|
||||
for (const item of list) {
|
||||
dict.push({
|
||||
value: item.name,
|
||||
label: item.title,
|
||||
});
|
||||
}
|
||||
return this.ok(dict);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
import {
|
||||
ALL,
|
||||
Body,
|
||||
Controller,
|
||||
Inject,
|
||||
Post,
|
||||
Provide,
|
||||
Query,
|
||||
} from '@midwayjs/decorator';
|
||||
import { CrudController } from '../../../basic/crud-controller';
|
||||
import { PipelineEntity } from '../entity/pipeline';
|
||||
import { HistoryService } from '../service/history-service';
|
||||
import { HistoryLogService } from '../service/history-log-service';
|
||||
import { HistoryEntity } from '../entity/history';
|
||||
import { HistoryLogEntity } from '../entity/history-log';
|
||||
|
||||
/**
|
||||
* 证书
|
||||
*/
|
||||
@Provide()
|
||||
@Controller('/api/pi/history')
|
||||
export class HistoryController extends CrudController {
|
||||
@Inject()
|
||||
service: HistoryService;
|
||||
@Inject()
|
||||
logService: HistoryLogService;
|
||||
|
||||
getService() {
|
||||
return this.service;
|
||||
}
|
||||
|
||||
@Post('/page')
|
||||
async page(@Body(ALL) body) {
|
||||
body.query.userId = this.ctx.user.id;
|
||||
return super.page(body);
|
||||
}
|
||||
|
||||
@Post('/list')
|
||||
async list(@Body(ALL) body) {
|
||||
body.userId = this.ctx.user.id;
|
||||
if (body.pipelineId == null) {
|
||||
return this.ok([]);
|
||||
}
|
||||
const buildQuery = qb => {
|
||||
qb.limit(10);
|
||||
};
|
||||
const listRet = await this.getService().list(
|
||||
body,
|
||||
{ prop: 'id', asc: false },
|
||||
buildQuery
|
||||
);
|
||||
return this.ok(listRet);
|
||||
}
|
||||
|
||||
@Post('/add')
|
||||
async add(@Body(ALL) bean: PipelineEntity) {
|
||||
bean.userId = this.ctx.user.id;
|
||||
return super.add(bean);
|
||||
}
|
||||
|
||||
@Post('/update')
|
||||
async update(@Body(ALL) bean) {
|
||||
await this.service.checkUserId(bean.id, this.ctx.user.id);
|
||||
return super.update(bean);
|
||||
}
|
||||
|
||||
@Post('/save')
|
||||
async save(@Body(ALL) bean: HistoryEntity) {
|
||||
bean.userId = this.ctx.user.id;
|
||||
if (bean.id > 0) {
|
||||
await this.service.checkUserId(bean.id, this.ctx.user.id);
|
||||
}
|
||||
await this.service.save(bean);
|
||||
return this.ok(bean.id);
|
||||
}
|
||||
|
||||
@Post('/saveLog')
|
||||
async saveLog(@Body(ALL) bean: HistoryLogEntity) {
|
||||
bean.userId = this.ctx.user.id;
|
||||
if (bean.id > 0) {
|
||||
await this.service.checkUserId(bean.id, this.ctx.user.id);
|
||||
}
|
||||
await this.logService.save(bean);
|
||||
return this.ok(bean.id);
|
||||
}
|
||||
|
||||
@Post('/delete')
|
||||
async delete(@Query('id') id) {
|
||||
await this.service.checkUserId(id, this.ctx.user.id);
|
||||
return super.delete(id);
|
||||
}
|
||||
|
||||
@Post('/detail')
|
||||
async detail(@Query('id') id) {
|
||||
await this.service.checkUserId(id, this.ctx.user.id);
|
||||
const detail = await this.service.detail(id);
|
||||
return this.ok(detail);
|
||||
}
|
||||
|
||||
@Post('/logs')
|
||||
async logs(@Query('id') id) {
|
||||
await this.logService.checkUserId(id, this.ctx.user.id);
|
||||
const logInfo = await this.logService.info(id);
|
||||
return this.ok(logInfo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import {
|
||||
ALL,
|
||||
Body,
|
||||
Controller,
|
||||
Inject,
|
||||
Post,
|
||||
Provide,
|
||||
Query,
|
||||
} from '@midwayjs/decorator';
|
||||
import { CrudController } from '../../../basic/crud-controller';
|
||||
import { PipelineService } from '../service/pipeline-service';
|
||||
import { PipelineEntity } from '../entity/pipeline';
|
||||
|
||||
/**
|
||||
* 证书
|
||||
*/
|
||||
@Provide()
|
||||
@Controller('/api/pi/pipeline')
|
||||
export class PipelineController extends CrudController {
|
||||
@Inject()
|
||||
service: PipelineService;
|
||||
|
||||
getService() {
|
||||
return this.service;
|
||||
}
|
||||
|
||||
@Post('/page')
|
||||
async page(@Body(ALL) body) {
|
||||
body.query.userId = this.ctx.user.id;
|
||||
const buildQuery = qb => {
|
||||
qb.where({});
|
||||
};
|
||||
return super.page({ ...body, buildQuery });
|
||||
}
|
||||
|
||||
@Post('/add')
|
||||
async add(@Body(ALL) bean: PipelineEntity) {
|
||||
bean.userId = this.ctx.user.id;
|
||||
return super.add(bean);
|
||||
}
|
||||
|
||||
@Post('/update')
|
||||
async update(@Body(ALL) bean) {
|
||||
await this.service.checkUserId(bean.id, this.ctx.user.id);
|
||||
return super.update(bean);
|
||||
}
|
||||
|
||||
@Post('/save')
|
||||
async save(@Body(ALL) bean: PipelineEntity) {
|
||||
bean.userId = this.ctx.user.id;
|
||||
if (bean.id > 0) {
|
||||
await this.service.checkUserId(bean.id, this.ctx.user.id);
|
||||
}
|
||||
await this.service.save(bean);
|
||||
return this.ok(bean.id);
|
||||
}
|
||||
|
||||
@Post('/delete')
|
||||
async delete(@Query('id') id) {
|
||||
await this.service.checkUserId(id, this.ctx.user.id);
|
||||
return super.delete(id);
|
||||
}
|
||||
|
||||
@Post('/detail')
|
||||
async detail(@Query('id') id) {
|
||||
await this.service.checkUserId(id, this.ctx.user.id);
|
||||
const detail = await this.service.detail(id);
|
||||
return this.ok(detail);
|
||||
}
|
||||
|
||||
@Post('/trigger')
|
||||
async trigger(@Query('id') id) {
|
||||
await this.service.checkUserId(id, this.ctx.user.id);
|
||||
await this.service.trigger(id);
|
||||
return this.ok({});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import {
|
||||
ALL,
|
||||
Controller,
|
||||
Inject,
|
||||
Post,
|
||||
Provide,
|
||||
Query,
|
||||
} from '@midwayjs/decorator';
|
||||
import { BaseController } from '../../../basic/base-controller';
|
||||
import { PluginService } from '../service/plugin-service';
|
||||
|
||||
/**
|
||||
* 插件
|
||||
*/
|
||||
@Provide()
|
||||
@Controller('/api/pi/plugin')
|
||||
export class PluginController extends BaseController {
|
||||
@Inject()
|
||||
service: PluginService;
|
||||
|
||||
@Post('/list')
|
||||
async list(@Query(ALL) query) {
|
||||
query.userId = this.ctx.user.id;
|
||||
const list = this.service.getList();
|
||||
return this.ok(list);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 授权配置
|
||||
*/
|
||||
@Entity('cd_access')
|
||||
export class AccessEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
@Column({ name: 'user_id', comment: '用户id' })
|
||||
userId: number;
|
||||
@Column({ comment: '名称', length: 100 })
|
||||
name: string;
|
||||
|
||||
@Column({ comment: '类型', length: 100 })
|
||||
type: string;
|
||||
|
||||
@Column({ name: 'setting', comment: '设置', length: 1024, nullable: true })
|
||||
setting: string;
|
||||
|
||||
@Column({
|
||||
name: 'create_time',
|
||||
comment: '创建时间',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
})
|
||||
createTime: Date;
|
||||
@Column({
|
||||
name: 'update_time',
|
||||
comment: '修改时间',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
})
|
||||
updateTime: Date;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
|
||||
|
||||
@Entity('pi_history_log')
|
||||
export class HistoryLogEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ name: 'user_id', comment: '用户id' })
|
||||
userId: number;
|
||||
|
||||
@Column({ name: 'pipeline_id', comment: '流水线' })
|
||||
pipelineId: number;
|
||||
|
||||
@Column({ name: 'history_id', comment: '历史id' })
|
||||
historyId: number;
|
||||
|
||||
@Column({
|
||||
name: 'node_id',
|
||||
comment: '任务节点id',
|
||||
length: 100,
|
||||
nullable: true,
|
||||
})
|
||||
nodeId: string;
|
||||
|
||||
@Column({ comment: '日志内容', length: 40960, nullable: true })
|
||||
logs: string;
|
||||
|
||||
@Column({
|
||||
name: 'create_time',
|
||||
comment: '创建时间',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
})
|
||||
createTime: Date;
|
||||
|
||||
@Column({
|
||||
name: 'update_time',
|
||||
comment: '修改时间',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
})
|
||||
updateTime: Date;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
|
||||
|
||||
@Entity('pi_history')
|
||||
export class HistoryEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ name: 'user_id', comment: '用户id' })
|
||||
userId: number;
|
||||
|
||||
@Column({ name: 'pipeline_id', comment: '流水线' })
|
||||
pipelineId: number;
|
||||
@Column({ comment: '运行状态', length: 40960, nullable: true })
|
||||
pipeline: string;
|
||||
|
||||
@Column({ comment: '结果状态', length: 20, nullable: true })
|
||||
status: string;
|
||||
|
||||
@Column({
|
||||
name: 'end_time',
|
||||
comment: '结束时间',
|
||||
nullable: true,
|
||||
})
|
||||
endTime: Date;
|
||||
|
||||
@Column({
|
||||
name: 'create_time',
|
||||
comment: '创建时间',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
})
|
||||
createTime: Date;
|
||||
@Column({
|
||||
name: 'update_time',
|
||||
comment: '修改时间',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
})
|
||||
updateTime: Date;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
|
||||
|
||||
@Entity('pi_pipeline')
|
||||
export class PipelineEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ name: 'user_id', comment: '用户id' })
|
||||
userId: number;
|
||||
|
||||
@Column({ name: 'title', comment: '标题' })
|
||||
title: number;
|
||||
|
||||
@Column({ comment: '配置', length: 40960 })
|
||||
content: string;
|
||||
|
||||
@Column({
|
||||
name: 'keep_history_count',
|
||||
comment: '历史记录保持数量',
|
||||
nullable: true,
|
||||
})
|
||||
keepHistoryCount: number;
|
||||
|
||||
@Column({ comment: '备注', length: 100, nullable: true })
|
||||
remark: string;
|
||||
|
||||
@Column({ comment: '状态', length: 100, nullable: true })
|
||||
status: string;
|
||||
|
||||
@Column({ comment: '启用/禁用', nullable: true, default: false })
|
||||
disabled: boolean;
|
||||
|
||||
@Column({
|
||||
name: 'last_history_time',
|
||||
comment: '最后一次执行时间',
|
||||
nullable: true,
|
||||
})
|
||||
lastHistoryTime: number;
|
||||
|
||||
@Column({
|
||||
name: 'create_time',
|
||||
comment: '创建时间',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
})
|
||||
createTime: Date;
|
||||
@Column({
|
||||
name: 'update_time',
|
||||
comment: '修改时间',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
})
|
||||
updateTime: Date;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
|
||||
|
||||
@Entity('pi_storage')
|
||||
export class StorageEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ name: 'user_id', comment: '用户id' })
|
||||
userId: number;
|
||||
|
||||
@Column({ name: 'scope', comment: '范围' })
|
||||
scope: string;
|
||||
|
||||
@Column({ name: 'namespace', comment: '命名空间' })
|
||||
namespace: string;
|
||||
|
||||
@Column({ comment: 'key', length: 100, nullable: true })
|
||||
key: string;
|
||||
|
||||
@Column({ comment: 'value', length: 40960, nullable: true })
|
||||
value: string;
|
||||
|
||||
@Column({
|
||||
name: 'create_time',
|
||||
comment: '创建时间',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
})
|
||||
createTime: Date;
|
||||
@Column({
|
||||
name: 'update_time',
|
||||
comment: '修改时间',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
})
|
||||
updateTime: Date;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { HistoryEntity } from '../history';
|
||||
import { HistoryLogEntity } from '../history-log';
|
||||
|
||||
export class HistoryDetail {
|
||||
history: HistoryEntity;
|
||||
log: HistoryLogEntity;
|
||||
|
||||
constructor(history: HistoryEntity, log: HistoryLogEntity) {
|
||||
this.history = history;
|
||||
this.log = log;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { PipelineEntity } from '../pipeline';
|
||||
import { HistoryEntity } from '../history';
|
||||
import { HistoryLogEntity } from '../history-log';
|
||||
|
||||
export class PipelineDetail {
|
||||
pipeline: PipelineEntity;
|
||||
constructor(pipeline: PipelineEntity) {
|
||||
this.pipeline = pipeline;
|
||||
}
|
||||
|
||||
last: HistoryEntity;
|
||||
logs: HistoryLogEntity[];
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { Provide, Scope, ScopeEnum } from "@midwayjs/decorator";
|
||||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { BaseService } from '../../../basic/base-service';
|
||||
import { AccessEntity } from '../entity/access';
|
||||
import {
|
||||
accessRegistry,
|
||||
IAccessService,
|
||||
} from '@certd/pipeline';
|
||||
|
||||
/**
|
||||
* 授权
|
||||
*/
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Singleton)
|
||||
export class AccessService
|
||||
extends BaseService<AccessEntity>
|
||||
implements IAccessService
|
||||
{
|
||||
@InjectEntityModel(AccessEntity)
|
||||
repository: Repository<AccessEntity>;
|
||||
|
||||
getRepository() {
|
||||
return this.repository;
|
||||
}
|
||||
|
||||
async getById(id: any): Promise<any> {
|
||||
const entity = await this.info(id);
|
||||
// const access = accessRegistry.get(entity.type);
|
||||
const setting = JSON.parse(entity.setting);
|
||||
return {
|
||||
id: entity.id,
|
||||
...setting,
|
||||
};
|
||||
}
|
||||
|
||||
getDefineList() {
|
||||
return accessRegistry.getDefineList();
|
||||
}
|
||||
|
||||
getDefineByType(type) {
|
||||
return accessRegistry.getDefine(type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { IStorage } from '@certd/pipeline/src/core/storage';
|
||||
import { StorageService } from './storage-service';
|
||||
|
||||
export class DbStorage implements IStorage {
|
||||
/**
|
||||
* 范围: user / pipeline / runtime / task
|
||||
*/
|
||||
storageService: StorageService;
|
||||
userId: number;
|
||||
constructor(userId: number, storageService: StorageService) {
|
||||
this.userId = userId;
|
||||
this.storageService = storageService;
|
||||
}
|
||||
|
||||
async get(
|
||||
scope: string,
|
||||
namespace: string,
|
||||
key: string
|
||||
): Promise<string | null> {
|
||||
const storageEntity = await this.storageService.get({
|
||||
userId: this.userId,
|
||||
scope: scope,
|
||||
namespace: namespace,
|
||||
key,
|
||||
});
|
||||
|
||||
if (storageEntity != null) {
|
||||
return storageEntity.value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async set(
|
||||
scope: string,
|
||||
namespace: string,
|
||||
key: string,
|
||||
value: string
|
||||
): Promise<void> {
|
||||
await this.storageService.set({
|
||||
userId: this.userId,
|
||||
scope: scope,
|
||||
namespace: namespace,
|
||||
key,
|
||||
value,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Provide, Scope, ScopeEnum } from "@midwayjs/decorator";
|
||||
import { dnsProviderRegistry } from '@certd/plugin-cert';
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Singleton)
|
||||
export class DnsProviderService {
|
||||
getList() {
|
||||
return dnsProviderRegistry.getDefineList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Provide, Scope, ScopeEnum } from "@midwayjs/decorator";
|
||||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { BaseService } from '../../../basic/base-service';
|
||||
import { HistoryLogEntity } from '../entity/history-log';
|
||||
|
||||
/**
|
||||
* 证书申请
|
||||
*/
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Singleton)
|
||||
export class HistoryLogService extends BaseService<HistoryLogEntity> {
|
||||
@InjectEntityModel(HistoryLogEntity)
|
||||
repository: Repository<HistoryLogEntity>;
|
||||
|
||||
getRepository() {
|
||||
return this.repository;
|
||||
}
|
||||
|
||||
async save(bean: HistoryLogEntity) {
|
||||
if (bean.id > 0) {
|
||||
await this.update(bean);
|
||||
} else {
|
||||
await this.add(bean);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/decorator";
|
||||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { BaseService } from '../../../basic/base-service';
|
||||
import { HistoryEntity } from '../entity/history';
|
||||
import { PipelineEntity } from '../entity/pipeline';
|
||||
import { HistoryDetail } from '../entity/vo/history-detail';
|
||||
import { HistoryLogService } from './history-log-service';
|
||||
|
||||
/**
|
||||
* 证书申请
|
||||
*/
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Singleton)
|
||||
export class HistoryService extends BaseService<HistoryEntity> {
|
||||
@InjectEntityModel(HistoryEntity)
|
||||
repository: Repository<HistoryEntity>;
|
||||
@Inject()
|
||||
logService: HistoryLogService;
|
||||
getRepository() {
|
||||
return this.repository;
|
||||
}
|
||||
|
||||
async save(bean: HistoryEntity) {
|
||||
if (bean.id > 0) {
|
||||
await this.update(bean);
|
||||
} else {
|
||||
await this.add(bean);
|
||||
}
|
||||
}
|
||||
|
||||
async detail(historyId: string) {
|
||||
const entity = await this.info(historyId);
|
||||
const log = await this.logService.info(historyId);
|
||||
return new HistoryDetail(entity, log);
|
||||
}
|
||||
|
||||
async start(pipeline: PipelineEntity) {
|
||||
const bean = {
|
||||
userId: pipeline.userId,
|
||||
pipelineId: pipeline.id,
|
||||
title: pipeline.title,
|
||||
status: 'start',
|
||||
};
|
||||
const { id } = await this.add(bean);
|
||||
//清除大于pipeline.keepHistoryCount的历史记录
|
||||
this.clear(pipeline.id, pipeline.keepHistoryCount);
|
||||
return id;
|
||||
}
|
||||
|
||||
private async clear(pipelineId: number, keepCount = 30) {
|
||||
const count = await this.repository.count({
|
||||
where: {
|
||||
pipelineId,
|
||||
},
|
||||
});
|
||||
if (count <= keepCount) {
|
||||
return;
|
||||
}
|
||||
let shouldDeleteCount = count - keepCount;
|
||||
const deleteCountBatch = 100;
|
||||
while (shouldDeleteCount > 0) {
|
||||
const list = await this.repository.find({
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
where: {
|
||||
pipelineId,
|
||||
},
|
||||
order: {
|
||||
id: 'ASC',
|
||||
},
|
||||
skip: 0,
|
||||
take: deleteCountBatch,
|
||||
});
|
||||
await this.repository.remove(list);
|
||||
|
||||
shouldDeleteCount -= deleteCountBatch;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/decorator';
|
||||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
import { In, Repository } from 'typeorm';
|
||||
import { BaseService } from '../../../basic/base-service';
|
||||
import { PipelineEntity } from '../entity/pipeline';
|
||||
import { PipelineDetail } from '../entity/vo/pipeline-detail';
|
||||
import { Executor, Pipeline, RunHistory } from '@certd/pipeline';
|
||||
import { AccessService } from './access-service';
|
||||
import { DbStorage } from './db-storage';
|
||||
import { StorageService } from './storage-service';
|
||||
import { Cron } from '../../../plugins/cron/cron';
|
||||
import { HistoryService } from './history-service';
|
||||
import { HistoryEntity } from '../entity/history';
|
||||
import { HistoryLogEntity } from '../entity/history-log';
|
||||
import { HistoryLogService } from './history-log-service';
|
||||
import { logger } from '../../../utils/logger';
|
||||
|
||||
/**
|
||||
* 证书申请
|
||||
*/
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Singleton)
|
||||
export class PipelineService extends BaseService<PipelineEntity> {
|
||||
@InjectEntityModel(PipelineEntity)
|
||||
repository: Repository<PipelineEntity>;
|
||||
|
||||
@Inject()
|
||||
accessService: AccessService;
|
||||
@Inject()
|
||||
storageService: StorageService;
|
||||
@Inject()
|
||||
historyService: HistoryService;
|
||||
@Inject()
|
||||
historyLogService: HistoryLogService;
|
||||
|
||||
@Inject()
|
||||
cron: Cron;
|
||||
|
||||
getRepository() {
|
||||
return this.repository;
|
||||
}
|
||||
|
||||
async update(entity) {
|
||||
await super.update(entity);
|
||||
|
||||
await this.registerTriggerById(entity.id);
|
||||
}
|
||||
|
||||
private async registerTriggerById(pipelineId) {
|
||||
if (pipelineId == null) {
|
||||
return;
|
||||
}
|
||||
const info = await this.info(pipelineId);
|
||||
if (info && !info.disabled) {
|
||||
const pipeline = JSON.parse(info.content);
|
||||
this.registerTriggers(pipeline);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取详情
|
||||
* @param id
|
||||
*/
|
||||
async detail(id) {
|
||||
const pipeline = await this.info(id);
|
||||
return new PipelineDetail(pipeline);
|
||||
}
|
||||
|
||||
async save(bean: PipelineEntity) {
|
||||
const pipeline = JSON.parse(bean.content);
|
||||
bean.title = pipeline.title;
|
||||
await this.addOrUpdate(bean);
|
||||
await this.registerTriggerById(bean.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用启动后初始加载记录
|
||||
*/
|
||||
async onStartup() {
|
||||
const idEntityList = await this.repository.find({
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
where: {
|
||||
disabled: false,
|
||||
},
|
||||
});
|
||||
const ids = idEntityList.map(item => {
|
||||
return item.id;
|
||||
});
|
||||
|
||||
//id 分段
|
||||
const idsSpan = [];
|
||||
let arr = [];
|
||||
for (let i = 0; i < ids.length; i++) {
|
||||
if (i % 20 === 0) {
|
||||
arr = [];
|
||||
idsSpan.push(arr);
|
||||
}
|
||||
arr.push(ids[i]);
|
||||
}
|
||||
|
||||
//分段加载记录
|
||||
for (const idArr of idsSpan) {
|
||||
const list = await this.repository.findBy({
|
||||
id: In(idArr),
|
||||
});
|
||||
|
||||
for (const entity of list) {
|
||||
const pipeline = JSON.parse(entity.content ?? '{}');
|
||||
this.registerTriggers(pipeline);
|
||||
}
|
||||
}
|
||||
logger.info('定时器数量:', this.cron.getList());
|
||||
}
|
||||
|
||||
registerTriggers(pipeline?: Pipeline) {
|
||||
if (pipeline?.triggers == null) {
|
||||
return;
|
||||
}
|
||||
for (const trigger of pipeline.triggers) {
|
||||
this.registerCron(pipeline.id, trigger);
|
||||
}
|
||||
}
|
||||
|
||||
async trigger(id) {
|
||||
this.cron.register({
|
||||
name: `pipeline.${id}.trigger.once`,
|
||||
cron: null,
|
||||
job: async () => {
|
||||
await this.run(id, null);
|
||||
},
|
||||
});
|
||||
logger.info('定时器数量:', this.cron.getList());
|
||||
}
|
||||
|
||||
registerCron(pipelineId, trigger) {
|
||||
let cron = trigger.props?.cron;
|
||||
if (cron == null) {
|
||||
return;
|
||||
}
|
||||
if(cron.startsWith("*")){
|
||||
cron = "0"+ cron.substring(1,cron.length)
|
||||
return
|
||||
}
|
||||
this.cron.register({
|
||||
name: this.buildCronKey(pipelineId, trigger.id),
|
||||
cron: cron,
|
||||
job: async () => {
|
||||
logger.info('定时任务触发:', pipelineId, trigger.id);
|
||||
await this.run(pipelineId, trigger.id);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async run(id, triggerId) {
|
||||
const entity: PipelineEntity = await this.info(id);
|
||||
const pipeline = JSON.parse(entity.content);
|
||||
|
||||
if (!pipeline.stages || pipeline.stages.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const triggerType = this.getTriggerType(triggerId, pipeline);
|
||||
if (triggerType == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const onChanged = async (history: RunHistory) => {
|
||||
//保存执行历史
|
||||
await this.saveHistory(history);
|
||||
};
|
||||
|
||||
const userId = entity.userId;
|
||||
const historyId = await this.historyService.start(entity);
|
||||
|
||||
const executor = new Executor({
|
||||
userId,
|
||||
pipeline,
|
||||
onChanged,
|
||||
accessService: this.accessService,
|
||||
storage: new DbStorage(userId, this.storageService),
|
||||
});
|
||||
|
||||
await executor.run(historyId, triggerType);
|
||||
}
|
||||
|
||||
private getTriggerType(triggerId, pipeline) {
|
||||
let triggerType = 'user';
|
||||
if (triggerId != null) {
|
||||
//如果不是手动触发
|
||||
//查找trigger
|
||||
const found = this.findTrigger(pipeline, triggerId);
|
||||
if (!found) {
|
||||
//如果没有找到triggerId,说明被用户删掉了,这里再删除一次
|
||||
this.cron.remove(this.buildCronKey(pipeline.id, triggerId));
|
||||
triggerType = null;
|
||||
} else {
|
||||
logger.info('timer trigger:' + found.id, found.title, found.cron);
|
||||
triggerType = 'timer';
|
||||
}
|
||||
}
|
||||
return triggerType;
|
||||
}
|
||||
|
||||
private buildCronKey(pipelineId, triggerId) {
|
||||
return `pipeline.${pipelineId}.trigger.${triggerId}`;
|
||||
}
|
||||
|
||||
private findTrigger(pipeline, triggerId) {
|
||||
for (const trigger of pipeline.triggers) {
|
||||
if (trigger.id === triggerId) {
|
||||
return trigger;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
private async saveHistory(history: RunHistory) {
|
||||
//修改pipeline状态
|
||||
const pipelineEntity = new PipelineEntity();
|
||||
pipelineEntity.id = parseInt(history.pipeline.id);
|
||||
pipelineEntity.status = history.pipeline.status.status;
|
||||
pipelineEntity.lastHistoryTime = history.pipeline.status.startTime;
|
||||
await this.update(pipelineEntity);
|
||||
|
||||
const entity: HistoryEntity = new HistoryEntity();
|
||||
entity.id = parseInt(history.id);
|
||||
entity.userId = history.pipeline.userId;
|
||||
entity.pipeline = JSON.stringify(history.pipeline);
|
||||
await this.historyService.save(entity);
|
||||
|
||||
const logEntity: HistoryLogEntity = new HistoryLogEntity();
|
||||
logEntity.id = entity.id;
|
||||
logEntity.userId = entity.userId;
|
||||
logEntity.pipelineId = entity.pipelineId;
|
||||
logEntity.historyId = entity.id;
|
||||
logEntity.logs = JSON.stringify(history.logs);
|
||||
await this.historyLogService.addOrUpdate(logEntity);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Provide, Scope, ScopeEnum } from "@midwayjs/decorator";
|
||||
import { pluginRegistry } from '@certd/pipeline';
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Singleton)
|
||||
export class PluginService {
|
||||
getList() {
|
||||
const collection = pluginRegistry.storage;
|
||||
const list = [];
|
||||
for (const key in collection) {
|
||||
const Plugin = collection[key];
|
||||
list.push({ ...Plugin.define, key });
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { Provide, Scope, ScopeEnum } from "@midwayjs/decorator";
|
||||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { BaseService } from '../../../basic/base-service';
|
||||
import { StorageEntity } from '../entity/storage';
|
||||
|
||||
/**
|
||||
*/
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Singleton)
|
||||
export class StorageService extends BaseService<StorageEntity> {
|
||||
@InjectEntityModel(StorageEntity)
|
||||
repository: Repository<StorageEntity>;
|
||||
|
||||
getRepository() {
|
||||
return this.repository;
|
||||
}
|
||||
|
||||
async get(where: {
|
||||
scope: any;
|
||||
namespace: any;
|
||||
userId: number;
|
||||
key: string;
|
||||
}) {
|
||||
if (where.userId == null) {
|
||||
throw new Error('userId 不能为空');
|
||||
}
|
||||
return await this.repository.findOne({
|
||||
where,
|
||||
});
|
||||
}
|
||||
|
||||
async set(entity: {
|
||||
id?: any;
|
||||
scope: any;
|
||||
namespace: any;
|
||||
userId: number;
|
||||
value: string;
|
||||
key: string;
|
||||
}) {
|
||||
entity.id = null;
|
||||
const query = { ...entity };
|
||||
delete query.value;
|
||||
const ret = await this.get(query);
|
||||
if (ret != null) {
|
||||
entity.id = ret.id;
|
||||
if (ret.userId !== entity.userId) {
|
||||
throw new Error('您没有权限修改此数据');
|
||||
}
|
||||
await this.repository.save(entity);
|
||||
} else {
|
||||
await this.repository.insert(entity);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Config, Configuration, Logger } from '@midwayjs/decorator';
|
||||
import { ILogger } from '@midwayjs/logger';
|
||||
import { IMidwayContainer } from '@midwayjs/core';
|
||||
import { Cron } from './cron';
|
||||
|
||||
// ... (see below) ...
|
||||
@Configuration({
|
||||
namespace: 'cron',
|
||||
//importConfigs: [join(__dirname, './config')],
|
||||
})
|
||||
export class CronConfiguration {
|
||||
@Config()
|
||||
config;
|
||||
@Logger()
|
||||
logger: ILogger;
|
||||
|
||||
cron: Cron;
|
||||
async onReady(container: IMidwayContainer) {
|
||||
this.logger.info('cron start');
|
||||
this.cron = new Cron({
|
||||
logger: this.logger,
|
||||
...this.config,
|
||||
});
|
||||
container.registerObject('cron', this.cron);
|
||||
this.logger.info('cron started');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import cron from 'node-cron';
|
||||
export type CronTask = {
|
||||
/**
|
||||
* 为空则为单次执行
|
||||
*/
|
||||
cron: string;
|
||||
job: () => Promise<void>;
|
||||
name: string;
|
||||
};
|
||||
export class Cron {
|
||||
logger;
|
||||
constructor(opts) {
|
||||
this.logger = opts.logger;
|
||||
}
|
||||
|
||||
register(task: CronTask) {
|
||||
if (!task.cron) {
|
||||
this.logger.info(`[cron] register once : [${task.name}]`);
|
||||
task.job();
|
||||
return;
|
||||
}
|
||||
this.logger.info(`[cron] register cron : [${task.name}] ,${task.cron}`);
|
||||
cron.schedule(task.cron, task.job, {
|
||||
name: task.name,
|
||||
});
|
||||
}
|
||||
|
||||
remove(taskName: string) {
|
||||
this.logger.info(`[cron] remove : [${taskName}]`);
|
||||
const tasks = cron.getTasks() as Map<any, any>;
|
||||
tasks.delete(taskName);
|
||||
}
|
||||
|
||||
getList() {
|
||||
const tasks = cron.getTasks();
|
||||
return tasks.size;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// src/index.ts
|
||||
export { CronConfiguration as Configuration } from './configuration';
|
||||
// export * from './controller/user';
|
||||
// export * from './controller/api';
|
||||
// export * from './service/user';
|
||||
@@ -0,0 +1,6 @@
|
||||
// src/index.ts
|
||||
import '@certd/plugin-all';
|
||||
export { PipelineConfiguration as Configuration } from '@certd/pipeline';
|
||||
// export * from './controller/user';
|
||||
// export * from './controller/api';
|
||||
// export * from './service/user';
|
||||
@@ -0,0 +1,27 @@
|
||||
import { ILogger } from "@midwayjs/logger";
|
||||
import { ITaskPlugin,Autowire, IsTaskPlugin, TaskInput } from "@certd/pipeline";
|
||||
|
||||
@IsTaskPlugin({
|
||||
name: "EchoPlugin",
|
||||
title: "测试插件",
|
||||
desc: "test",
|
||||
})
|
||||
export class EchoPlugin implements ITaskPlugin {
|
||||
@TaskInput({
|
||||
title: "测试属性",
|
||||
component: {
|
||||
name: "text",
|
||||
},
|
||||
})
|
||||
test?: string;
|
||||
|
||||
@Autowire()
|
||||
// @ts-ignore
|
||||
logger: ILogger;
|
||||
|
||||
async onInit(){}
|
||||
|
||||
async execute(): Promise<void> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
const log4js = require('log4js');
|
||||
const level = process.env.NODE_ENV === 'development' ? 'debug' : 'info';
|
||||
const path = require('path');
|
||||
const filename = path.join('/logs/server.log');
|
||||
log4js.configure({
|
||||
appenders: {
|
||||
std: { type: 'stdout', level: 'debug' },
|
||||
file: { type: 'file', pattern: 'yyyy-MM-dd', daysToKeep: 3, filename },
|
||||
},
|
||||
categories: { default: { appenders: ['std'], level } },
|
||||
});
|
||||
export const logger = log4js.getLogger('fast');
|
||||
@@ -0,0 +1,43 @@
|
||||
const numbers = '0123456789';
|
||||
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
||||
const specials = '~!@#$%^*()_+-=[]{}|;:,./<>?';
|
||||
|
||||
/**
|
||||
* Generate random string
|
||||
* @param {Number} length
|
||||
* @param {Object} options
|
||||
*/
|
||||
function randomStr(length, options) {
|
||||
length || (length = 8);
|
||||
options || (options = {});
|
||||
|
||||
let chars = '';
|
||||
let result = '';
|
||||
|
||||
if (options === true) {
|
||||
chars = numbers + letters;
|
||||
} else if (typeof options === 'string') {
|
||||
chars = options;
|
||||
} else {
|
||||
if (options.numbers !== false) {
|
||||
chars += typeof options.numbers === 'string' ? options.numbers : numbers;
|
||||
}
|
||||
|
||||
if (options.letters !== false) {
|
||||
chars += typeof options.letters === 'string' ? options.letters : letters;
|
||||
}
|
||||
|
||||
if (options.specials) {
|
||||
chars +=
|
||||
typeof options.specials === 'string' ? options.specials : specials;
|
||||
}
|
||||
}
|
||||
|
||||
while (length > 0) {
|
||||
length--;
|
||||
result += chars[Math.floor(Math.random() * chars.length)];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export const RandomUtil = { randomStr };
|
||||
@@ -0,0 +1,25 @@
|
||||
import { createApp, close, createHttpRequest } from '@midwayjs/mock';
|
||||
import { Framework } from '@midwayjs/koa';
|
||||
import * as assert from 'assert';
|
||||
|
||||
describe('test/controller/home.test.ts', () => {
|
||||
|
||||
it('should POST /api/get_user', async () => {
|
||||
// create app
|
||||
const app = await createApp<Framework>();
|
||||
|
||||
// make request
|
||||
const result = await createHttpRequest(app).post('/api/get_user').query({ uid: 123 });
|
||||
|
||||
// use expect by jest
|
||||
expect(result.status).toBe(200);
|
||||
expect(result.body.message).toBe('OK');
|
||||
|
||||
// or use assert
|
||||
assert.deepStrictEqual(result.status, 200);
|
||||
assert.deepStrictEqual(result.body.data.uid, '123');
|
||||
|
||||
// close app
|
||||
await close(app);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,26 @@
|
||||
import { createApp, close, createHttpRequest } from '@midwayjs/mock';
|
||||
import { Framework } from '@midwayjs/koa';
|
||||
import * as assert from 'assert';
|
||||
|
||||
describe('test/controller/home.test.ts', () => {
|
||||
|
||||
it('should GET /', async () => {
|
||||
// create app
|
||||
const app = await createApp<Framework>();
|
||||
|
||||
// make request
|
||||
const result = await createHttpRequest(app).get('/');
|
||||
|
||||
// use expect by jest
|
||||
expect(result.status).toBe(200);
|
||||
expect(result.text).toBe('Hello Midwayjs!');
|
||||
|
||||
// or use assert
|
||||
assert.deepStrictEqual(result.status, 200);
|
||||
assert.deepStrictEqual(result.text, 'Hello Midwayjs!');
|
||||
|
||||
// close app
|
||||
await close(app);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"compileOnSave": true,
|
||||
"compilerOptions": {
|
||||
"target": "ES2018",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"inlineSourceMap":true,
|
||||
"noImplicitThis": true,
|
||||
"noUnusedLocals": true,
|
||||
"stripInternal": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"pretty": true,
|
||||
"declaration": true,
|
||||
"typeRoots": [ "./typings", "./node_modules/@types"],
|
||||
"outDir": "dist"
|
||||
},
|
||||
"exclude": [
|
||||
"dist",
|
||||
"node_modules",
|
||||
"test"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user