feat: 초기 프로젝트 설정 및 룰.md 파일 추가

This commit is contained in:
2025-07-28 09:53:31 +09:00
commit 09a4d38512
8165 changed files with 1021855 additions and 0 deletions

4
api.hyungi.net/node_modules/pm2/.gitattributes generated vendored Normal file
View File

@@ -0,0 +1,4 @@
* text=auto
*.sh eol=lf
bin/** eol=lf
test/fixtures/** eol=lf

14
api.hyungi.net/node_modules/pm2/.mocharc.js generated vendored Normal file
View File

@@ -0,0 +1,14 @@
module.exports = {
'allow-uncaught' : false,
'async-only': false,
bail: true,
color: true,
delay: false,
diff: true,
exit: true,
timeout: 10000,
'trace-warnings': true,
ui: 'bdd',
retries: 2
}

2355
api.hyungi.net/node_modules/pm2/CHANGELOG.md generated vendored Normal file

File diff suppressed because it is too large Load Diff

124
api.hyungi.net/node_modules/pm2/CONTRIBUTING.md generated vendored Normal file
View File

@@ -0,0 +1,124 @@
# Contributing
## Cloning PM2 development
```bash
$ git clone https://github.com/Unitech/pm2.git
$ cd pm2
$ git checkout development
$ npm install
```
I recommend having a pm2 alias pointing to the development version to make it easier to use pm2 development:
```
$ cd pm2/
$ echo "alias pm2='`pwd`/bin/pm2'" >> ~/.bashrc
```
You are now able to use pm2 in dev mode:
```
$ pm2 update
$ pm2 ls
```
## Project structure
```
.
├── bin // pm2, pmd, pm2-dev, pm2-docker are there
├── examples // examples files
├── lib // source files
├── pres // presentation files
├── test // test files
└── types // TypeScript definition files
```
## Modifying the Daemon
When you modify the Daemon (lib/Daemon.js, lib/God.js, lib/God/*, lib/Watcher.js), you must restart the pm2 Daemon by doing:
```
$ pm2 update
```
## Commit rules
### Commit message
A good commit message should describe what changed and why.
It should :
* contain a short description of the change (preferably 50 characters or less)
* be entirely in lowercase with the exception of proper nouns, acronyms, and the words that refer to code, like function/variable names
* be prefixed with one of the following word
* fix : bug fix
* hotfix : urgent bug fix
* feat : new or updated feature
* docs : documentation updates
* BREAKING : if commit is a breaking change
* refactor : code refactoring (no functional change)
* perf : performance improvement
* style : UX and display updates
* test : tests and CI updates
* chore : updates on build, tools, configuration ...
* Merge branch : when merging branch
* Merge pull request : when merging PR
## Tests
There are two tests type. Programmatic and Behavioral.
The main test command is `npm test`
### Programmatic
Programmatic tests are runned by doing
```
$ bash test/pm2_programmatic_tests.sh
```
This test files are located in test/programmatic/*
### Behavioral
Behavioral tests are runned by doing:
```
$ bash test/e2e.sh
```
This test files are located in test/e2e/*
## File of interest
- `$HOME/.pm2` contain all PM2 related files
- `$HOME/.pm2/logs` contain all applications logs
- `$HOME/.pm2/pids` contain all applications pids
- `$HOME/.pm2/pm2.log` PM2 logs
- `$HOME/.pm2/pm2.pid` PM2 pid
- `$HOME/.pm2/rpc.sock` Socket file for remote commands
- `$HOME/.pm2/pub.sock` Socket file for publishable events
## Generate changelog
### requirements
```
npm install git-changelog -g
```
### usage
Edit .changelogrc
Change "version_name" to the next version to release (example 1.1.2).
Change "tag" to the latest existing tag (example 1.1.1).
Run the following command into pm2 directory
```
git-changelog
```
It will generate currentTagChangelog.md file.
Just copy/paste the result into changelog.md

665
api.hyungi.net/node_modules/pm2/GNU-AGPL-3.0.txt generated vendored Normal file
View File

@@ -0,0 +1,665 @@
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
PM2 Process manager for Node.JS
Copyright (C) 2013-2016 Strzelewicz Alexandre
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<http://www.gnu.org/licenses/>.
--- ALEXANDRE STRZELEWICZ

1
api.hyungi.net/node_modules/pm2/LICENSE generated vendored Normal file
View File

@@ -0,0 +1 @@
GNU-AGPL-3.0.txt

252
api.hyungi.net/node_modules/pm2/README.md generated vendored Normal file
View File

@@ -0,0 +1,252 @@
<div align="center">
<br/>
<picture>
<source
srcset="https://raw.githubusercontent.com/Unitech/pm2/master/pres/pm2-v4.png"
width=710px
media="(prefers-color-scheme: light)"
/>
<source
srcset="https://raw.githubusercontent.com/Unitech/pm2/development/pres/pm2-v4-dark-mode.png"
width=710px
media="(prefers-color-scheme: dark), (prefers-color-scheme: no-preference)"
/>
<img src="https://raw.githubusercontent.com/Unitech/pm2/master/pres/pm2-v4.png" />
</picture>
<br/>
<br/>
<b>P</b>(rocess) <b>M</b>(anager) <b>2</b><br/>
<i>Runtime Edition</i>
<br/><br/>
<a title="PM2 Downloads" href="https://npm-stat.com/charts.html?package=pm2&from=2018-01-01&to=2023-08-01">
<img src="https://img.shields.io/npm/dm/pm2" alt="Downloads per Month"/>
</a>
<a title="PM2 Downloads" href="https://npm-stat.com/charts.html?package=pm2&from=2018-01-01&to=2023-08-01">
<img src="https://img.shields.io/npm/dy/pm2" alt="Downloads per Year"/>
</a>
<a href="https://badge.fury.io/js/pm2" title="NPM Version Badge">
<img src="https://badge.fury.io/js/pm2.svg" alt="npm version">
</a>
<br/>
<br/>
<br/>
</div>
PM2 is a production process manager for Node.js applications with a built-in load balancer. It allows you to keep applications alive forever, to reload them without downtime and to facilitate common system admin tasks.
Starting an application in production mode is as easy as:
```bash
$ pm2 start app.js
```
PM2 is constantly assailed by [more than 1800 tests](https://github.com/Unitech/pm2/actions/workflows/node.js.yml).
Official website: [https://pm2.keymetrics.io/](https://pm2.keymetrics.io/)
Works on Linux (stable) & macOS (stable) & Windows (stable). All Node.js versions are supported starting Node.js 12.X.
### Installing PM2
With NPM:
```bash
$ npm install pm2 -g
```
You can install Node.js easily with [NVM](https://github.com/nvm-sh/nvm#installing-and-updating) or [FNM](https://github.com/Schniz/fnm).
### Start an application
You can start any application (Node.js, Python, Ruby, binaries in $PATH...) like that:
```bash
$ pm2 start app.js
```
Your app is now daemonized, monitored and kept alive forever.
### Managing Applications
Once applications are started you can manage them easily:
![Process listing](https://github.com/Unitech/pm2/raw/master/pres/pm2-ls-v2.png)
To list all running applications:
```bash
$ pm2 list
```
Managing apps is straightforward:
```bash
$ pm2 stop <app_name|namespace|id|'all'|json_conf>
$ pm2 restart <app_name|namespace|id|'all'|json_conf>
$ pm2 delete <app_name|namespace|id|'all'|json_conf>
```
To have more details on a specific application:
```bash
$ pm2 describe <id|app_name>
```
To monitor logs, custom metrics, application information:
```bash
$ pm2 monit
```
[More about Process Management](https://pm2.keymetrics.io/docs/usage/process-management/)
### Cluster Mode: Node.js Load Balancing & Zero Downtime Reload
The Cluster mode is a special mode when starting a Node.js application, it starts multiple processes and load-balance HTTP/TCP/UDP queries between them. This increase overall performance (by a factor of x10 on 16 cores machines) and reliability (faster socket re-balancing in case of unhandled errors).
![Framework supported](https://raw.githubusercontent.com/Unitech/PM2/master/pres/cluster.png)
Starting a Node.js application in cluster mode that will leverage all CPUs available:
```bash
$ pm2 start api.js -i <processes>
```
`<processes>` can be `'max'`, `-1` (all cpu minus 1) or a specified number of instances to start.
**Zero Downtime Reload**
Hot Reload allows to update an application without any downtime:
```bash
$ pm2 reload all
```
[More informations about how PM2 make clustering easy](https://pm2.keymetrics.io/docs/usage/cluster-mode/)
### Container Support
With the drop-in replacement command for `node`, called `pm2-runtime`, run your Node.js application in a hardened production environment.
Using it is seamless:
```
RUN npm install pm2 -g
CMD [ "pm2-runtime", "npm", "--", "start" ]
```
[Read More about the dedicated integration](https://pm2.keymetrics.io/docs/usage/docker-pm2-nodejs/)
### Host monitoring speedbar
PM2 allows to monitor your host/server vitals with a monitoring speedbar.
To enable host monitoring:
```bash
$ pm2 set pm2:sysmonit true
$ pm2 update
```
![Framework supported](https://raw.githubusercontent.com/Unitech/PM2/master/pres/vitals.png)
### Terminal Based Monitoring
![Monit](https://github.com/Unitech/pm2/raw/master/pres/pm2-monit.png)
Monitor all processes launched straight from the command line:
```bash
$ pm2 monit
```
### Log Management
To consult logs just type the command:
```bash
$ pm2 logs
```
Standard, Raw, JSON and formated output are available.
Examples:
```bash
$ pm2 logs APP-NAME # Display APP-NAME logs
$ pm2 logs --json # JSON output
$ pm2 logs --format # Formated output
$ pm2 flush # Flush all logs
$ pm2 reloadLogs # Reload all logs
```
To enable log rotation install the following module
```bash
$ pm2 install pm2-logrotate
```
[More about log management](https://pm2.keymetrics.io/docs/usage/log-management/)
### Startup Scripts Generation
PM2 can generate and configure a Startup Script to keep PM2 and your processes alive at every server restart.
Init Systems Supported: **systemd**, **upstart**, **launchd**, **rc.d**
```bash
# Generate Startup Script
$ pm2 startup
# Freeze your process list across server restart
$ pm2 save
# Remove Startup Script
$ pm2 unstartup
```
[More about Startup Scripts Generation](https://pm2.keymetrics.io/docs/usage/startup/)
### Updating PM2
```bash
# Install latest PM2 version
$ npm install pm2@latest -g
# Save process list, exit old PM2 & restore all processes
$ pm2 update
```
*PM2 updates are seamless*
## PM2+ Monitoring
If you manage your apps with PM2, PM2+ makes it easy to monitor and manage apps across servers.
![https://app.pm2.io/](https://pm2.io/img/app-overview.png)
Feel free to try it:
[Discover the monitoring dashboard for PM2](https://app.pm2.io/)
Thanks in advance and we hope that you like PM2!
## CHANGELOG
[CHANGELOG](https://github.com/Unitech/PM2/blob/master/CHANGELOG.md)
## Contributors
[Contributors](http://pm2.keymetrics.io/hall-of-fame/)
## License
PM2 is made available under the terms of the GNU Affero General Public License 3.0 (AGPL 3.0).
For other licenses [contact us](mailto:contact@keymetrics.io).

3
api.hyungi.net/node_modules/pm2/bin/pm2 generated vendored Normal file
View File

@@ -0,0 +1,3 @@
#!/usr/bin/env node
require('../lib/binaries/CLI.js');

3
api.hyungi.net/node_modules/pm2/bin/pm2-dev generated vendored Normal file
View File

@@ -0,0 +1,3 @@
#!/usr/bin/env node
require('../lib/binaries/DevCLI.js');

3
api.hyungi.net/node_modules/pm2/bin/pm2-docker generated vendored Normal file
View File

@@ -0,0 +1,3 @@
#!/usr/bin/env node
require('../lib/binaries/Runtime4Docker.js');

3
api.hyungi.net/node_modules/pm2/bin/pm2-runtime generated vendored Normal file
View File

@@ -0,0 +1,3 @@
#!/usr/bin/env node
require('../lib/binaries/Runtime4Docker.js');

112
api.hyungi.net/node_modules/pm2/constants.js generated vendored Normal file
View File

@@ -0,0 +1,112 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
var debug = require('debug')('pm2:conf');
var p = require('path');
var util = require('util');
var chalk = require('chalk');
/**
* Get PM2 path structure
*/
var path_structure = require('./paths.js')(process.env.OVER_HOME);
/**
* Constants variables used by PM2
*/
var csts = {
PREFIX_MSG : chalk.green('[PM2] '),
PREFIX_MSG_INFO : chalk.cyan('[PM2][INFO] '),
PREFIX_MSG_ERR : chalk.red('[PM2][ERROR] '),
PREFIX_MSG_MOD : chalk.bold.green('[PM2][Module] '),
PREFIX_MSG_MOD_ERR : chalk.red('[PM2][Module][ERROR] '),
PREFIX_MSG_WARNING : chalk.yellow('[PM2][WARN] '),
PREFIX_MSG_SUCCESS : chalk.cyan('[PM2] '),
PM2_IO_MSG : chalk.cyan('[PM2 I/O]'),
PM2_IO_MSG_ERR : chalk.red('[PM2 I/O]'),
TEMPLATE_FOLDER : p.join(__dirname, 'lib/templates'),
APP_CONF_DEFAULT_FILE : 'ecosystem.config.js',
APP_CONF_TPL : 'ecosystem.tpl',
APP_CONF_TPL_SIMPLE : 'ecosystem-simple.tpl',
SAMPLE_CONF_FILE : 'sample-conf.js',
LOGROTATE_SCRIPT : 'logrotate.d/pm2',
DOCKERFILE_NODEJS : 'Dockerfiles/Dockerfile-nodejs.tpl',
DOCKERFILE_JAVA : 'Dockerfiles/Dockerfile-java.tpl',
DOCKERFILE_RUBY : 'Dockerfiles/Dockerfile-ruby.tpl',
SUCCESS_EXIT : 0,
ERROR_EXIT : 1,
CODE_UNCAUGHTEXCEPTION : 1,
IS_WINDOWS : (process.platform === 'win32' || process.platform === 'win64' || /^(msys|cygwin)$/.test(process.env.OSTYPE)),
ONLINE_STATUS : 'online',
STOPPED_STATUS : 'stopped',
STOPPING_STATUS : 'stopping',
WAITING_RESTART : 'waiting restart',
LAUNCHING_STATUS : 'launching',
ERRORED_STATUS : 'errored',
ONE_LAUNCH_STATUS : 'one-launch-status',
CLUSTER_MODE_ID : 'cluster_mode',
FORK_MODE_ID : 'fork_mode',
LOW_MEMORY_ENVIRONMENT : process.env.PM2_OPTIMIZE_MEMORY || false,
MACHINE_NAME : process.env.INSTANCE_NAME || process.env.MACHINE_NAME || process.env.PM2_MACHINE_NAME,
SECRET_KEY : process.env.KEYMETRICS_SECRET || process.env.PM2_SECRET_KEY || process.env.SECRET_KEY,
PUBLIC_KEY : process.env.KEYMETRICS_PUBLIC || process.env.PM2_PUBLIC_KEY || process.env.PUBLIC_KEY,
KEYMETRICS_ROOT_URL : process.env.KEYMETRICS_NODE || process.env.PM2_APM_ADDRESS || process.env.ROOT_URL || process.env.INFO_NODE || 'root.keymetrics.io',
PM2_BANNER : '../lib/motd',
PM2_UPDATE : '../lib/API/pm2-plus/pres/motd.update',
DEFAULT_MODULE_JSON : 'package.json',
MODULE_BASEFOLDER: 'module',
MODULE_CONF_PREFIX: 'module-db-v2',
MODULE_CONF_PREFIX_TAR: 'tar-modules',
EXP_BACKOFF_RESET_TIMER : parseInt(process.env.EXP_BACKOFF_RESET_TIMER) || 30000,
REMOTE_PORT_TCP : isNaN(parseInt(process.env.KEYMETRICS_PUSH_PORT)) ? 80 : parseInt(process.env.KEYMETRICS_PUSH_PORT),
REMOTE_PORT : 41624,
REMOTE_HOST : 's1.keymetrics.io',
SEND_INTERVAL : 1000,
RELOAD_LOCK_TIMEOUT : parseInt(process.env.PM2_RELOAD_LOCK_TIMEOUT) || 30000,
GRACEFUL_TIMEOUT : parseInt(process.env.PM2_GRACEFUL_TIMEOUT) || 8000,
GRACEFUL_LISTEN_TIMEOUT : parseInt(process.env.PM2_GRACEFUL_LISTEN_TIMEOUT) || 3000,
LOGS_BUFFER_SIZE : 8,
CONTEXT_ON_ERROR : 2,
AGGREGATION_DURATION : process.env.PM2_DEBUG || process.env.NODE_ENV === 'local_test' || process.env.NODE_ENV === 'development' ? 3000 : 5 * 60000,
TRACE_FLUSH_INTERVAL : process.env.PM2_DEBUG || process.env.NODE_ENV === 'local_test' ? 1000 : 60000,
// Concurrent actions when doing start/restart/reload
CONCURRENT_ACTIONS : (function() {
var concurrent_actions = parseInt(process.env.PM2_CONCURRENT_ACTIONS) || 2;
debug('Using %d parallelism (CONCURRENT_ACTIONS)', concurrent_actions);
return concurrent_actions;
})(),
DEBUG : process.env.PM2_DEBUG || false,
WEB_IPADDR : process.env.PM2_API_IPADDR || '0.0.0.0',
WEB_PORT : parseInt(process.env.PM2_API_PORT) || 9615,
WEB_STRIP_ENV_VARS : process.env.PM2_WEB_STRIP_ENV_VARS || false,
MODIFY_REQUIRE : process.env.PM2_MODIFY_REQUIRE || false,
WORKER_INTERVAL : process.env.PM2_WORKER_INTERVAL || 30000,
KILL_TIMEOUT : process.env.PM2_KILL_TIMEOUT || 1600,
KILL_SIGNAL : process.env.PM2_KILL_SIGNAL || 'SIGINT',
KILL_USE_MESSAGE : process.env.PM2_KILL_USE_MESSAGE || false,
PM2_PROGRAMMATIC : typeof(process.env.pm_id) !== 'undefined' || process.env.PM2_PROGRAMMATIC,
PM2_LOG_DATE_FORMAT : process.env.PM2_LOG_DATE_FORMAT !== undefined ? process.env.PM2_LOG_DATE_FORMAT : 'YYYY-MM-DDTHH:mm:ss'
};
module.exports = Object.assign(csts, path_structure);

8
api.hyungi.net/node_modules/pm2/e2e_time generated vendored Normal file
View File

@@ -0,0 +1,8 @@
[V] ./test/e2e/cli/reload.sh succeeded and took 64 seconds
[V] ./test/e2e/cli/start-app.sh succeeded and took 10 seconds
[V] ./test/e2e/cli/operate-regex.sh succeeded and took 5 seconds
[V] ./test/e2e/cli/app-configuration.sh succeeded and took 9 seconds
[V] ./test/e2e/cli/binary.sh succeeded and took 4 seconds
[V] ./test/e2e/cli/startOrX.sh succeeded and took 3 seconds
[V] ./test/e2e/cli/reset.sh succeeded and took 14 seconds
[V] ./test/e2e/cli/env-refresh.sh succeeded and took 11 seconds

12
api.hyungi.net/node_modules/pm2/index.js generated vendored Normal file
View File

@@ -0,0 +1,12 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
process.env.PM2_PROGRAMMATIC = 'true';
var API = require('./lib/API.js');
module.exports = new API;
module.exports.custom = API;

1929
api.hyungi.net/node_modules/pm2/lib/API.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,212 @@
var Common = require('../Common.js');
var cst = require('../../constants.js');
var UX = require('./UX');
var chalk = require('chalk');
var Configuration = require('../Configuration.js');
module.exports = function(CLI) {
CLI.prototype.get = function(key, cb) {
var that = this;
if (!key || key == 'all') {
displayConf(function(err, data) {
if (err)
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
return cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT);
});
return false;
}
Configuration.get(key, function(err, data) {
if (err) {
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
}
// pm2 conf module-name
if (key.indexOf(':') === -1 && key.indexOf('.') === -1) {
displayConf(key, function() {
console.log('Modules configuration. Copy/Paste line to edit values.')
return cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT)
});
return false;
}
// pm2 conf module-name:key
var module_name, key_name;
if (key.indexOf(':') > -1) {
module_name = key.split(':')[0];
key_name = key.split(':')[1];
} else if (key.indexOf('.') > -1) {
module_name = key.split('.')[0];
key_name = key.split('.')[1];
}
Common.printOut('Value for module ' + chalk.blue(module_name), 'key ' + chalk.blue(key_name) + ': ' + chalk.bold.green(data));
return cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT);
});
};
CLI.prototype.set = function(key, value, cb) {
var that = this;
if (!key) {
interactiveConfigEdit(function(err) {
if (err)
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
return cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT);
});
return false;
}
/**
* Set value
*/
Configuration.set(key, value, function(err) {
if (err)
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
var values = [];
if (key.indexOf('.') > -1)
values = key.split('.');
if (key.indexOf(':') > -1)
values = key.split(':');
if (values && values.length > 1) {
// The first element is the app name (module_conf.json)
var app_name = values[0];
process.env.PM2_PROGRAMMATIC = 'true';
that.restart(app_name, {
updateEnv : true
}, function(err, data) {
process.env.PM2_PROGRAMMATIC = 'false';
if (!err)
Common.printOut(cst.PREFIX_MSG + 'Module %s restarted', app_name);
Common.log('Setting changed')
displayConf(app_name, function() {
return cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT);
});
});
return false;
}
displayConf(null, function() {
return cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT);
});
});
};
CLI.prototype.multiset = function(serial, cb) {
var that = this;
Configuration.multiset(serial, function(err, data) {
if (err)
return cb ? cb({success:false, err:err}) : that.exitCli(cst.ERROR_EXIT);
var values = [];
var key = serial.match(/(?:[^ "]+|"[^"]*")+/g)[0];
if (key.indexOf('.') > -1)
values = key.split('.');
if (key.indexOf(':') > -1)
values = key.split(':');
if (values && values.length > 1) {
// The first element is the app name (module_conf.json)
var app_name = values[0];
process.env.PM2_PROGRAMMATIC = 'true';
that.restart(app_name, {
updateEnv : true
}, function(err, data) {
process.env.PM2_PROGRAMMATIC = 'false';
if (!err)
Common.printOut(cst.PREFIX_MSG + 'Module %s restarted', app_name);
displayConf(app_name, function() {
return cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT)
});
});
return false;
}
displayConf(app_name, function() {
return cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT)
});
});
};
CLI.prototype.unset = function(key, cb) {
var that = this;
Configuration.unset(key, function(err) {
if (err) {
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
}
displayConf(function() { cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT) });
});
};
CLI.prototype.conf = function(key, value, cb) {
var that = this;
if (typeof(value) === 'function') {
cb = value;
value = null;
}
// If key + value = set
if (key && value) {
that.set(key, value, function(err) {
if (err)
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
return cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT);
});
}
// If only key = get
else if (key) {
that.get(key, function(err, data) {
if (err)
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
return cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT);
});
}
else {
interactiveConfigEdit(function(err) {
if (err)
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
return cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT);
});
}
};
};
function interactiveConfigEdit(cb) {
UX.helpers.openEditor(cst.PM2_MODULE_CONF_FILE, function(err, data) {
Common.printOut(chalk.bold('Module configuration (%s) edited.'), cst.PM2_MODULE_CONF_FILE);
Common.printOut(chalk.bold('To take changes into account, please restart module related.'), cst.PM2_MODULE_CONF_FILE);
if (err)
return cb(Common.retErr(err));
return cb(null, {success:true});
});
}
/**
* Configuration
*/
function displayConf(target_app, cb) {
if (typeof(target_app) == 'function') {
cb = target_app;
target_app = null;
}
Configuration.getAll(function(err, data) {
UX.helpers.dispKeys(data, target_app);
return cb();
});
}

View File

@@ -0,0 +1,335 @@
var spawn = require('child_process').spawn;
var exec = require('child_process').exec;
var chalk = require('chalk');
var util = require('util');
var fmt = require('../tools/fmt.js');
var fs = require('fs');
var path = require('path');
var cst = require('../../constants.js');
var Promise = require('../tools/promise.min.js');
function pspawn(cmd) {
return new Promise(function(resolve, reject) {
var p_cmd = cmd.split(' ');
var install_instance = spawn(p_cmd[0], p_cmd.splice(1, cmd.length), {
stdio : 'inherit',
env : process.env,
shell : true
});
install_instance.on('close', function(code) {
if (code != 0) {
console.log(chalk.bold.red('Command failed'));
return reject(new Error('Bad cmd return'));
}
return resolve();
});
install_instance.on('error', function (err) {
return reject(err);
});
});
}
function checkDockerSetup() {
return new Promise(function(resolve, reject) {
exec("docker version -f '{{.Client.Version}}'", function(err, stdout, stderr) {
if (err) {
console.error(chalk.red.bold('[Docker access] Error while trying to use docker command'));
if (err.message && err.message.indexOf('Cannot connect to the Docker') > -1) {
console.log();
console.log(chalk.blue.bold('[Solution] Setup Docker to be able to be used without sudo rights:'));
console.log(chalk.bold('$ sudo groupadd docker'));
console.log(chalk.bold('$ sudo usermod -aG docker $USER'));
console.log(chalk.bold('Then LOGOUT and LOGIN your Linux session'));
console.log('Read more: http://bit.ly/29JGdCE');
}
return reject(err);
}
return resolve();
});
});
}
/**
* Switch Dockerfile mode
* check test/programmatic/containerizer.mocha.js
*/
function parseAndSwitch(file_content, main_file, opts) {
var lines = file_content.split('\n');
var mode = opts.mode;
lines[0] = 'FROM keymetrics/pm2:' + opts.node_version;
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
if (['## DISTRIBUTION MODE', '## DEVELOPMENT MODE'].indexOf(line) > -1 ||
i == lines.length - 1) {
lines.splice(i, lines.length);
lines[i] = '## ' + mode.toUpperCase() + ' MODE';
lines[i + 1] = 'ENV NODE_ENV=' + (mode == 'distribution' ? 'production' : mode);
if (mode == 'distribution') {
lines[i + 2] = 'COPY . /var/app';
lines[i + 3] = 'CMD ["pm2-docker", "' + main_file + '", "--env", "production"]';
}
if (mode == 'development') {
lines[i + 2] = 'CMD ["pm2-dev", "' + main_file + '", "--env", "development"]';
}
break;
}
};
lines = lines.join('\n');
return lines;
};
/**
* Replace ENV, COPY and CMD depending on the mode
* @param {String} docker_filepath Dockerfile absolute path
* @param {String} main_file Main file to start in container
* @param {String} mode Mode to switch the Dockerfile
*/
function switchDockerFile(docker_filepath, main_file, opts) {
return new Promise(function(resolve, reject) {
var data = fs.readFileSync(docker_filepath, 'utf8').toString();
if (['distribution', 'development'].indexOf(opts.mode) == -1)
return reject(new Error('Unknown mode'));
var lines = parseAndSwitch(data, main_file, opts)
fs.writeFile(docker_filepath, lines, function(err) {
if (err) return reject(err);
resolve({
Dockerfile_path : docker_filepath,
Dockerfile : lines,
CMD : ''
});
})
});
}
/**
* Generate sample Dockerfile (lib/templates/Dockerfiles)
* @param {String} docker_filepath Dockerfile absolute path
* @param {String} main_file Main file to start in container
* @param {String} mode Mode to switch the Dockerfile
*/
function generateDockerfile(docker_filepath, main_file, opts) {
return new Promise(function(resolve, reject) {
var tpl_file = path.join(cst.TEMPLATE_FOLDER, cst.DOCKERFILE_NODEJS);
var template = fs.readFileSync(tpl_file, {encoding: 'utf8'});
var CMD;
template = parseAndSwitch(template, main_file, opts);
fs.writeFile(docker_filepath, template, function(err) {
if (err) return reject(err);
resolve({
Dockerfile_path : docker_filepath,
Dockerfile : template,
CMD : CMD
});
});
});
}
function handleExit(CLI, opts, mode) {
process.on('SIGINT', function() {
CLI.disconnect();
if (mode != 'distribution')
return false;
exec('docker ps -lq', function(err, stdout, stderr) {
if (err) {
console.error(err);
}
require('vizion').analyze({folder : process.cwd()}, function recur_path(err, meta){
if (!err && meta.revision) {
var commit_id = util.format('#%s(%s) %s',
meta.branch,
meta.revision.slice(0, 5),
meta.comment);
console.log(chalk.bold.magenta('$ docker commit -m "%s" %s %s'),
commit_id,
stdout.replace('\n', ''),
opts.imageName);
}
else
console.log(chalk.bold.magenta('$ docker commit %s %s'), stdout.replace('\n', ''), opts.imageName);
console.log(chalk.bold.magenta('$ docker push %s'), opts.imageName);
});
});
});
}
module.exports = function(CLI) {
CLI.prototype.generateDockerfile = function(script, opts) {
var docker_filepath = path.join(process.cwd(), 'Dockerfile');
var that = this;
fs.stat(docker_filepath, function(err, stat) {
if (err || opts.force == true) {
generateDockerfile(docker_filepath, script, {
mode : 'development'
})
.then(function() {
console.log(chalk.bold('New Dockerfile generated in current folder'));
console.log(chalk.bold('You can now run\n$ pm2 docker:dev <file|config>'));
return that.exitCli(cst.SUCCESS_EXIT);
});
return false;
}
console.log(chalk.red.bold('Dockerfile already exists in this folder, use --force if you want to replace it'));
that.exitCli(cst.ERROR_EXIT);
});
};
CLI.prototype.dockerMode = function(script, opts, mode) {
var promptly = require('promptly');
var self = this;
handleExit(self, opts, mode);
if (mode == 'distribution' && !opts.imageName) {
console.error(chalk.bold.red('--image-name [name] option is missing'));
return self.exitCli(cst.ERROR_EXIT);
}
var template;
var app_path, main_script;
var image_name;
var node_version = opts.nodeVersion ? opts.nodeVersion.split('.')[0] : 'latest';
image_name = opts.imageName || require('crypto').randomBytes(6).toString('hex');
if (script.indexOf('/') > -1) {
app_path = path.join(process.cwd(), path.dirname(script));
main_script = path.basename(script);
}
else {
app_path = process.cwd();
main_script = script;
}
checkDockerSetup()
.then(function() {
/////////////////////////
// Generate Dockerfile //
/////////////////////////
return new Promise(function(resolve, reject) {
var docker_filepath = path.join(process.cwd(), 'Dockerfile');
fs.stat(docker_filepath, function(err, stat) {
if (err) {
// Dockerfile does not exist, generate one
// console.log(chalk.blue.bold('Generating new Dockerfile'));
if (opts.force == true) {
return resolve(generateDockerfile(docker_filepath, main_script, {
node_version : node_version,
mode : mode
}));
}
if (opts.dockerdaemon)
return resolve(generateDockerfile(docker_filepath, main_script, {
node_version : node_version,
mode : mode
}));
promptly.prompt('No Dockerfile in current directory, ok to generate a new one? (y/n)', function(err, value) {
if (value == 'y')
return resolve(generateDockerfile(docker_filepath, main_script, {
node_version : node_version,
mode : mode
}));
else
return self.exitCli(cst.SUCCESS_EXIT);
});
return false;
}
return resolve(switchDockerFile(docker_filepath, main_script, {
node_version : node_version,
mode : mode
}));
});
});
})
.then(function(_template) {
template = _template;
return Promise.resolve();
})
.then(function() {
//////////////////
// Docker build //
//////////////////
var docker_build = util.format('docker build -t %s -f %s',
image_name,
template.Dockerfile_path);
if (opts.fresh == true)
docker_build += ' --no-cache';
docker_build += ' .';
console.log();
fmt.sep();
fmt.title('Building Boot System');
fmt.field('Type', chalk.cyan.bold('Docker'));
fmt.field('Mode', mode);
fmt.field('Image name', image_name);
fmt.field('Docker build command', docker_build);
fmt.field('Dockerfile path', template.Dockerfile_path);
fmt.sep();
return pspawn(docker_build);
})
.then(function() {
////////////////
// Docker run //
////////////////
var docker_run = 'docker run --net host';
if (opts.dockerdaemon == true)
docker_run += ' -d';
if (mode != 'distribution')
docker_run += util.format(' -v %s:/var/app -v /var/app/node_modules', app_path);
docker_run += ' ' + image_name;
var dockerfile_parsed = template.Dockerfile.split('\n');
var base_image = dockerfile_parsed[0];
var run_cmd = dockerfile_parsed[dockerfile_parsed.length - 1];
console.log();
fmt.sep();
fmt.title('Booting');
fmt.field('Type', chalk.cyan.bold('Docker'));
fmt.field('Mode', mode);
fmt.field('Base Image', base_image);
fmt.field('Image Name', image_name);
fmt.field('Docker Command', docker_run);
fmt.field('RUN Command', run_cmd);
fmt.field('CWD', app_path);
fmt.sep();
return pspawn(docker_run);
})
.then(function() {
console.log(chalk.blue.bold('>>> Leaving Docker instance uuid=%s'), image_name);
self.disconnect();
return Promise.resolve();
})
.catch(function(err) {
console.log();
console.log(chalk.grey('Raw error=', err.message));
self.disconnect();
});
};
};
module.exports.generateDockerfile = generateDockerfile;
module.exports.parseAndSwitch = parseAndSwitch;
module.exports.switchDockerFile = switchDockerFile;

459
api.hyungi.net/node_modules/pm2/lib/API/Dashboard.js generated vendored Normal file
View File

@@ -0,0 +1,459 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
var os = require('os');
var p = require('path');
var blessed = require('blessed');
var debug = require('debug')('pm2:monit');
var printf = require('sprintf-js').sprintf;
// Total memory
const totalMem = os.totalmem();
var Dashboard = {};
var DEFAULT_PADDING = {
top : 0,
left : 1,
right : 1
};
var WIDTH_LEFT_PANEL = 30;
/**
* Synchronous Dashboard init method
* @method init
* @return this
*/
Dashboard.init = function() {
// Init Screen
this.screen = blessed.screen({
smartCSR: true,
fullUnicode: true
});
this.screen.title = 'PM2 Dashboard';
this.logLines = {}
this.list = blessed.list({
top: '0',
left: '0',
width: WIDTH_LEFT_PANEL + '%',
height: '70%',
padding: 0,
scrollbar: {
ch: ' ',
inverse: false
},
border: {
type: 'line'
},
keys: true,
autoCommandKeys: true,
tags: true,
style: {
selected: {
bg: 'blue',
fg: 'white'
},
scrollbar: {
bg: 'blue',
fg: 'black'
},
fg: 'white',
border: {
fg: 'blue'
},
header: {
fg: 'blue'
}
}
});
this.list.on('select item', (item, i) => {
this.logBox.clearItems()
})
this.logBox = blessed.list({
label: ' Logs ',
top: '0',
left: WIDTH_LEFT_PANEL + '%',
width: 100 - WIDTH_LEFT_PANEL + '%',
height: '70%',
padding: DEFAULT_PADDING,
scrollable: true,
scrollbar: {
ch: ' ',
inverse: false
},
keys: true,
autoCommandKeys: true,
tags: true,
border: {
type: 'line'
},
style: {
fg: 'white',
border: {
fg: 'white'
},
scrollbar: {
bg: 'blue',
fg: 'black'
}
}
});
this.metadataBox = blessed.box({
label: ' Metadata ',
top: '70%',
left: WIDTH_LEFT_PANEL + '%',
width: 100 - WIDTH_LEFT_PANEL + '%',
height: '26%',
padding: DEFAULT_PADDING,
scrollable: true,
scrollbar: {
ch: ' ',
inverse: false
},
keys: true,
autoCommandKeys: true,
tags: true,
border: {
type: 'line'
},
style: {
fg: 'white',
border: {
fg: 'white'
},
scrollbar: {
bg: 'blue',
fg: 'black'
}
}
});
this.metricsBox = blessed.list({
label: ' Custom Metrics ',
top: '70%',
left: '0%',
width: WIDTH_LEFT_PANEL + '%',
height: '26%',
padding: DEFAULT_PADDING,
scrollbar: {
ch: ' ',
inverse: false
},
keys: true,
autoCommandKeys: true,
tags: true,
border: {
type: 'line'
},
style: {
fg: 'white',
border: {
fg: 'white'
},
scrollbar: {
bg: 'blue',
fg: 'black'
}
}
});
this.box4 = blessed.text({
content: ' left/right: switch boards | up/down/mouse: scroll | Ctrl-C: exit{|} {cyan-fg}{bold}To go further check out https://pm2.io/{/} ',
left: '0%',
top: '95%',
width: '100%',
height: '6%',
valign: 'middle',
tags: true,
style: {
fg: 'white'
}
});
this.list.focus();
this.screen.append(this.list);
this.screen.append(this.logBox);
this.screen.append(this.metadataBox);
this.screen.append(this.metricsBox);
this.screen.append(this.box4);
this.list.setLabel(' Process List ');
this.screen.render();
var that = this;
var i = 0;
var boards = ['list', 'logBox', 'metricsBox', 'metadataBox'];
this.screen.key(['left', 'right'], function(ch, key) {
(key.name === 'left') ? i-- : i++;
if (i == 4)
i = 0;
if (i == -1)
i = 3;
that[boards[i]].focus();
that[boards[i]].style.border.fg = 'blue';
if (key.name === 'left') {
if (i == 3)
that[boards[0]].style.border.fg = 'white';
else
that[boards[i + 1]].style.border.fg = 'white';
}
else {
if (i == 0)
that[boards[3]].style.border.fg = 'white';
else
that[boards[i - 1]].style.border.fg = 'white';
}
});
this.screen.key(['escape', 'q', 'C-c'], function(ch, key) {
this.screen.destroy();
process.exit(0);
});
// async refresh of the ui
setInterval(function () {
that.screen.render();
}, 300);
return this;
}
/**
* Refresh dashboard
* @method refresh
* @param {} processes
* @return this
*/
Dashboard.refresh = function(processes) {
debug('Monit refresh');
if(!processes) {
this.list.setItem(0, 'No process available');
return;
}
if (processes.length != this.list.items.length) {
this.list.clearItems();
}
// Total of processes memory
var mem = 0;
processes.forEach(function(proc) {
mem += proc.monit.memory;
})
// Sort process list
processes.sort(function(a, b) {
if (a.pm2_env.name < b.pm2_env.name)
return -1;
if (a.pm2_env.name > b.pm2_env.name)
return 1;
return 0;
});
// Loop to get process infos
for (var i = 0; i < processes.length; i++) {
// Percent of memory use by one process in all pm2 processes
var memPercent = (processes[i].monit.memory / mem) * 100;
// Status of process
var status = processes[i].pm2_env.status == 'online' ? '{green-fg}' : '{red-fg}';
status = status + '{bold}' + processes[i].pm2_env.status + '{/}';
var name = processes[i].pm2_env.name || p.basename(processes[i].pm2_env.pm_exec_path);
// Line of list
var item = printf('[%2s] %s {|} Mem: {bold}{%s-fg}%3d{/} MB CPU: {bold}{%s-fg}%2d{/} %s %s',
processes[i].pm2_env.pm_id,
name,
gradient(memPercent, [255, 0, 0], [0, 255, 0]),
(processes[i].monit.memory / 1048576).toFixed(2),
gradient(processes[i].monit.cpu, [255, 0, 0], [0, 255, 0]),
processes[i].monit.cpu,
"%",
status);
// Check if item exist
if (this.list.getItem(i)) {
this.list.setItem(i, item);
}
else {
this.list.pushItem(item);
}
var proc = processes[this.list.selected];
// render the logBox
let process_id = proc.pm_id
let logs = this.logLines[process_id];
if(typeof(logs) !== "undefined"){
this.logBox.setItems(logs)
if (!this.logBox.focused) {
this.logBox.setScrollPerc(100);
}
}else{
this.logBox.clearItems();
}
this.logBox.setLabel(` ${proc.pm2_env.name} Logs `)
this.metadataBox.setLine(0, 'App Name ' + '{bold}' + proc.pm2_env.name + '{/}');
this.metadataBox.setLine(1, 'Namespace ' + '{bold}' + proc.pm2_env.namespace + '{/}');
this.metadataBox.setLine(2, 'Version ' + '{bold}' + proc.pm2_env.version + '{/}');
this.metadataBox.setLine(3, 'Restarts ' + proc.pm2_env.restart_time);
this.metadataBox.setLine(4, 'Uptime ' + ((proc.pm2_env.pm_uptime && proc.pm2_env.status == 'online') ? timeSince(proc.pm2_env.pm_uptime) : 0));
this.metadataBox.setLine(5, 'Script path ' + proc.pm2_env.pm_exec_path);
this.metadataBox.setLine(6, 'Script args ' + (proc.pm2_env.args ? (typeof proc.pm2_env.args == 'string' ? JSON.parse(proc.pm2_env.args.replace(/'/g, '"')):proc.pm2_env.args).join(' ') : 'N/A'));
this.metadataBox.setLine(7, 'Interpreter ' + proc.pm2_env.exec_interpreter);
this.metadataBox.setLine(8, 'Interpreter args ' + (proc.pm2_env.node_args.length != 0 ? proc.pm2_env.node_args : 'N/A'));
this.metadataBox.setLine(9, 'Exec mode ' + (proc.pm2_env.exec_mode == 'fork_mode' ? '{bold}fork{/}' : '{blue-fg}{bold}cluster{/}'));
this.metadataBox.setLine(10, 'Node.js version ' + proc.pm2_env.node_version);
this.metadataBox.setLine(11, 'watch & reload ' + (proc.pm2_env.watch ? '{green-fg}{bold}✔{/}' : '{red-fg}{bold}✘{/}'));
this.metadataBox.setLine(12, 'Unstable restarts ' + proc.pm2_env.unstable_restarts);
this.metadataBox.setLine(13, 'Comment ' + ((proc.pm2_env.versioning) ? proc.pm2_env.versioning.comment : 'N/A'));
this.metadataBox.setLine(14, 'Revision ' + ((proc.pm2_env.versioning) ? proc.pm2_env.versioning.revision : 'N/A'));
this.metadataBox.setLine(15, 'Branch ' + ((proc.pm2_env.versioning) ? proc.pm2_env.versioning.branch : 'N/A'));
this.metadataBox.setLine(16, 'Remote url ' + ((proc.pm2_env.versioning) ? proc.pm2_env.versioning.url : 'N/A'));
this.metadataBox.deleteLine(17)
this.metadataBox.setLine(17, 'Last update ' + ((proc.pm2_env.versioning) ? proc.pm2_env.versioning.update_time : 'N/A'));
if (Object.keys(proc.pm2_env.axm_monitor).length != this.metricsBox.items.length) {
this.metricsBox.clearItems();
}
var j = 0;
for (var key in proc.pm2_env.axm_monitor) {
var metric_name = proc.pm2_env.axm_monitor[key].hasOwnProperty('value') ? proc.pm2_env.axm_monitor[key].value : proc.pm2_env.axm_monitor[key]
var metric_unit = proc.pm2_env.axm_monitor[key].hasOwnProperty('unit') ? proc.pm2_env.axm_monitor[key].unit : null
var probe = `{bold}${key}{/} {|} ${metric_name}${metric_unit == null ? '' : ' ' + metric_unit}`
if (this.metricsBox.getItem(j)) {
this.metricsBox.setItem(j, probe);
}
else {
this.metricsBox.pushItem(probe);
}
j++;
}
this.screen.render();
}
return this;
}
/**
* Put Log
* @method log
* @param {} data
* @return this
*/
Dashboard.log = function(type, data) {
var that = this;
if(typeof(this.logLines[data.process.pm_id]) == "undefined"){
this.logLines[data.process.pm_id]=[];
}
// Logs colors
switch (type) {
case 'PM2':
var color = '{blue-fg}';
break;
case 'out':
var color = '{green-fg}';
break;
case 'err':
var color = '{red-fg}';
break;
default:
var color = '{white-fg}';
}
var logs = data.data.split('\n')
logs.forEach((log) => {
if (log.length > 0) {
this.logLines[data.process.pm_id].push(color + data.process.name + '{/} > ' + log)
//removing logs if longer than limit
let count = 0;
let max_count = 0;
let leading_process_id = -1;
for(var process_id in this.logLines){
count += this.logLines[process_id].length;
if( this.logLines[process_id].length > max_count){
leading_process_id = process_id;
max_count = this.logLines[process_id].length;
}
}
if (count > 200) {
this.logLines[leading_process_id].shift()
}
}
})
return this;
}
module.exports = Dashboard;
function timeSince(date) {
var seconds = Math.floor((new Date() - date) / 1000);
var interval = Math.floor(seconds / 31536000);
if (interval > 1) {
return interval + 'Y';
}
interval = Math.floor(seconds / 2592000);
if (interval > 1) {
return interval + 'M';
}
interval = Math.floor(seconds / 86400);
if (interval > 1) {
return interval + 'D';
}
interval = Math.floor(seconds / 3600);
if (interval > 1) {
return interval + 'h';
}
interval = Math.floor(seconds / 60);
if (interval > 1) {
return interval + 'm';
}
return Math.floor(seconds) + 's';
}
/* Args :
* p : Percent 0 - 100
* rgb_ : Array of rgb [255, 255, 255]
* Return :
* Hexa #FFFFFF
*/
function gradient(p, rgb_beginning, rgb_end) {
var w = (p / 100) * 2 - 1;
var w1 = (w + 1) / 2.0;
var w2 = 1 - w1;
var rgb = [parseInt(rgb_beginning[0] * w1 + rgb_end[0] * w2),
parseInt(rgb_beginning[1] * w1 + rgb_end[1] * w2),
parseInt(rgb_beginning[2] * w1 + rgb_end[2] * w2)];
return "#" + ((1 << 24) + (rgb[0] << 16) + (rgb[1] << 8) + rgb[2]).toString(16).slice(1);
}

117
api.hyungi.net/node_modules/pm2/lib/API/Deploy.js generated vendored Normal file
View File

@@ -0,0 +1,117 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
var fs = require('fs');
var cst = require('../../constants.js');
var Utility = require('../Utility.js');
var Common = require('../Common.js');
function deployHelper() {
console.log('');
console.log('-----> Helper: Deployment with PM2');
console.log('');
console.log(' Generate a sample ecosystem.config.js with the command');
console.log(' $ pm2 ecosystem');
console.log(' Then edit the file depending on your needs');
console.log('');
console.log(' Commands:');
console.log(' setup run remote setup commands');
console.log(' update update deploy to the latest release');
console.log(' revert [n] revert to [n]th last deployment or 1');
console.log(' curr[ent] output current release commit');
console.log(' prev[ious] output previous release commit');
console.log(' exec|run <cmd> execute the given <cmd>');
console.log(' list list previous deploy commits');
console.log(' [ref] deploy to [ref], the "ref" setting, or latest tag');
console.log('');
console.log('');
console.log(' Basic Examples:');
console.log('');
console.log(' First initialize remote production host:');
console.log(' $ pm2 deploy ecosystem.config.js production setup');
console.log('');
console.log(' Then deploy new code:');
console.log(' $ pm2 deploy ecosystem.config.js production');
console.log('');
console.log(' If I want to revert to the previous commit:');
console.log(' $ pm2 deploy ecosystem.config.js production revert 1');
console.log('');
console.log(' Execute a command on remote server:');
console.log(' $ pm2 deploy ecosystem.config.js production exec "pm2 restart all"');
console.log('');
console.log(' PM2 will look by default to the ecosystem.config.js file so you dont need to give the file name:');
console.log(' $ pm2 deploy production');
console.log(' Else you have to tell PM2 the name of your ecosystem file');
console.log('');
console.log(' More examples in https://github.com/Unitech/pm2');
console.log('');
};
module.exports = function(CLI) {
CLI.prototype.deploy = function(file, commands, cb) {
var that = this;
if (file == 'help') {
deployHelper();
return cb ? cb() : that.exitCli(cst.SUCCESS_EXIT);
}
var args = commands.rawArgs;
var env;
args.splice(0, args.indexOf('deploy') + 1);
// Find ecosystem file by default
if (!Common.isConfigFile(file)) {
env = args[0];
var defaultConfigNames = [ ...Common.getConfigFileCandidates('ecosystem'), 'ecosystem.json5', 'package.json'];
file = Utility.whichFileExists(defaultConfigNames);
if (!file) {
Common.printError('Not any default deployment file exists.'+
' Allowed default config file names are: ' + defaultConfigNames.join(', '));
return cb ? cb('Not any default ecosystem file present') : that.exitCli(cst.ERROR_EXIT);
}
}
else
env = args[1];
var json_conf = null;
try {
json_conf = Common.parseConfig(fs.readFileSync(file), file);
} catch (e) {
Common.printError(e);
return cb ? cb(e) : that.exitCli(cst.ERROR_EXIT);
}
if (!env) {
deployHelper();
return cb ? cb() : that.exitCli(cst.SUCCESS_EXIT);
}
if (!json_conf.deploy || !json_conf.deploy[env]) {
Common.printError('%s environment is not defined in %s file', env, file);
return cb ? cb('%s environment is not defined in %s file') : that.exitCli(cst.ERROR_EXIT);
}
if (!json_conf.deploy[env]['post-deploy']) {
json_conf.deploy[env]['post-deploy'] = 'pm2 startOrRestart ' + file + ' --env ' + env;
}
require('pm2-deploy').deployForEnv(json_conf.deploy, env, args, function(err, data) {
if (err) {
Common.printError('Deploy failed');
Common.printError(err.message || err);
return cb ? cb(err) : that.exitCli(cst.ERROR_EXIT);
}
Common.printOut('--> Success');
return cb ? cb(null, data) : that.exitCli(cst.SUCCESS_EXIT);
});
};
};

775
api.hyungi.net/node_modules/pm2/lib/API/Extra.js generated vendored Normal file
View File

@@ -0,0 +1,775 @@
/***************************
*
* Extra methods
*
**************************/
var cst = require('../../constants.js');
var Common = require('../Common.js');
var UX = require('./UX');
var chalk = require('chalk');
var path = require('path');
var fs = require('fs');
var fmt = require('../tools/fmt.js');
var dayjs = require('dayjs');
var pkg = require('../../package.json');
const copyDirSync = require('../tools/copydirSync.js')
module.exports = function(CLI) {
/**
* Get version of the daemonized PM2
* @method getVersion
* @callback cb
*/
CLI.prototype.getVersion = function(cb) {
var that = this;
that.Client.executeRemote('getVersion', {}, function(err) {
return cb ? cb.apply(null, arguments) : that.exitCli(cst.SUCCESS_EXIT);
});
};
/**
* Install pm2-sysmonit
*/
CLI.prototype.launchSysMonitoring = function(cb) {
if ((this.pm2_configuration && this.pm2_configuration.sysmonit != 'true') ||
process.env.TRAVIS ||
global.it === 'function' ||
cst.IS_WINDOWS === true)
return cb ? cb(null) : null
var filepath
try {
filepath = path.dirname(require.resolve('pm2-sysmonit'))
} catch(e) {
return cb ? cb(null) : null
}
this.start({
script: filepath
}, {
started_as_module : true
}, (err, res) => {
if (err) {
Common.printError(cst.PREFIX_MSG_ERR + 'Error while trying to serve : ' + err.message || err);
return cb ? cb(err) : this.speedList(cst.ERROR_EXIT);
}
return cb ? cb(null) : this.speedList();
});
};
/**
* Show application environment
* @method env
* @callback cb
*/
CLI.prototype.env = function(app_id, cb) {
var procs = []
var printed = 0
this.Client.executeRemote('getMonitorData', {}, (err, list) => {
list.forEach(l => {
if (app_id == l.pm_id) {
printed++
var env = Common.safeExtend({}, l.pm2_env)
Object.keys(env).forEach(key => {
console.log(`${key}: ${chalk.green(env[key])}`)
})
}
})
if (printed == 0) {
Common.err(`Modules with id ${app_id} not found`)
return cb ? cb.apply(null, arguments) : this.exitCli(cst.ERROR_EXIT);
}
return cb ? cb.apply(null, arguments) : this.exitCli(cst.SUCCESS_EXIT);
})
};
/**
* Get version of the daemonized PM2
* @method getVersion
* @callback cb
*/
CLI.prototype.report = function() {
var that = this;
var Log = require('./Log');
that.Client.executeRemote('getReport', {}, function(err, report) {
console.log()
console.log()
console.log()
console.log('```')
fmt.title('PM2 report')
fmt.field('Date', new Date());
fmt.sep();
if (report && !err) {
fmt.title(chalk.bold.blue('Daemon'));
fmt.field('pm2d version', report.pm2_version);
fmt.field('node version', report.node_version);
fmt.field('node path', report.node_path);
fmt.field('argv', report.argv);
fmt.field('argv0', report.argv0);
fmt.field('user', report.user);
fmt.field('uid', report.uid);
fmt.field('gid', report.gid);
fmt.field('uptime', dayjs(new Date()).diff(report.started_at, 'minute') + 'min');
}
fmt.sep();
fmt.title(chalk.bold.blue('CLI'));
fmt.field('local pm2', pkg.version);
fmt.field('node version', process.versions.node);
fmt.field('node path', process.env['_'] || 'not found');
fmt.field('argv', process.argv);
fmt.field('argv0', process.argv0);
fmt.field('user', process.env.USER || process.env.LNAME || process.env.USERNAME);
if (cst.IS_WINDOWS === false && process.geteuid)
fmt.field('uid', process.geteuid());
if (cst.IS_WINDOWS === false && process.getegid)
fmt.field('gid', process.getegid());
var os = require('os');
fmt.sep();
fmt.title(chalk.bold.blue('System info'));
fmt.field('arch', os.arch());
fmt.field('platform', os.platform());
fmt.field('type', os.type());
fmt.field('cpus', os.cpus()[0].model);
fmt.field('cpus nb', Object.keys(os.cpus()).length);
fmt.field('freemem', os.freemem());
fmt.field('totalmem', os.totalmem());
fmt.field('home', os.homedir());
that.Client.executeRemote('getMonitorData', {}, function(err, list) {
fmt.sep();
fmt.title(chalk.bold.blue('PM2 list'));
UX.list(list, that.gl_interact_infos);
fmt.sep();
fmt.title(chalk.bold.blue('Daemon logs'));
Log.tail([{
path : cst.PM2_LOG_FILE_PATH,
app_name : 'PM2',
type : 'PM2'
}], 20, false, function() {
console.log('```')
console.log()
console.log()
console.log(chalk.bold.green('Please copy/paste the above report in your issue on https://github.com/Unitech/pm2/issues'));
console.log()
console.log()
that.exitCli(cst.SUCCESS_EXIT);
});
});
});
};
CLI.prototype.getPID = function(app_name, cb) {
var that = this;
if (typeof(app_name) === 'function') {
cb = app_name;
app_name = null;
}
this.Client.executeRemote('getMonitorData', {}, function(err, list) {
if (err) {
Common.printError(cst.PREFIX_MSG_ERR + err);
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
}
var pids = [];
list.forEach(function(app) {
if (!app_name || app_name == app.name)
pids.push(app.pid);
})
if (!cb) {
Common.printOut(pids.join("\n"))
return that.exitCli(cst.SUCCESS_EXIT);
}
return cb(null, pids);
})
}
/**
* Create PM2 memory snapshot
* @method getVersion
* @callback cb
*/
CLI.prototype.profile = function(type, time, cb) {
var that = this;
var dayjs = require('dayjs');
var cmd
if (type == 'cpu') {
cmd = {
ext: '.cpuprofile',
action: 'profileCPU'
}
}
if (type == 'mem') {
cmd = {
ext: '.heapprofile',
action: 'profileMEM'
}
}
var file = path.join(process.cwd(), dayjs().format('dd-HH:mm:ss') + cmd.ext);
time = time || 10000
console.log(`Starting ${cmd.action} profiling for ${time}ms...`)
that.Client.executeRemote(cmd.action, {
pwd : file,
timeout: time
}, function(err) {
if (err) {
console.error(err);
return that.exitCli(1);
}
console.log(`Profile done in ${file}`)
return cb ? cb.apply(null, arguments) : that.exitCli(cst.SUCCESS_EXIT);
});
};
function basicMDHighlight(lines) {
console.log('\n\n+-------------------------------------+')
console.log(chalk.bold('README.md content:'))
lines = lines.split('\n')
var isInner = false
lines.forEach(l => {
if (l.startsWith('#'))
console.log(chalk.bold.green(l))
else if (isInner || l.startsWith('```')) {
if (isInner && l.startsWith('```'))
isInner = false
else if (isInner == false)
isInner = true
console.log(chalk.grey(l))
}
else if (l.startsWith('`'))
console.log(chalk.grey(l))
else
console.log(l)
})
console.log('+-------------------------------------+')
}
/**
* pm2 create command
* create boilerplate of application for fast try
* @method boilerplate
*/
CLI.prototype.boilerplate = function(cb) {
var i = 0
var projects = []
var enquirer = require('enquirer')
const forEach = require('async/forEach')
fs.readdir(path.join(__dirname, '../templates/sample-apps'), (err, items) => {
forEach(items, (app, next) => {
var fp = path.join(__dirname, '../templates/sample-apps', app)
fs.readFile(path.join(fp, 'package.json'), (err, dt) => {
var meta = JSON.parse(dt)
meta.fullpath = fp
meta.folder_name = app
projects.push(meta)
next()
})
}, () => {
const prompt = new enquirer.Select({
name: 'boilerplate',
message: 'Select a boilerplate',
choices: projects.map((p, i) => {
return {
message: `${chalk.bold.blue(p.name)} ${p.description}`,
value: `${i}`
}
})
});
prompt.run()
.then(answer => {
var p = projects[parseInt(answer)]
basicMDHighlight(fs.readFileSync(path.join(p.fullpath, 'README.md')).toString())
console.log(chalk.bold(`>> Project copied inside folder ./${p.folder_name}/\n`))
copyDirSync(p.fullpath, path.join(process.cwd(), p.folder_name));
this.start(path.join(p.fullpath, 'ecosystem.config.js'), {
cwd: p.fullpath
}, () => {
return cb ? cb.apply(null, arguments) : this.speedList(cst.SUCCESS_EXIT);
})
})
.catch(e => {
return cb ? cb.apply(null, arguments) : this.speedList(cst.SUCCESS_EXIT);
});
})
})
}
/**
* Description
* @method sendLineToStdin
*/
CLI.prototype.sendLineToStdin = function(pm_id, line, separator, cb) {
var that = this;
if (!cb && typeof(separator) == 'function') {
cb = separator;
separator = null;
}
var packet = {
pm_id : pm_id,
line : line + (separator || '\n')
};
that.Client.executeRemote('sendLineToStdin', packet, function(err, res) {
if (err) {
Common.printError(cst.PREFIX_MSG_ERR + err);
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
}
return cb ? cb(null, res) : that.speedList();
});
};
/**
* Description
* @method attachToProcess
*/
CLI.prototype.attach = function(pm_id, separator, cb) {
var that = this;
var readline = require('readline');
if (isNaN(pm_id)) {
Common.printError('pm_id must be a process number (not a process name)');
return cb ? cb(Common.retErr('pm_id must be number')) : that.exitCli(cst.ERROR_EXIT);
}
if (typeof(separator) == 'function') {
cb = separator;
separator = null;
}
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.on('close', function() {
return cb ? cb() : that.exitCli(cst.SUCCESS_EXIT);
});
that.Client.launchBus(function(err, bus, socket) {
if (err) {
Common.printError(err);
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
}
bus.on('log:*', function(type, packet) {
if (packet.process.pm_id !== parseInt(pm_id))
return;
process.stdout.write(packet.data);
});
});
rl.on('line', function(line) {
that.sendLineToStdin(pm_id, line, separator, function() {});
});
};
/**
* Description
* @method sendDataToProcessId
*/
CLI.prototype.sendDataToProcessId = function(proc_id, packet, cb) {
var that = this;
if (typeof proc_id === 'object' && typeof packet === 'function') {
// the proc_id is packet.
cb = packet;
packet = proc_id;
} else {
packet.id = proc_id;
}
that.Client.executeRemote('sendDataToProcessId', packet, function(err, res) {
if (err) {
Common.printError(err);
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
}
Common.printOut('successfully sent data to process');
return cb ? cb(null, res) : that.speedList();
});
};
/**
* Used for custom actions, allows to trigger function inside an app
* To expose a function you need to use keymetrics/pmx
*
* @method msgProcess
* @param {Object} opts
* @param {String} id process id
* @param {String} action_name function name to trigger
* @param {Object} [opts.opts] object passed as first arg of the function
* @param {String} [uuid] optional unique identifier when logs are emitted
*
*/
CLI.prototype.msgProcess = function(opts, cb) {
var that = this;
that.Client.executeRemote('msgProcess', opts, cb);
};
/**
* Trigger a PMX custom action in target application
* Custom actions allows to interact with an application
*
* @method trigger
* @param {String|Number} pm_id process id or application name
* @param {String} action_name name of the custom action to trigger
* @param {Mixed} params parameter to pass to target action
* @param {Function} cb callback
*/
CLI.prototype.trigger = function(pm_id, action_name, params, cb) {
if (typeof(params) === 'function') {
cb = params;
params = null;
}
var cmd = {
msg : action_name
};
var counter = 0;
var process_wait_count = 0;
var that = this;
var results = [];
if (params)
cmd.opts = params;
if (isNaN(pm_id))
cmd.name = pm_id;
else
cmd.id = pm_id;
this.launchBus(function(err, bus) {
bus.on('axm:reply', function(ret) {
if (ret.process.name == pm_id || ret.process.pm_id == pm_id || ret.process.namespace == pm_id || pm_id == 'all') {
results.push(ret);
Common.printOut('[%s:%s:%s]=%j', ret.process.name, ret.process.pm_id, ret.process.namespace, ret.data.return);
if (++counter == process_wait_count)
return cb ? cb(null, results) : that.exitCli(cst.SUCCESS_EXIT);
}
});
that.msgProcess(cmd, function(err, data) {
if (err) {
Common.printError(err);
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
}
if (data.process_count == 0) {
Common.printError('Not any process has received a command (offline or unexistent)');
return cb ? cb(Common.retErr('Unknown process')) : that.exitCli(cst.ERROR_EXIT);
}
process_wait_count = data.process_count;
Common.printOut(chalk.bold('%s processes have received command %s'),
data.process_count, action_name);
});
});
};
/**
* Description
* @method sendSignalToProcessName
* @param {} signal
* @param {} process_name
* @return
*/
CLI.prototype.sendSignalToProcessName = function(signal, process_name, cb) {
var that = this;
that.Client.executeRemote('sendSignalToProcessName', {
signal : signal,
process_name : process_name
}, function(err, list) {
if (err) {
Common.printError(err);
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
}
Common.printOut('successfully sent signal %s to process name %s', signal, process_name);
return cb ? cb(null, list) : that.speedList();
});
};
/**
* Description
* @method sendSignalToProcessId
* @param {} signal
* @param {} process_id
* @return
*/
CLI.prototype.sendSignalToProcessId = function(signal, process_id, cb) {
var that = this;
that.Client.executeRemote('sendSignalToProcessId', {
signal : signal,
process_id : process_id
}, function(err, list) {
if (err) {
Common.printError(err);
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
}
Common.printOut('successfully sent signal %s to process id %s', signal, process_id);
return cb ? cb(null, list) : that.speedList();
});
};
/**
* API method to launch a process that will serve directory over http
*/
CLI.prototype.autoinstall = function (cb) {
var filepath = path.resolve(path.dirname(module.filename), '../Sysinfo/ServiceDetection/ServiceDetection.js');
this.start(filepath, (err, res) => {
if (err) {
Common.printError(cst.PREFIX_MSG_ERR + 'Error while trying to serve : ' + err.message || err);
return cb ? cb(err) : this.speedList(cst.ERROR_EXIT);
}
return cb ? cb(null) : this.speedList();
});
}
/**
* API method to launch a process that will serve directory over http
*
* @param {Object} opts options
* @param {String} opts.path path to be served
* @param {Number} opts.port port on which http will bind
* @param {Boolean} opts.spa single page app served
* @param {String} opts.basicAuthUsername basic auth username
* @param {String} opts.basicAuthPassword basic auth password
* @param {Object} commander commander object
* @param {Function} cb optional callback
*/
CLI.prototype.serve = function (target_path, port, opts, commander, cb) {
var that = this;
var servePort = process.env.PM2_SERVE_PORT || port || 8080;
var servePath = path.resolve(process.env.PM2_SERVE_PATH || target_path || '.');
var filepath = path.resolve(path.dirname(module.filename), './Serve.js');
if (typeof commander.name === 'string')
opts.name = commander.name
else
opts.name = 'static-page-server-' + servePort
if (!opts.env)
opts.env = {};
opts.env.PM2_SERVE_PORT = servePort;
opts.env.PM2_SERVE_PATH = servePath;
opts.env.PM2_SERVE_SPA = opts.spa;
if (opts.basicAuthUsername && opts.basicAuthPassword) {
opts.env.PM2_SERVE_BASIC_AUTH = 'true';
opts.env.PM2_SERVE_BASIC_AUTH_USERNAME = opts.basicAuthUsername;
opts.env.PM2_SERVE_BASIC_AUTH_PASSWORD = opts.basicAuthPassword;
}
if (opts.monitor) {
opts.env.PM2_SERVE_MONITOR = opts.monitor
}
opts.cwd = servePath;
this.start(filepath, opts, function (err, res) {
if (err) {
Common.printError(cst.PREFIX_MSG_ERR + 'Error while trying to serve : ' + err.message || err);
return cb ? cb(err) : that.speedList(cst.ERROR_EXIT);
}
Common.printOut(cst.PREFIX_MSG + 'Serving ' + servePath + ' on port ' + servePort);
return cb ? cb(null, res) : that.speedList();
});
}
/**
* Ping daemon - if PM2 daemon not launched, it will launch it
* @method ping
*/
CLI.prototype.ping = function(cb) {
var that = this;
that.Client.executeRemote('ping', {}, function(err, res) {
if (err) {
Common.printError(err);
return cb ? cb(new Error(err)) : that.exitCli(cst.ERROR_EXIT);
}
Common.printOut(res);
return cb ? cb(null, res) : that.exitCli(cst.SUCCESS_EXIT);
});
};
/**
* Execute remote command
*/
CLI.prototype.remote = function(command, opts, cb) {
var that = this;
that[command](opts.name, function(err_cmd, ret) {
if (err_cmd)
console.error(err_cmd);
console.log('Command %s finished', command);
return cb(err_cmd, ret);
});
};
/**
* This remote method allows to pass multiple arguments
* to PM2
* It is used for the new scoped PM2 action system
*/
CLI.prototype.remoteV2 = function(command, opts, cb) {
var that = this;
if (that[command].length == 1)
return that[command](cb);
opts.args.push(cb);
return that[command].apply(this, opts.args);
};
/**
* Description
* @method generateSample
* @param {} name
* @return
*/
CLI.prototype.generateSample = function(mode) {
var that = this;
var templatePath;
if (mode == 'simple')
templatePath = path.join(cst.TEMPLATE_FOLDER, cst.APP_CONF_TPL_SIMPLE);
else
templatePath = path.join(cst.TEMPLATE_FOLDER, cst.APP_CONF_TPL);
var sample = fs.readFileSync(templatePath);
var dt = sample.toString();
var f_name = 'ecosystem.config.js';
var pwd = process.env.PWD || process.cwd();
try {
fs.writeFileSync(path.join(pwd, f_name), dt);
} catch (e) {
console.error(e.stack || e);
return that.exitCli(cst.ERROR_EXIT);
}
Common.printOut('File %s generated', path.join(pwd, f_name));
that.exitCli(cst.SUCCESS_EXIT);
};
/**
* Description
* @method dashboard
* @return
*/
CLI.prototype.dashboard = function(cb) {
var that = this;
var Dashboard = require('./Dashboard');
if (cb)
return cb(new Error('Dashboard cant be called programmatically'));
Dashboard.init();
this.Client.launchBus(function (err, bus) {
if (err) {
console.error('Error launchBus: ' + err);
that.exitCli(cst.ERROR_EXIT);
}
bus.on('log:*', function(type, data) {
Dashboard.log(type, data)
})
});
process.on('SIGINT', function() {
this.Client.disconnectBus(function() {
process.exit(cst.SUCCESS_EXIT);
});
});
function refreshDashboard() {
that.Client.executeRemote('getMonitorData', {}, function(err, list) {
if (err) {
console.error('Error retrieving process list: ' + err);
that.exitCli(cst.ERROR_EXIT);
}
Dashboard.refresh(list);
setTimeout(function() {
refreshDashboard();
}, 800);
});
}
refreshDashboard();
};
CLI.prototype.monit = function(cb) {
var that = this;
var Monit = require('./Monit.js');
if (cb) return cb(new Error('Monit cant be called programmatically'));
Monit.init();
function launchMonitor() {
that.Client.executeRemote('getMonitorData', {}, function(err, list) {
if (err) {
console.error('Error retrieving process list: ' + err);
that.exitCli(conf.ERROR_EXIT);
}
Monit.refresh(list);
setTimeout(function() {
launchMonitor();
}, 400);
});
}
launchMonitor();
};
CLI.prototype.inspect = function(app_name, cb) {
const that = this;
this.trigger(app_name, 'internal:inspect', function (err, res) {
if(res && res[0]) {
if (res[0].data.return === '') {
Common.printOut(`Inspect disabled on ${app_name}`);
} else {
Common.printOut(`Inspect enabled on ${app_name} => go to chrome : chrome://inspect !!!`);
}
} else {
Common.printOut(`Unable to activate inspect mode on ${app_name} !!!`);
}
that.exitCli(cst.SUCCESS_EXIT);
});
};
};

View File

@@ -0,0 +1,30 @@
const util = require('util')
const spawn = require('child_process').spawn
const DockerMgmt = {}
module.exports = DockerMgmt
function execDocker(cmd, cb) {
var i = spawn('docker', cmd, {
stdio : 'inherit',
env: process.env,
shell : true
})
i.on('close', cb)
}
DockerMgmt.processCommand = function(PM2, start_id, select_id, action, cb) {
PM2.Client.executeRemote('getSystemData', {}, (err, sys_infos) => {
if (sys_infos.containers && sys_infos.containers.length == 0)
return cb(new Error(`Process ${select_id} not found`))
var container = sys_infos.containers[select_id - start_id - 1]
if (action == 'stopProcessId')
execDocker(['stop', container.id], cb)
if (action == 'deleteProcessId')
execDocker(['rm', container.id], cb)
if (action == 'restartProcessId')
execDocker(['restart', container.id], cb)
})
}

315
api.hyungi.net/node_modules/pm2/lib/API/Log.js generated vendored Normal file
View File

@@ -0,0 +1,315 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
var fs = require('fs'),
util = require('util'),
chalk = require('chalk'),
forEachLimit = require('async/forEachLimit'),
dayjs = require('dayjs');
var Log = module.exports = {};
var DEFAULT_PADDING = ' ';
/**
* Tail logs from file stream.
* @param {Object} apps_list
* @param {Number} lines
* @param {Boolean} raw
* @param {Function} callback
* @return
*/
Log.tail = function(apps_list, lines, raw, callback) {
var that = this;
if (lines === 0 || apps_list.length === 0)
return callback && callback();
var count = 0;
var getLastLines = function (filename, lines, callback) {
var chunk = '';
var size = Math.max(0, fs.statSync(filename).size - (lines * 200));
var fd = fs.createReadStream(filename, {start : size});
fd.on('data', function(data) { chunk += data.toString(); });
fd.on('end', function() {
chunk = chunk.split('\n').slice(-(lines+1));
chunk.pop();
callback(chunk);
});
};
apps_list.sort(function(a, b) {
return (fs.existsSync(a.path) ? fs.statSync(a.path).mtime.valueOf() : 0) -
(fs.existsSync(b.path) ? fs.statSync(b.path).mtime.valueOf() : 0);
});
forEachLimit(apps_list, 1, function(app, next) {
if (!fs.existsSync(app.path || ''))
return next();
getLastLines(app.path, lines, function(output) {
console.log(chalk.grey('%s last %d lines:'), app.path, lines);
output.forEach(function(out) {
if (raw)
return app.type === 'err' ? console.error(out) : console.log(out);
if (app.type === 'out')
process.stdout.write(chalk.green(pad(DEFAULT_PADDING, app.app_name) + ' | '));
else if (app.type === 'err')
process.stdout.write(chalk.red(pad(DEFAULT_PADDING, app.app_name) + ' | '));
else
process.stdout.write(chalk.blue(pad(DEFAULT_PADDING, 'PM2') + ' | '));
console.log(out);
});
if (output.length)
process.stdout.write('\n');
next();
});
}, function() {
callback && callback();
});
};
/**
* Stream logs in realtime from the bus eventemitter.
* @param {String} id
* @param {Boolean} raw
* @return
*/
Log.stream = function(Client, id, raw, timestamp, exclusive, highlight) {
var that = this;
Client.launchBus(function(err, bus, socket) {
socket.on('reconnect attempt', function() {
if (global._auto_exit === true) {
if (timestamp)
process.stdout.write(chalk['dim'](chalk.grey(dayjs().format(timestamp) + ' ')));
process.stdout.write(chalk.blue(pad(DEFAULT_PADDING, 'PM2') + ' | ') + '[[[ Target PM2 killed. ]]]');
process.exit(0);
}
});
var min_padding = 3
bus.on('log:*', function(type, packet) {
var isMatchingProcess = id === 'all'
|| packet.process.name == id
|| packet.process.pm_id == id
|| packet.process.namespace == id;
if (!isMatchingProcess)
return;
if ((type === 'out' && exclusive === 'err')
|| (type === 'err' && exclusive === 'out')
|| (type === 'PM2' && exclusive !== false))
return;
var lines;
if (typeof(packet.data) === 'string')
lines = (packet.data || '').split('\n');
else
return;
lines.forEach(function(line) {
if (!line || line.length === 0) return;
if (raw)
return type === 'err' ? process.stderr.write(util.format(line) + '\n') : process.stdout.write(util.format(line) + '\n');
if (timestamp)
process.stdout.write(chalk['dim'](chalk.grey(dayjs().format(timestamp) + ' ')));
var name = packet.process.pm_id + '|' + packet.process.name;
if (name.length > min_padding)
min_padding = name.length + 1
if (type === 'out')
process.stdout.write(chalk.green(pad(' '.repeat(min_padding), name) + ' | '));
else if (type === 'err')
process.stdout.write(chalk.red(pad(' '.repeat(min_padding), name) + ' | '));
else if (!raw && (id === 'all' || id === 'PM2'))
process.stdout.write(chalk.blue(pad(' '.repeat(min_padding), 'PM2') + ' | '));
if (highlight)
process.stdout.write(util.format(line).replace(highlight, chalk.bgBlackBright(highlight)) + '\n');
else
process.stdout.write(util.format(line)+ '\n');
});
});
});
};
Log.devStream = function(Client, id, raw, timestamp, exclusive) {
var that = this;
Client.launchBus(function(err, bus) {
setTimeout(function() {
bus.on('process:event', function(packet) {
if (packet.event == 'online')
console.log(chalk.green('[rundev] App %s restarted'), packet.process.name);
});
}, 1000);
var min_padding = 3
bus.on('log:*', function(type, packet) {
if (id !== 'all'
&& packet.process.name != id
&& packet.process.pm_id != id)
return;
if ((type === 'out' && exclusive === 'err')
|| (type === 'err' && exclusive === 'out')
|| (type === 'PM2' && exclusive !== false))
return;
if (type === 'PM2')
return;
var name = packet.process.pm_id + '|' + packet.process.name;
var lines;
if (typeof(packet.data) === 'string')
lines = (packet.data || '').split('\n');
else
return;
lines.forEach(function(line) {
if (!line || line.length === 0) return;
if (raw)
return process.stdout.write(util.format(line) + '\n');
if (timestamp)
process.stdout.write(chalk['dim'](chalk.grey(dayjs().format(timestamp) + ' ')));
var name = packet.process.name + '-' + packet.process.pm_id;
if (name.length > min_padding)
min_padding = name.length + 1
if (type === 'out')
process.stdout.write(chalk.green(pad(' '.repeat(min_padding), name) + ' | '));
else if (type === 'err')
process.stdout.write(chalk.red(pad(' '.repeat(min_padding), name) + ' | '));
else if (!raw && (id === 'all' || id === 'PM2'))
process.stdout.write(chalk.blue(pad(' '.repeat(min_padding), 'PM2') + ' | '));
process.stdout.write(util.format(line) + '\n');
});
});
});
};
Log.jsonStream = function(Client, id) {
var that = this;
Client.launchBus(function(err, bus) {
if (err) console.error(err);
bus.on('process:event', function(packet) {
process.stdout.write(JSON.stringify({
timestamp : dayjs(packet.at),
type : 'process_event',
status : packet.event,
app_name : packet.process.name
}));
process.stdout.write('\n');
});
bus.on('log:*', function(type, packet) {
if (id !== 'all'
&& packet.process.name != id
&& packet.process.pm_id != id)
return;
if (type === 'PM2')
return;
if (typeof(packet.data) == 'string')
packet.data = packet.data.replace(/(\r\n|\n|\r)/gm,'');
process.stdout.write(JSON.stringify({
message : packet.data,
timestamp : dayjs(packet.at),
type : type,
process_id : packet.process.pm_id,
app_name : packet.process.name
}));
process.stdout.write('\n');
});
});
};
Log.formatStream = function(Client, id, raw, timestamp, exclusive, highlight) {
var that = this;
Client.launchBus(function(err, bus) {
bus.on('log:*', function(type, packet) {
if (id !== 'all'
&& packet.process.name != id
&& packet.process.pm_id != id)
return;
if ((type === 'out' && exclusive === 'err')
|| (type === 'err' && exclusive === 'out')
|| (type === 'PM2' && exclusive !== false))
return;
if (type === 'PM2' && raw)
return;
var name = packet.process.name + '-' + packet.process.pm_id;
var lines;
if (typeof(packet.data) === 'string')
lines = (packet.data || '').split('\n');
else
return;
lines.forEach(function(line) {
if (!line || line.length === 0) return;
if (!raw) {
if (timestamp)
process.stdout.write('timestamp=' + dayjs().format(timestamp) + ' ');
if (packet.process.name === 'PM2')
process.stdout.write('app=pm2 ');
if (packet.process.name !== 'PM2')
process.stdout.write('app=' + packet.process.name + ' id=' + packet.process.pm_id + ' ');
if (type === 'out')
process.stdout.write('type=out ');
else if (type === 'err')
process.stdout.write('type=error ');
}
process.stdout.write('message=');
if (highlight)
process.stdout.write(util.format(line).replace(highlight, chalk.bgBlackBright(highlight)) + '\n');
else
process.stdout.write(util.format(line) + '\n');
});
});
});
};
function pad(pad, str, padLeft) {
if (typeof str === 'undefined')
return pad;
if (padLeft) {
return (pad + str).slice(-pad.length);
} else {
return (str + pad).substring(0, pad.length);
}
}

View File

@@ -0,0 +1,371 @@
var chalk = require('chalk');
var util = require('util');
var fs = require('fs');
var exec = require('child_process').exec;
var path = require('path');
var Log = require('./Log');
var cst = require('../../constants.js');
var Common = require('../Common.js');
module.exports = function(CLI) {
/**
* Description
* @method flush
* @return
*/
CLI.prototype.flush = function(api, cb) {
var that = this;
if (!api) {
Common.printOut(cst.PREFIX_MSG + 'Flushing ' + cst.PM2_LOG_FILE_PATH);
fs.closeSync(fs.openSync(cst.PM2_LOG_FILE_PATH, 'w'));
}
that.Client.executeRemote('getMonitorData', {}, function(err, list) {
if (err) {
Common.printError(err);
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
}
list.forEach(function(l) {
if (typeof api == 'undefined') {
Common.printOut(cst.PREFIX_MSG + 'Flushing:');
Common.printOut(cst.PREFIX_MSG + l.pm2_env.pm_out_log_path);
Common.printOut(cst.PREFIX_MSG + l.pm2_env.pm_err_log_path);
if (l.pm2_env.pm_log_path) {
Common.printOut(cst.PREFIX_MSG + l.pm2_env.pm_log_path);
fs.closeSync(fs.openSync(l.pm2_env.pm_log_path, 'w'));
}
fs.closeSync(fs.openSync(l.pm2_env.pm_out_log_path, 'w'));
fs.closeSync(fs.openSync(l.pm2_env.pm_err_log_path, 'w'));
}
else if (l.pm2_env.pm_id == api || l.pm2_env.name === api) {
Common.printOut(cst.PREFIX_MSG + 'Flushing:');
if (l.pm2_env.pm_log_path && fs.existsSync(l.pm2_env.pm_log_path)) {
Common.printOut(cst.PREFIX_MSG + l.pm2_env.pm_log_path);
fs.closeSync(fs.openSync(l.pm2_env.pm_log_path, 'w'));
}
if (l.pm2_env.pm_out_log_path && fs.existsSync(l.pm2_env.pm_out_log_path)) {
Common.printOut(cst.PREFIX_MSG + l.pm2_env.pm_out_log_path);
fs.closeSync(fs.openSync(l.pm2_env.pm_out_log_path, 'w'));
}
if (l.pm2_env.pm_err_log_path && fs.existsSync(l.pm2_env.pm_err_log_path)) {
Common.printOut(cst.PREFIX_MSG + l.pm2_env.pm_err_log_path);
fs.closeSync(fs.openSync(l.pm2_env.pm_err_log_path, 'w'));
}
}
});
Common.printOut(cst.PREFIX_MSG + 'Logs flushed');
return cb ? cb(null, list) : that.exitCli(cst.SUCCESS_EXIT);
});
};
CLI.prototype.logrotate = function(opts, cb) {
var that = this;
if (process.getuid() != 0) {
return exec('whoami', function(err, stdout, stderr) {
Common.printError(cst.PREFIX_MSG + 'You have to run this command as root. Execute the following command:');
Common.printError(cst.PREFIX_MSG + chalk.grey(' sudo env PATH=$PATH:' + path.dirname(process.execPath) + ' pm2 logrotate -u ' + stdout.trim()));
cb ? cb(Common.retErr('You have to run this with elevated rights')) : that.exitCli(cst.ERROR_EXIT);
});
}
if (!fs.existsSync('/etc/logrotate.d')) {
Common.printError(cst.PREFIX_MSG + '/etc/logrotate.d does not exist we can not copy the default configuration.');
return cb ? cb(Common.retErr('/etc/logrotate.d does not exist')) : that.exitCli(cst.ERROR_EXIT);
}
var templatePath = path.join(cst.TEMPLATE_FOLDER, cst.LOGROTATE_SCRIPT);
Common.printOut(cst.PREFIX_MSG + 'Getting logrorate template ' + templatePath);
var script = fs.readFileSync(templatePath, {encoding: 'utf8'});
var user = opts.user || 'root';
script = script.replace(/%HOME_PATH%/g, cst.PM2_ROOT_PATH)
.replace(/%USER%/g, user);
try {
fs.writeFileSync('/etc/logrotate.d/pm2-'+user, script);
} catch (e) {
console.error(e.stack || e);
}
Common.printOut(cst.PREFIX_MSG + 'Logrotate configuration added to /etc/logrotate.d/pm2');
return cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT);
};
/**
* Description
* @method reloadLogs
* @return
*/
CLI.prototype.reloadLogs = function(cb) {
var that = this;
Common.printOut('Reloading all logs...');
that.Client.executeRemote('reloadLogs', {}, function(err, logs) {
if (err) {
Common.printError(err);
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
}
Common.printOut('All logs reloaded');
return cb ? cb(null, logs) : that.exitCli(cst.SUCCESS_EXIT);
});
};
/**
* Description
* @method streamLogs
* @param {String} id
* @param {Number} lines
* @param {Boolean} raw
* @return
*/
CLI.prototype.streamLogs = function(id, lines, raw, timestamp, exclusive, highlight) {
var that = this;
var files_list = [];
// If no argument is given, we stream logs for all running apps
id = id || 'all';
lines = lines !== undefined ? lines : 20;
lines = lines < 0 ? -(lines) : lines;
// Avoid duplicates and check if path is different from '/dev/null'
var pushIfUnique = function(entry) {
var exists = false;
if (entry.path.toLowerCase
&& entry.path.toLowerCase() !== '/dev/null') {
files_list.some(function(file) {
if (file.path === entry.path)
exists = true;
return exists;
});
if (exists)
return;
files_list.push(entry);
}
}
// Get the list of all running apps
that.Client.executeRemote('getMonitorData', {}, function(err, list) {
var regexList = [];
var namespaceList = [];
if (err) {
Common.printError(err);
that.exitCli(cst.ERROR_EXIT);
}
if (lines === 0)
return Log.stream(that.Client, id, raw, timestamp, exclusive, highlight);
Common.printOut(chalk.bold.grey(util.format.call(this, '[TAILING] Tailing last %d lines for [%s] process%s (change the value with --lines option)', lines, id, id === 'all' ? 'es' : '')));
// Populate the array `files_list` with the paths of all files we need to tail
list.forEach(function(proc) {
if (proc.pm2_env && (id === 'all' ||
proc.pm2_env.name == id ||
proc.pm2_env.pm_id == id)) {
if (proc.pm2_env.pm_out_log_path && exclusive !== 'err')
pushIfUnique({
path : proc.pm2_env.pm_out_log_path,
app_name :proc.pm2_env.pm_id + '|' + proc.pm2_env.name,
type : 'out'});
if (proc.pm2_env.pm_err_log_path && exclusive !== 'out')
pushIfUnique({
path : proc.pm2_env.pm_err_log_path,
app_name : proc.pm2_env.pm_id + '|' + proc.pm2_env.name,
type : 'err'
});
} else if(proc.pm2_env && proc.pm2_env.namespace == id) {
if(namespaceList.indexOf(proc.pm2_env.name) === -1) {
namespaceList.push(proc.pm2_env.name)
}
if (proc.pm2_env.pm_out_log_path && exclusive !== 'err')
pushIfUnique({
path : proc.pm2_env.pm_out_log_path,
app_name :proc.pm2_env.pm_id + '|' + proc.pm2_env.name,
type : 'out'});
if (proc.pm2_env.pm_err_log_path && exclusive !== 'out')
pushIfUnique({
path : proc.pm2_env.pm_err_log_path,
app_name : proc.pm2_env.pm_id + '|' + proc.pm2_env.name,
type : 'err'
});
}
// Populate the array `files_list` with the paths of all files we need to tail, when log in put is a regex
else if(proc.pm2_env && (isNaN(id) && id[0] === '/' && id[id.length - 1] === '/')) {
var regex = new RegExp(id.replace(/\//g, ''));
if(regex.test(proc.pm2_env.name)) {
if(regexList.indexOf(proc.pm2_env.name) === -1) {
regexList.push(proc.pm2_env.name);
}
if (proc.pm2_env.pm_out_log_path && exclusive !== 'err')
pushIfUnique({
path : proc.pm2_env.pm_out_log_path,
app_name : proc.pm2_env.pm_id + '|' + proc.pm2_env.name,
type : 'out'});
if (proc.pm2_env.pm_err_log_path && exclusive !== 'out')
pushIfUnique({
path : proc.pm2_env.pm_err_log_path,
app_name : proc.pm2_env.pm_id + '|' + proc.pm2_env.name,
type : 'err'
});
}
}
});
//for fixing issue https://github.com/Unitech/pm2/issues/3506
/* if (files_list && files_list.length == 0) {
Common.printError(cst.PREFIX_MSG_ERR + 'No file to stream for app [%s], exiting.', id);
return process.exit(cst.ERROR_EXIT);
}*/
if (!raw && (id === 'all' || id === 'PM2') && exclusive === false) {
Log.tail([{
path : cst.PM2_LOG_FILE_PATH,
app_name : 'PM2',
type : 'PM2'
}], lines, raw, function() {
Log.tail(files_list, lines, raw, function() {
Log.stream(that.Client, id, raw, timestamp, exclusive, highlight);
});
});
}
else {
Log.tail(files_list, lines, raw, function() {
if(regexList.length > 0) {
regexList.forEach(function(id) {
Log.stream(that.Client, id, raw, timestamp, exclusive, highlight);
})
}
else if(namespaceList.length > 0) {
namespaceList.forEach(function(id) {
Log.stream(that.Client, id, raw, timestamp, exclusive, highlight);
})
}
else {
Log.stream(that.Client, id, raw, timestamp, exclusive, highlight);
}
});
}
});
};
/**
* Description
* @method printLogs
* @param {String} id
* @param {Number} lines
* @param {Boolean} raw
* @return
*/
CLI.prototype.printLogs = function(id, lines, raw, timestamp, exclusive) {
var that = this;
var files_list = [];
// If no argument is given, we stream logs for all running apps
id = id || 'all';
lines = lines !== undefined ? lines : 20;
lines = lines < 0 ? -(lines) : lines;
// Avoid duplicates and check if path is different from '/dev/null'
var pushIfUnique = function(entry) {
var exists = false;
if (entry.path.toLowerCase
&& entry.path.toLowerCase() !== '/dev/null') {
files_list.some(function(file) {
if (file.path === entry.path)
exists = true;
return exists;
});
if (exists)
return;
files_list.push(entry);
}
}
// Get the list of all running apps
that.Client.executeRemote('getMonitorData', {}, function(err, list) {
if (err) {
Common.printError(err);
that.exitCli(cst.ERROR_EXIT);
}
if (lines <= 0) {
return that.exitCli(cst.SUCCESS_EXIT)
}
Common.printOut(chalk.bold.grey(util.format.call(this, '[TAILING] Tailing last %d lines for [%s] process%s (change the value with --lines option)', lines, id, id === 'all' ? 'es' : '')));
// Populate the array `files_list` with the paths of all files we need to tail
list.forEach(function(proc) {
if (proc.pm2_env && (id === 'all' ||
proc.pm2_env.name == id ||
proc.pm2_env.pm_id == id)) {
if (proc.pm2_env.pm_out_log_path && exclusive !== 'err')
pushIfUnique({
path : proc.pm2_env.pm_out_log_path,
app_name :proc.pm2_env.pm_id + '|' + proc.pm2_env.name,
type : 'out'});
if (proc.pm2_env.pm_err_log_path && exclusive !== 'out')
pushIfUnique({
path : proc.pm2_env.pm_err_log_path,
app_name : proc.pm2_env.pm_id + '|' + proc.pm2_env.name,
type : 'err'
});
}
// Populate the array `files_list` with the paths of all files we need to tail, when log in put is a regex
else if(proc.pm2_env && (isNaN(id) && id[0] === '/' && id[id.length - 1] === '/')) {
var regex = new RegExp(id.replace(/\//g, ''));
if(regex.test(proc.pm2_env.name)) {
if (proc.pm2_env.pm_out_log_path && exclusive !== 'err')
pushIfUnique({
path : proc.pm2_env.pm_out_log_path,
app_name : proc.pm2_env.pm_id + '|' + proc.pm2_env.name,
type : 'out'});
if (proc.pm2_env.pm_err_log_path && exclusive !== 'out')
pushIfUnique({
path : proc.pm2_env.pm_err_log_path,
app_name : proc.pm2_env.pm_id + '|' + proc.pm2_env.name,
type : 'err'
});
}
}
});
if (!raw && (id === 'all' || id === 'PM2') && exclusive === false) {
Log.tail([{
path : cst.PM2_LOG_FILE_PATH,
app_name : 'PM2',
type : 'PM2'
}], lines, raw, function() {
Log.tail(files_list, lines, raw, function() {
that.exitCli(cst.SUCCESS_EXIT);
});
});
}
else {
Log.tail(files_list, lines, raw, function() {
that.exitCli(cst.SUCCESS_EXIT);
});
}
});
};
};

View File

@@ -0,0 +1,122 @@
var path = require('path');
var fs = require('fs');
var os = require('os');
var spawn = require('child_process').spawn;
var chalk = require('chalk');
var parallel = require('async/parallel');
var Configuration = require('../../Configuration.js');
var cst = require('../../../constants.js');
var Common = require('../../Common');
var Utility = require('../../Utility.js');
var readline = require('readline')
var INTERNAL_MODULES = {
'deep-monitoring': {
dependencies: [{name: 'v8-profiler-node8'}, {name: 'gc-stats'}, {name: 'event-loop-inspector'}]
},
'gc-stats': {name: 'gc-stats'},
'event-loop-inspector': {name: 'event-loop-inspector'},
'v8-profiler': {name: 'v8-profiler-node8'},
'profiler': {name: 'v8-profiler-node8'},
'typescript': {dependencies: [{name: 'typescript'}, {name: 'ts-node@latest'}]},
'livescript': {name: 'livescript'},
'coffee-script': {name: 'coffee-script', message: 'Coffeescript v1 support'},
'coffeescript': {name: 'coffeescript', message: 'Coffeescript v2 support'}
};
module.exports = {
install,
INTERNAL_MODULES,
installMultipleModules
}
function install(module, cb, verbose) {
if (!module || !module.name || module.name.length === 0) {
return cb(new Error('No module name !'));
}
if (typeof verbose === 'undefined') {
verbose = true;
}
installLangModule(module.name, function (err) {
var display = module.message || module.name;
if (err) {
if (verbose) { Common.printError(cst.PREFIX_MSG_MOD_ERR + chalk.bold.green(display + ' installation has FAILED (checkout previous logs)')); }
return cb(err);
}
if (verbose) { Common.printOut(cst.PREFIX_MSG + chalk.bold.green(display + ' ENABLED')); }
return cb();
});
}
function installMultipleModules(modules, cb, post_install) {
var functionList = [];
for (var i = 0; i < modules.length; i++) {
functionList.push((function (index) {
return function (callback) {
var module = modules[index];
if (typeof modules[index] === 'string') {
module = {name: modules[index]};
}
install(module, function ($post_install, err, $index, $modules) {
try {
var install_instance = spawn(post_install[modules[index]], {
stdio : 'inherit',
windowsHide: true,
env: process.env,
shell : true,
cwd : process.cwd()
});
Common.printOut(cst.PREFIX_MSG_MOD + 'Running configuraton script.');
}
catch(e)
{
Common.printOut(cst.PREFIX_MSG_MOD + 'No configuraton script found.');
}
callback(null, { module: module, err: err });
}, false);
};
})(i));
}
parallel(functionList, function (err, results) {
for (var i = 0; i < results.length; i++) {
var display = results[i].module.message || results[i].module.name;
if (results[i].err) {
err = results[i].err;
Common.printError(cst.PREFIX_MSG_MOD_ERR + chalk.bold.green(display + ' installation has FAILED (checkout previous logs)'));
} else {
Common.printOut(cst.PREFIX_MSG + chalk.bold.green(display + ' ENABLED'));
}
}
if(cb) cb(err);
});
};
function installLangModule(module_name, cb) {
var node_module_path = path.resolve(path.join(__dirname, '../../../'));
Common.printOut(cst.PREFIX_MSG_MOD + 'Calling ' + chalk.bold.red('[NPM]') + ' to install ' + module_name + ' ...');
var install_instance = spawn(cst.IS_WINDOWS ? 'npm.cmd' : 'npm', ['install', module_name, '--loglevel=error'], {
stdio : 'inherit',
env: process.env,
shell : true,
cwd : node_module_path
});
install_instance.on('close', function(code) {
if (code > 0)
return cb(new Error('Module install failed'));
return cb(null);
});
install_instance.on('error', function (err) {
console.error(err.stack || err);
});
};

View File

@@ -0,0 +1,148 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
var path = require('path');
var eachLimit = require('async/eachLimit');
var forEachLimit = require('async/forEachLimit');
var Configuration = require('../../Configuration.js');
var cst = require('../../../constants.js');
var Common = require('../../Common');
var NPM = require('./NPM.js')
var TAR = require('./TAR.js')
var LOCAL = require('./LOCAL.js')
var Modularizer = module.exports = {};
/**
* PM2 Module System.
*/
Modularizer.install = function (CLI, module_name, opts, cb) {
module_name = module_name.replace(/[;`|]/g, "");
if (typeof(opts) == 'function') {
cb = opts;
opts = {};
}
if (LOCAL.INTERNAL_MODULES.hasOwnProperty(module_name)) {
Common.logMod(`Adding dependency ${module_name} to PM2 Runtime`);
var currentModule = LOCAL.INTERNAL_MODULES[module_name];
if (currentModule && currentModule.hasOwnProperty('dependencies')) {
LOCAL.installMultipleModules(currentModule.dependencies, cb);
} else {
LOCAL.install(currentModule, cb);
}
}
else if (module_name == '.') {
Common.logMod(`Installing local NPM module`);
return NPM.localStart(CLI, opts, cb)
}
else if (opts.tarball || /\.tar\.gz$/i.test(module_name)) {
Common.logMod(`Installing TAR module`);
TAR.install(CLI, module_name, opts, cb)
}
else {
Common.logMod(`Installing NPM ${module_name} module`);
NPM.install(CLI, module_name, opts, cb)
}
};
/**
* Launch All Modules
* Used PM2 at startup
*/
Modularizer.launchModules = function(CLI, cb) {
var modules = Modularizer.listModules();
if (!modules) return cb();
// 1#
function launchNPMModules(cb) {
if (!modules.npm_modules) return launchTARModules(cb)
eachLimit(Object.keys(modules.npm_modules), 1, function(module_name, next) {
NPM.start(CLI, modules, module_name, next)
}, function() {
launchTARModules(cb)
});
}
// 2#
function launchTARModules(cb) {
if (!modules.tar_modules) return cb()
eachLimit(Object.keys(modules.tar_modules), 1, function(module_name, next) {
TAR.start(CLI, module_name, next)
}, function() {
return cb ? cb(null) : false;
});
}
launchNPMModules(cb)
}
Modularizer.package = function(CLI, module_path, cb) {
var fullpath = process.cwd()
if (module_path)
fullpath = require('path').resolve(module_path)
TAR.package(fullpath, process.cwd(), cb)
}
/**
* Uninstall module
*/
Modularizer.uninstall = function(CLI, module_name, cb) {
Common.printOut(cst.PREFIX_MSG_MOD + 'Uninstalling module ' + module_name);
var modules_list = Modularizer.listModules();
if (module_name == 'all') {
if (!modules_list) return cb();
return forEachLimit(Object.keys(modules_list.npm_modules), 1, function(module_name, next) {
NPM.uninstall(CLI, module_name, next)
}, () => {
forEachLimit(Object.keys(modules_list.tar_modules), 1, function(module_name, next) {
TAR.uninstall(CLI, module_name, next)
}, cb)
});
}
if (modules_list.npm_modules[module_name]) {
NPM.uninstall(CLI, module_name, cb)
} else if (modules_list.tar_modules[module_name]) {
TAR.uninstall(CLI, module_name, cb)
}
else {
Common.errMod('Unknown module')
CLI.exitCli(1)
}
};
/**
* List modules based on modules present in ~/.pm2/modules/ folder
*/
Modularizer.listModules = function() {
return {
npm_modules: Configuration.getSync(cst.MODULE_CONF_PREFIX) || {},
tar_modules: Configuration.getSync(cst.MODULE_CONF_PREFIX_TAR) || {}
}
};
Modularizer.getAdditionalConf = function(app_name) {
return NPM.getModuleConf(app_name)
};
Modularizer.publish = function(PM2, folder, opts, cb) {
if (opts.npm == true) {
NPM.publish(opts, cb)
}
else {
TAR.publish(PM2, folder, cb)
}
};
Modularizer.generateSample = function(app_name, cb) {
NPM.generateSample(app_name, cb)
};

437
api.hyungi.net/node_modules/pm2/lib/API/Modules/NPM.js generated vendored Normal file
View File

@@ -0,0 +1,437 @@
const path = require('path');
const fs = require('fs');
const os = require('os');
const spawn = require('child_process').spawn;
const chalk = require('chalk');
const readline = require('readline')
const which = require('../../tools/which.js')
const sexec = require('../../tools/sexec.js')
const copydirSync = require('../../tools/copydirSync.js')
const deleteFolderRecursive = require('../../tools/deleteFolderRecursive.js')
var Configuration = require('../../Configuration.js');
var cst = require('../../../constants.js');
var Common = require('../../Common');
var Utility = require('../../Utility.js');
module.exports = {
install,
uninstall,
start,
publish,
generateSample,
localStart,
getModuleConf
}
/**
* PM2 Module System.
* Features:
* - Installed modules are listed separately from user applications
* - Always ON, a module is always up along PM2, to stop it, you need to uninstall it
* - Install a runnable module from NPM/Github/HTTP (require a package.json only)
* - Some modules add internal PM2 depencencies (like typescript, profiling...)
* - Internally it uses NPM install (https://docs.npmjs.com/cli/install)
* - Auto discover script to launch (first it checks the apps field, then bin and finally main attr)
* - Generate sample module via pm2 module:generate <module_name>
*/
function localStart(PM2, opts, cb) {
var proc_path = '',
cmd = '',
conf = {};
Common.printOut(cst.PREFIX_MSG_MOD + 'Installing local module in DEVELOPMENT MODE with WATCH auto restart');
proc_path = process.cwd();
cmd = path.join(proc_path, cst.DEFAULT_MODULE_JSON);
Common.extend(opts, {
cmd : cmd,
development_mode : true,
proc_path : proc_path
});
return StartModule(PM2, opts, function(err, dt) {
if (err) return cb(err);
Common.printOut(cst.PREFIX_MSG_MOD + 'Module successfully installed and launched');
return cb(null, dt);
});
}
function generateSample(app_name, cb) {
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
function samplize(module_name) {
var cmd1 = 'git clone https://github.com/pm2-hive/sample-module.git ' + module_name + '; cd ' + module_name + '; rm -rf .git';
var cmd2 = 'cd ' + module_name + ' ; sed -i "s:sample-module:'+ module_name +':g" package.json';
var cmd3 = 'cd ' + module_name + ' ; npm install';
Common.printOut(cst.PREFIX_MSG_MOD + 'Getting sample app');
sexec(cmd1, function(err) {
if (err) Common.printError(cst.PREFIX_MSG_MOD_ERR + err.message);
sexec(cmd2, function(err) {
console.log('');
sexec(cmd3, function(err) {
console.log('');
Common.printOut(cst.PREFIX_MSG_MOD + 'Module sample created in folder: ', path.join(process.cwd(), module_name));
console.log('');
Common.printOut('Start module in development mode:');
Common.printOut('$ cd ' + module_name + '/');
Common.printOut('$ pm2 install . ');
console.log('');
Common.printOut('Module Log: ');
Common.printOut('$ pm2 logs ' + module_name);
console.log('');
Common.printOut('Uninstall module: ');
Common.printOut('$ pm2 uninstall ' + module_name);
console.log('');
Common.printOut('Force restart: ');
Common.printOut('$ pm2 restart ' + module_name);
return cb ? cb() : false;
});
});
});
}
if (app_name) return samplize(app_name);
rl.question(cst.PREFIX_MSG_MOD + "Module name: ", function(module_name) {
samplize(module_name);
});
}
function publish(opts, cb) {
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
var semver = require('semver');
var package_file = path.join(process.cwd(), 'package.json');
var package_json = require(package_file);
package_json.version = semver.inc(package_json.version, 'minor');
Common.printOut(cst.PREFIX_MSG_MOD + 'Incrementing module to: %s@%s',
package_json.name,
package_json.version);
rl.question("Write & Publish? [Y/N]", function(answer) {
if (answer != "Y")
return cb();
fs.writeFile(package_file, JSON.stringify(package_json, null, 2), function(err, data) {
if (err) return cb(err);
Common.printOut(cst.PREFIX_MSG_MOD + 'Publishing module - %s@%s',
package_json.name,
package_json.version);
sexec('npm publish', function(code) {
Common.printOut(cst.PREFIX_MSG_MOD + 'Module - %s@%s successfully published',
package_json.name,
package_json.version);
Common.printOut(cst.PREFIX_MSG_MOD + 'Pushing module on Git');
sexec('git add . ; git commit -m "' + package_json.version + '"; git push origin master', function(code) {
Common.printOut(cst.PREFIX_MSG_MOD + 'Installable with pm2 install %s', package_json.name);
return cb(null, package_json);
});
});
});
});
}
function moduleExistInLocalDB(CLI, module_name, cb) {
var modules = Configuration.getSync(cst.MODULE_CONF_PREFIX);
if (!modules) return cb(false);
var module_name_only = Utility.getCanonicModuleName(module_name)
modules = Object.keys(modules);
return cb(modules.indexOf(module_name_only) > -1 ? true : false);
};
function install(CLI, module_name, opts, cb) {
moduleExistInLocalDB(CLI, module_name, function (exists) {
if (exists) {
Common.logMod('Module already installed. Updating.');
Rollback.backup(module_name);
return uninstall(CLI, module_name, function () {
return continueInstall(CLI, module_name, opts, cb);
});
}
return continueInstall(CLI, module_name, opts, cb);
})
}
// Builtin Node Switch
function getNPMCommandLine(module_name, install_path) {
if (which('npm')) {
return spawn.bind(this, cst.IS_WINDOWS ? 'npm.cmd' : 'npm', ['install', module_name, '--loglevel=error', '--prefix', `"${install_path}"` ], {
stdio : 'inherit',
env: process.env,
windowsHide: true,
shell : true
})
}
else {
return spawn.bind(this, cst.BUILTIN_NODE_PATH, [cst.BUILTIN_NPM_PATH, 'install', module_name, '--loglevel=error', '--prefix', `"${install_path}"`], {
stdio : 'inherit',
env: process.env,
windowsHide: true,
shell : true
})
}
}
function continueInstall(CLI, module_name, opts, cb) {
Common.printOut(cst.PREFIX_MSG_MOD + 'Calling ' + chalk.bold.red('[NPM]') + ' to install ' + module_name + ' ...');
var canonic_module_name = Utility.getCanonicModuleName(module_name);
var install_path = path.join(cst.DEFAULT_MODULE_PATH, canonic_module_name);
require('mkdirp')(install_path)
.then(function() {
process.chdir(os.homedir());
var install_instance = getNPMCommandLine(module_name, install_path)();
install_instance.on('close', finalizeInstall);
install_instance.on('error', function (err) {
console.error(err.stack || err);
});
});
function finalizeInstall(code) {
if (code != 0) {
// If install has failed, revert to previous module version
return Rollback.revert(CLI, module_name, function() {
return cb(new Error('Installation failed via NPM, module has been restored to prev version'));
});
}
Common.printOut(cst.PREFIX_MSG_MOD + 'Module downloaded');
var proc_path = path.join(install_path, 'node_modules', canonic_module_name);
var package_json_path = path.join(proc_path, 'package.json');
// Append default configuration to module configuration
try {
var conf = JSON.parse(fs.readFileSync(package_json_path).toString()).config;
if (conf) {
Object.keys(conf).forEach(function(key) {
Configuration.setSyncIfNotExist(canonic_module_name + ':' + key, conf[key]);
});
}
} catch(e) {
Common.printError(e);
}
opts = Common.extend(opts, {
cmd : package_json_path,
development_mode : false,
proc_path : proc_path
});
Configuration.set(cst.MODULE_CONF_PREFIX + ':' + canonic_module_name, {
uid : opts.uid,
gid : opts.gid
}, function(err, data) {
if (err) return cb(err);
StartModule(CLI, opts, function(err, dt) {
if (err) return cb(err);
if (process.env.PM2_PROGRAMMATIC === 'true')
return cb(null, dt);
CLI.conf(canonic_module_name, function() {
Common.printOut(cst.PREFIX_MSG_MOD + 'Module successfully installed and launched');
Common.printOut(cst.PREFIX_MSG_MOD + 'Checkout module options: `$ pm2 conf`');
return cb(null, dt);
});
});
});
}
}
function start(PM2, modules, module_name, cb) {
Common.printOut(cst.PREFIX_MSG_MOD + 'Starting NPM module ' + module_name);
var install_path = path.join(cst.DEFAULT_MODULE_PATH, module_name);
var proc_path = path.join(install_path, 'node_modules', module_name);
var package_json_path = path.join(proc_path, 'package.json');
var opts = {};
// Merge with embedded configuration inside module_conf (uid, gid)
Common.extend(opts, modules[module_name]);
// Merge meta data to start module properly
Common.extend(opts, {
// package.json path
cmd : package_json_path,
// starting mode
development_mode : false,
// process cwd
proc_path : proc_path
});
StartModule(PM2, opts, function(err, dt) {
if (err) console.error(err);
return cb();
})
}
function uninstall(CLI, module_name, cb) {
var module_name_only = Utility.getCanonicModuleName(module_name)
var proc_path = path.join(cst.DEFAULT_MODULE_PATH, module_name_only);
Configuration.unsetSync(cst.MODULE_CONF_PREFIX + ':' + module_name_only);
CLI.deleteModule(module_name_only, function(err, data) {
console.log('Deleting', proc_path)
if (module_name != '.' && proc_path.includes('modules') === true) {
deleteFolderRecursive(proc_path)
}
if (err) {
Common.printError(err);
return cb(err);
}
return cb(null, data);
});
}
function getModuleConf(app_name) {
if (!app_name) throw new Error('No app_name defined');
var module_conf = Configuration.getAllSync();
var additional_env = {};
if (!module_conf[app_name]) {
additional_env = {};
additional_env[app_name] = {};
}
else {
additional_env = Common.clone(module_conf[app_name]);
additional_env[app_name] = JSON.stringify(module_conf[app_name]);
}
return additional_env;
}
function StartModule(CLI, opts, cb) {
if (!opts.cmd && !opts.package) throw new Error('module package.json not defined');
if (!opts.development_mode) opts.development_mode = false;
var package_json = require(opts.cmd || opts.package);
/**
* Script file detection
* 1- *apps* field (default pm2 json configuration)
* 2- *bin* field
* 3- *main* field
*/
if (!package_json.apps && !package_json.pm2) {
package_json.apps = {};
if (package_json.bin) {
var bin = Object.keys(package_json.bin)[0];
package_json.apps.script = package_json.bin[bin];
}
else if (package_json.main) {
package_json.apps.script = package_json.main;
}
}
Common.extend(opts, {
cwd : opts.proc_path,
watch : opts.development_mode,
force_name : package_json.name,
started_as_module : true
});
// Start the module
CLI.start(package_json, opts, function(err, data) {
if (err) return cb(err);
if (opts.safe) {
Common.printOut(cst.PREFIX_MSG_MOD + 'Monitoring module behavior for potential issue (5secs...)');
var time = typeof(opts.safe) == 'boolean' ? 3000 : parseInt(opts.safe);
return setTimeout(function() {
CLI.describe(package_json.name, function(err, apps) {
if (err || apps[0].pm2_env.restart_time > 2) {
return Rollback.revert(CLI, package_json.name, function() {
return cb(new Error('New Module is instable, restored to previous version'));
});
}
return cb(null, data);
});
}, time);
}
return cb(null, data);
});
};
var Rollback = {
revert : function(CLI, module_name, cb) {
var canonic_module_name = Utility.getCanonicModuleName(module_name);
var backup_path = path.join(require('os').tmpdir(), canonic_module_name);
var module_path = path.join(cst.DEFAULT_MODULE_PATH, canonic_module_name);
try {
fs.statSync(backup_path)
} catch(e) {
return cb(new Error('no backup found'));
}
Common.printOut(cst.PREFIX_MSG_MOD + chalk.bold.red('[[[[[ Module installation failure! ]]]]]'));
Common.printOut(cst.PREFIX_MSG_MOD + chalk.bold.red('[RESTORING TO PREVIOUS VERSION]'));
CLI.deleteModule(canonic_module_name, function() {
// Delete failing module
if (module_name.includes('modules') === true)
deleteFolderRecursive(module_path)
// Restore working version
copydirSync(backup_path, path.join(cst.DEFAULT_MODULE_PATH, canonic_module_name));
var proc_path = path.join(module_path, 'node_modules', canonic_module_name);
var package_json_path = path.join(proc_path, 'package.json');
// Start module
StartModule(CLI, {
cmd : package_json_path,
development_mode : false,
proc_path : proc_path
}, cb);
});
},
backup : function(module_name) {
// Backup current module
var tmpdir = require('os').tmpdir();
var canonic_module_name = Utility.getCanonicModuleName(module_name);
var module_path = path.join(cst.DEFAULT_MODULE_PATH, canonic_module_name);
copydirSync(module_path, path.join(tmpdir, canonic_module_name));
}
}

362
api.hyungi.net/node_modules/pm2/lib/API/Modules/TAR.js generated vendored Normal file
View File

@@ -0,0 +1,362 @@
var Configuration = require('../../Configuration.js');
var cst = require('../../../constants.js');
var Common = require('../../Common');
var forEachLimit = require('async/forEachLimit');
const sexec = require('../../tools/sexec.js');
const deleteFolderRecursive = require('../../tools/deleteFolderRecursive.js');
var path = require('path');
var fs = require('fs');
var os = require('os');
var spawn = require('child_process').spawn;
var exec = require('child_process').exec;
var execSync = require('child_process').execSync;
module.exports = {
install,
uninstall,
start,
publish,
packager
}
/**
* Module management to manage tarball packages
*
* pm2 install http.tar.gz
* pm2 uninstall http
*
* - the first and only folder in the tarball must be called module (tar zcvf http module/)
* - a package.json must be present with attribute "name", "version" and "pm2" to declare apps to run
*/
function install(PM2, module_filepath, opts, cb) {
// Remote file retrieval
if (module_filepath.includes('http') === true) {
var target_file = module_filepath.split('/').pop()
var target_filepath = path.join(os.tmpdir(), target_file)
opts.install_url = module_filepath
return retrieveRemote(module_filepath, target_filepath, (err) => {
if (err) {
Common.errMod(err)
process.exit(1)
}
installLocal(PM2, target_filepath, opts, cb)
})
}
// Local install
installLocal(PM2, module_filepath, opts, cb)
}
function retrieveRemote(url, dest, cb) {
Common.logMod(`Retrieving remote package ${url}...`)
var wget = spawn('wget', [url, '-O', dest, '-q'], {
stdio : 'inherit',
env: process.env,
windowsHide: true,
shell : true
})
wget.on('error', (err) => {
console.error(err.stack || err)
})
wget.on('close', (code) => {
if (code !== 0)
return cb(new Error('Could not download'))
return cb(null)
})
}
function installLocal(PM2, module_filepath, opts, cb) {
Common.logMod(`Installing package ${module_filepath}`)
// Get module name by unpacking the module/package.json only and read the name attribute
getModuleName(module_filepath, function(err, module_name) {
if (err) return cb(err)
Common.logMod(`Module name is ${module_name}`)
Common.logMod(`Depackaging module...`)
var install_path = path.join(cst.DEFAULT_MODULE_PATH, module_name);
require('mkdirp').sync(install_path)
var install_instance = spawn('tar', ['zxf', module_filepath, '-C', install_path, '--strip-components 1'], {
stdio : 'inherit',
env: process.env,
shell : true
})
install_instance.on('close', function(code) {
Common.logMod(`Module depackaged in ${install_path}`)
if (code == 0)
return runInstall(PM2, install_path, module_name, opts, cb)
return PM2.exitCli(1)
});
install_instance.on('error', function (err) {
console.error(err.stack || err);
});
})
}
function deleteModulePath(module_name) {
var sanitized = module_name.replace(/\./g, '')
deleteFolderRecursive(path.join(cst.DEFAULT_MODULE_PATH, module_name));
}
function runInstall(PM2, target_path, module_name, opts, cb) {
var config_file = path.join(target_path, 'package.json')
var conf
try {
conf = require(config_file)
module_name = conf.name
} catch(e) {
Common.errMod(new Error('Cannot find package.json file with name attribute at least'));
}
// Force with the name in the package.json
opts.started_as_module = true
opts.cwd = target_path
if (needPrefix(conf))
opts.name_prefix = module_name
if (opts.install) {
Common.logMod(`Running YARN install...`)
sexec(`cd ${target_path} ; yarn install`, {silent: false}, function(code) {
// Start apps under "apps" or "pm2" attribute
Common.logMod(`Starting ${target_path}`)
PM2.start(conf, opts, function(err, data) {
if (err) return cb(err)
Configuration.setSync(`${cst.MODULE_CONF_PREFIX_TAR}:${module_name}`, {
source: 'tarball',
install_url: opts.install_url,
installed_at: Date.now()
})
Common.logMod(`Module INSTALLED and STARTED`)
return cb(null, 'Module installed & Started')
})
})
}
else {
PM2.start(conf, opts, function(err, data) {
if (err) return cb(err)
Configuration.setSync(`${cst.MODULE_CONF_PREFIX_TAR}:${module_name}`, {
source: 'tarball',
install_url: opts.install_url,
installed_at: Date.now()
})
Common.logMod(`Module INSTALLED and STARTED`)
return cb(null, 'Module installed & Started')
})
}
}
function start(PM2, module_name, cb) {
var module_path = path.join(cst.DEFAULT_MODULE_PATH, module_name);
Common.printOut(cst.PREFIX_MSG_MOD + 'Starting TAR module ' + module_name);
var package_json_path = path.join(module_path, 'package.json');
var module_conf = Configuration.getSync(`${cst.MODULE_CONF_PREFIX_TAR}:${module_name}`)
try {
var conf = require(package_json_path)
} catch(e) {
Common.printError(`Could not find package.json as ${package_json_path}`)
return cb()
}
var opts = {};
opts.started_as_module = true
opts.cwd = module_path
if (module_conf.install_url)
opts.install_url = module_conf.install_url
if (needPrefix(conf))
opts.name_prefix = module_name
PM2.start(conf, opts, function(err, data) {
if (err) {
Common.printError(`Could not start ${module_name} ${module_path}`)
return cb()
}
Common.printOut(`${cst.PREFIX_MSG_MOD} Module ${module_name} STARTED`)
return cb();
})
}
/**
* Retrieve from module package.json the name of each application
* delete process and delete folder
*/
function uninstall(PM2, module_name, cb) {
var module_path = path.join(cst.DEFAULT_MODULE_PATH, module_name);
Common.logMod(`Removing ${module_name} from auto startup`)
try {
var pkg = require(path.join(module_path, 'package.json'))
} catch(e) {
Common.errMod('Could not retrieve module package.json');
return cb(e)
}
var apps = pkg.apps || pkg.pm2
apps = [].concat(apps);
/**
* Some time a module can have multiple processes
*/
forEachLimit(apps, 1, (app, next) => {
var app_name
if (!app.name) {
Common.renderApplicationName(app)
}
if (apps.length > 1)
app_name = `${module_name}:${app.name}`
else if (apps.length == 1 && pkg.name != apps[0].name)
app_name = `${module_name}:${app.name}`
else
app_name = app.name
PM2._operate('deleteProcessId', app_name, () => {
deleteModulePath(module_name)
next()
})
}, () => {
Configuration.unsetSync(`${cst.MODULE_CONF_PREFIX_TAR}:${module_name}`)
cb(null)
})
}
/**
* Uncompress only module/package.json and retrieve the "name" attribute in the package.json
*/
function getModuleName(module_filepath, cb) {
var tmp_folder = path.join(os.tmpdir(), cst.MODULE_BASEFOLDER)
var install_instance = spawn('tar', ['zxf', module_filepath, '-C', os.tmpdir(), `${cst.MODULE_BASEFOLDER}/package.json`], {
stdio : 'inherit',
env: process.env,
shell : true
})
install_instance.on('close', function(code) {
try {
var pkg = JSON.parse(fs.readFileSync(path.join(tmp_folder, `package.json`)))
return cb(null, pkg.name)
} catch(e) {
return cb(e)
}
});
}
function packager(module_path, target_path, cb) {
var base_folder = path.dirname(module_path)
var module_folder_name = path.basename(module_path)
var pkg = require(path.join(module_path, 'package.json'))
var pkg_name = `${module_folder_name}-v${pkg.version.replace(/\./g, '-')}.tar.gz`
var target_fullpath = path.join(target_path, pkg_name)
var cmd = `tar zcf ${target_fullpath} -C ${base_folder} --transform 's,${module_folder_name},module,' ${module_folder_name}`
Common.logMod(`Gziping ${module_path} to ${target_fullpath}`)
var tar = exec(cmd, (err, sto, ste) => {
if (err) {
console.log(sto.toString().trim())
console.log(ste.toString().trim())
}
})
tar.on('close', function (code) {
cb(code == 0 ? null : code, {
package_name: pkg_name,
path: target_fullpath
})
})
}
function publish(PM2, folder, cb) {
var target_folder = folder ? path.resolve(folder) : process.cwd()
try {
var pkg = JSON.parse(fs.readFileSync(path.join(target_folder, 'package.json')).toString())
} catch(e) {
Common.errMod(`${process.cwd()} module does not contain any package.json`)
process.exit(1)
}
if (!pkg.name) throw new Error('Attribute name should be present')
if (!pkg.version) throw new Error('Attribute version should be present')
if (!pkg.pm2 && !pkg.apps) throw new Error('Attribute apps should be present')
var current_path = target_folder
var module_name = path.basename(current_path)
var target_path = os.tmpdir()
Common.logMod(`Starting publishing procedure for ${module_name}@${pkg.version}`)
packager(current_path, target_path, (err, res) => {
if (err) {
Common.errMod('Can\'t package, exiting')
process.exit(1)
}
Common.logMod(`Package [${pkg.name}] created in path ${res.path}`)
var data = {
module_data: {
file: res.path,
content_type: 'content/gzip'
},
id: pkg.name,
name: pkg.name,
version: pkg.version
};
var uri = `${PM2.pm2_configuration.registry}/api/v1/modules`
Common.logMod(`Sending Package to remote ${pkg.name} ${uri}`)
require('needle')
.post(uri, data, { multipart: true }, function(err, res, body) {
if (err) {
Common.errMod(err)
process.exit(1)
}
if (res.statusCode !== 200) {
Common.errMod(`${pkg.name}-${pkg.version}: ${res.body.msg}`)
process.exit(1)
}
Common.logMod(`Module ${module_name} published under version ${pkg.version}`)
process.exit(0)
})
})
}
function needPrefix(conf) {
if ((conf.apps && conf.apps.length > 1) ||
(conf.pm2 && conf.pm2.length > 1) ||
(conf.apps.length == 1 && conf.name != conf.apps[0].name))
return true
return false
}

View File

@@ -0,0 +1,46 @@
var fs = require('fs');
var conf = require('../../../constants.js');
function find_extensions(folder, ext, ret)
{
try {
fs.accessSync(folder, fs.constants.R_OK);
} catch (err) {
return;
}
if(fs.statSync(folder).isDirectory() && folder.indexOf('node_modules') == -1 && (fs.statSync(folder)["mode"] & 4))
{
fs.readdirSync(folder).forEach(file => {
var tmp;
if(Number.parseInt(folder.lastIndexOf('/') + 1) === folder.length)
tmp = folder + file;
else
tmp = folder + '/' + file;
if(fs.statSync(tmp).isDirectory())
find_extensions(tmp, ext, ret);
else
{
var p = true;
for(var i = 0; i < ext.length;i++)
if(ext[i].test(file))
p = false;
if(p)
ret.push(folder + '/' + file);
}
});
}
}
module.exports.make_available_extension = function make_available_extension(opts, ret)
{
if(typeof opts == 'object' && typeof ret == 'object')
{
var mas = opts.ext.split(',');
for(var i = 0;i < mas.length;i++)
mas[i] = '.' + mas[i];
var res = [];
for(var i = 0;i < mas.length;i++)
res[i] = new RegExp(mas[i] + '$');
find_extensions(process.cwd(), res, ret);
}
}

View File

@@ -0,0 +1,120 @@
/***************************
*
* Module methods
*
**************************/
var cst = require('../../../constants.js');
var Common = require('../../Common.js');
var chalk = require('chalk');
var forEachLimit = require('async/forEachLimit');
var Modularizer = require('./Modularizer.js');
module.exports = function(CLI) {
/**
* Install / Update a module
*/
CLI.prototype.install = function(module_name, opts, cb) {
var that = this;
if (typeof(opts) == 'function') {
cb = opts;
opts = {};
}
Modularizer.install(this, module_name, opts, function(err, data) {
if (err) {
Common.printError(cst.PREFIX_MSG_ERR + (err.message || err));
return cb ? cb(Common.retErr(err)) : that.speedList(cst.ERROR_EXIT);
}
return cb ? cb(null, data) : that.speedList(cst.SUCCESS_EXIT);
});
};
/**
* Uninstall a module
*/
CLI.prototype.uninstall = function(module_name, cb) {
var that = this;
Modularizer.uninstall(this, module_name, function(err, data) {
if (err)
return cb ? cb(Common.retErr(err)) : that.speedList(cst.ERROR_EXIT);
return cb ? cb(null, data) : that.speedList(cst.SUCCESS_EXIT);
});
};
CLI.prototype.launchAll = function(CLI, cb) {
Modularizer.launchModules(CLI, cb);
};
CLI.prototype.package = function(module_path, cb) {
Modularizer.package(this, module_path, (err, res) => {
if (err) {
Common.errMod(err)
return cb ? cb(err) : this.exitCli(1)
}
Common.logMod(`Module packaged in ${res.path}`)
return cb ? cb(err) : this.exitCli(0)
})
};
/**
* Publish module on NPM + Git push
*/
CLI.prototype.publish = function(folder, opts, cb) {
var that = this;
Modularizer.publish(this, folder, opts, function(err, data) {
if (err)
return cb ? cb(Common.retErr(err)) : that.speedList(cst.ERROR_EXIT);
return cb ? cb(null, data) : that.speedList(cst.SUCCESS_EXIT);
});
};
/**
* Publish module on NPM + Git push
*/
CLI.prototype.generateModuleSample = function(app_name, cb) {
var that = this;
Modularizer.generateSample(app_name, function(err, data) {
if (err)
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
return cb ? cb(null, data) : that.exitCli(cst.SUCCESS_EXIT);
});
};
/**
* Special delete method
*/
CLI.prototype.deleteModule = function(module_name, cb) {
var that = this;
var found_proc = [];
this.Client.getAllProcess(function(err, procs) {
if (err) {
Common.printError('Error retrieving process list: ' + err);
return cb(Common.retErr(err));
}
procs.forEach(function(proc) {
if (proc.pm2_env.name == module_name && proc.pm2_env.pmx_module) {
found_proc.push(proc.pm_id);
}
});
if (found_proc.length == 0)
return cb();
that._operate('deleteProcessId', found_proc[0], function(err) {
if (err) return cb(Common.retErr(err));
Common.printOut('In memory process deleted');
return cb();
});
});
};
};

247
api.hyungi.net/node_modules/pm2/lib/API/Monit.js generated vendored Normal file
View File

@@ -0,0 +1,247 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
// pm2-htop
// Library who interacts with PM2 to display processes resources in htop way
// by Strzelewicz Alexandre
var multimeter = require('pm2-multimeter');
var os = require('os');
var p = require('path');
var chalk = require('chalk');
var UX = require('./UX');
var debug = require('debug')('pm2:monit');
// Cst for light programs
const RATIO_T1 = Math.floor(os.totalmem() / 500);
// Cst for medium programs
const RATIO_T2 = Math.floor(os.totalmem() / 50);
// Cst for heavy programs
const RATIO_T3 = Math.floor(os.totalmem() / 5);
// Cst for heavy programs
const RATIO_T4 = Math.floor(os.totalmem());
var Monit = {};
//helper to get bars.length (num bars printed)
Object.size = function(obj) {
var size = 0, key;
for (key in obj) {
if (obj.hasOwnProperty(key)) size++;
}
return size;
};
/**
* Reset the monitor through charm, basically \033c
* @param String msg optional message to show
* @return Monit
*/
Monit.reset = function(msg) {
this.multi.charm.reset();
this.multi.write('\x1B[32m⌬ PM2 \x1B[39mmonitoring\x1B[96m (To go further check out https://app.pm2.io) \x1B[39m\n\n');
if(msg) {
this.multi.write(msg);
}
this.bars = {};
return this;
}
/**
* Synchronous Monitor init method
* @method init
* @return Monit
*/
Monit.init = function() {
this.multi = multimeter(process);
this.multi.on('^C', this.stop);
this.reset();
return this;
}
/**
* Stops monitor
* @method stop
*/
Monit.stop = function() {
this.multi.charm.destroy();
process.exit(0);
}
/**
* Refresh monitor
* @method refresh
* @param {} processes
* @return this
*/
Monit.refresh = function(processes) {
debug('Monit refresh');
if(!processes) {
processes = [];
}
var num = processes.length;
this.num_bars = Object.size(this.bars);
if(num !== this.num_bars) {
debug('Monit addProcesses - actual: %s, new: %s', this.num_bars, num);
return this.addProcesses(processes);
} else {
if(num === 0) {
return;
}
debug('Monit refresh');
var proc;
for(var i = 0; i < num; i++) {
proc = processes[i];
//this is to avoid a print issue when the process is restarted for example
//we might also check for the pid but restarted|restarting will be rendered bad
if(this.bars[proc.pm_id] && proc.pm2_env.status !== this.bars[proc.pm_id].status) {
debug('bars for %s does not exist', proc.pm_id);
this.addProcesses(processes);
break;
}
this.updateBars(proc);
}
}
return this;
}
Monit.addProcess = function(proc, i) {
if(proc.pm_id in this.bars) {
return ;
}
if (proc.monit.error)
throw new Error(JSON.stringify(proc.monit.error));
var process_name = proc.pm2_env.name || p.basename(proc.pm2_env.pm_exec_path);
var status = proc.pm2_env.status == 'online' ? chalk.green.bold('●') : chalk.red.bold('●');
this.multi.write(' ' + status + ' ' + chalk.green.bold(process_name));
this.multi.write('\n');
this.multi.write('[' + proc.pm2_env.pm_id + '] [' + proc.pm2_env.exec_mode + ']\n');
var bar_cpu = this.multi(40, (i * 2) + 3 + i, {
width: 30,
solid: {
text: '|',
foreground: 'white',
background: 'blue'
},
empty: {
text: ' '
}
});
var bar_memory = this.multi(40, (i * 2) + 4 + i, {
width: 30,
solid: {
text: '|',
foreground: 'white',
background: 'red'
},
empty: {
text: ' '
}
});
this.bars[proc.pm_id] = {
memory: bar_memory,
cpu: bar_cpu,
status: proc.pm2_env.status
};
this.updateBars(proc);
this.multi.write('\n');
return this;
}
Monit.addProcesses = function(processes) {
if(!processes) {
processes = [];
}
this.reset();
var num = processes.length;
if(num > 0) {
for(var i = 0; i < num; i++) {
this.addProcess(processes[i], i);
}
} else {
this.reset('No processes to monit');
}
}
// Draw memory bars
/**
* Description
* @method drawRatio
* @param {} bar_memory
* @param {} memory
* @return
*/
Monit.drawRatio = function(bar_memory, memory) {
var scale = 0;
if (memory < RATIO_T1) scale = RATIO_T1;
else if (memory < RATIO_T2) scale = RATIO_T2;
else if (memory < RATIO_T3) scale = RATIO_T3;
else scale = RATIO_T4;
bar_memory.ratio(memory,
scale,
UX.helpers.bytesToSize(memory, 3));
};
/**
* Updates bars informations
* @param {} proc proc object
* @return this
*/
Monit.updateBars = function(proc) {
if (this.bars[proc.pm_id]) {
if (proc.pm2_env.status !== 'online' || proc.pm2_env.status !== this.bars[proc.pm_id].status) {
this.bars[proc.pm_id].cpu.percent(0, chalk.red(proc.pm2_env.status));
this.drawRatio(this.bars[proc.pm_id].memory, 0, chalk.red(proc.pm2_env.status));
} else if (!proc.monit) {
this.bars[proc.pm_id].cpu.percent(0, chalk.red('No data'));
this.drawRatio(this.bars[proc.pm_id].memory, 0, chalk.red('No data'));
} else {
this.bars[proc.pm_id].cpu.percent(proc.monit.cpu);
this.drawRatio(this.bars[proc.pm_id].memory, proc.monit.memory);
}
}
return this;
}
module.exports = Monit;

342
api.hyungi.net/node_modules/pm2/lib/API/Serve.js generated vendored Normal file
View File

@@ -0,0 +1,342 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
'use strict';
var fs = require('fs');
var http = require('http');
var url = require('url');
var path = require('path');
var debug = require('debug')('pm2:serve');
var probe = require('@pm2/io');
var errorMeter = probe.meter({
name : '404/sec',
samples : 1,
timeframe : 60
})
/**
* list of supported content types.
*/
var contentTypes = {
'3gp': 'video/3gpp',
'a': 'application/octet-stream',
'ai': 'application/postscript',
'aif': 'audio/x-aiff',
'aiff': 'audio/x-aiff',
'asc': 'application/pgp-signature',
'asf': 'video/x-ms-asf',
'asm': 'text/x-asm',
'asx': 'video/x-ms-asf',
'atom': 'application/atom+xml',
'au': 'audio/basic',
'avi': 'video/x-msvideo',
'bat': 'application/x-msdownload',
'bin': 'application/octet-stream',
'bmp': 'image/bmp',
'bz2': 'application/x-bzip2',
'c': 'text/x-c',
'cab': 'application/vnd.ms-cab-compressed',
'cc': 'text/x-c',
'chm': 'application/vnd.ms-htmlhelp',
'class': 'application/octet-stream',
'com': 'application/x-msdownload',
'conf': 'text/plain',
'cpp': 'text/x-c',
'crt': 'application/x-x509-ca-cert',
'css': 'text/css',
'csv': 'text/csv',
'cxx': 'text/x-c',
'deb': 'application/x-debian-package',
'der': 'application/x-x509-ca-cert',
'diff': 'text/x-diff',
'djv': 'image/vnd.djvu',
'djvu': 'image/vnd.djvu',
'dll': 'application/x-msdownload',
'dmg': 'application/octet-stream',
'doc': 'application/msword',
'dot': 'application/msword',
'dtd': 'application/xml-dtd',
'dvi': 'application/x-dvi',
'ear': 'application/java-archive',
'eml': 'message/rfc822',
'eps': 'application/postscript',
'exe': 'application/x-msdownload',
'f': 'text/x-fortran',
'f77': 'text/x-fortran',
'f90': 'text/x-fortran',
'flv': 'video/x-flv',
'for': 'text/x-fortran',
'gem': 'application/octet-stream',
'gemspec': 'text/x-script.ruby',
'gif': 'image/gif',
'gz': 'application/x-gzip',
'h': 'text/x-c',
'hh': 'text/x-c',
'htm': 'text/html',
'html': 'text/html',
'ico': 'image/vnd.microsoft.icon',
'ics': 'text/calendar',
'ifb': 'text/calendar',
'iso': 'application/octet-stream',
'jar': 'application/java-archive',
'java': 'text/x-java-source',
'jnlp': 'application/x-java-jnlp-file',
'jpeg': 'image/jpeg',
'jpg': 'image/jpeg',
'js': 'application/javascript',
'json': 'application/json',
'log': 'text/plain',
'm3u': 'audio/x-mpegurl',
'm4v': 'video/mp4',
'man': 'text/troff',
'mathml': 'application/mathml+xml',
'mbox': 'application/mbox',
'mdoc': 'text/troff',
'me': 'text/troff',
'mid': 'audio/midi',
'midi': 'audio/midi',
'mime': 'message/rfc822',
'mml': 'application/mathml+xml',
'mng': 'video/x-mng',
'mov': 'video/quicktime',
'mp3': 'audio/mpeg',
'mp4': 'video/mp4',
'mp4v': 'video/mp4',
'mpeg': 'video/mpeg',
'mpg': 'video/mpeg',
'ms': 'text/troff',
'msi': 'application/x-msdownload',
'odp': 'application/vnd.oasis.opendocument.presentation',
'ods': 'application/vnd.oasis.opendocument.spreadsheet',
'odt': 'application/vnd.oasis.opendocument.text',
'ogg': 'application/ogg',
'p': 'text/x-pascal',
'pas': 'text/x-pascal',
'pbm': 'image/x-portable-bitmap',
'pdf': 'application/pdf',
'pem': 'application/x-x509-ca-cert',
'pgm': 'image/x-portable-graymap',
'pgp': 'application/pgp-encrypted',
'pkg': 'application/octet-stream',
'pl': 'text/x-script.perl',
'pm': 'text/x-script.perl-module',
'png': 'image/png',
'pnm': 'image/x-portable-anymap',
'ppm': 'image/x-portable-pixmap',
'pps': 'application/vnd.ms-powerpoint',
'ppt': 'application/vnd.ms-powerpoint',
'ps': 'application/postscript',
'psd': 'image/vnd.adobe.photoshop',
'py': 'text/x-script.python',
'qt': 'video/quicktime',
'ra': 'audio/x-pn-realaudio',
'rake': 'text/x-script.ruby',
'ram': 'audio/x-pn-realaudio',
'rar': 'application/x-rar-compressed',
'rb': 'text/x-script.ruby',
'rdf': 'application/rdf+xml',
'roff': 'text/troff',
'rpm': 'application/x-redhat-package-manager',
'rss': 'application/rss+xml',
'rtf': 'application/rtf',
'ru': 'text/x-script.ruby',
's': 'text/x-asm',
'sgm': 'text/sgml',
'sgml': 'text/sgml',
'sh': 'application/x-sh',
'sig': 'application/pgp-signature',
'snd': 'audio/basic',
'so': 'application/octet-stream',
'svg': 'image/svg+xml',
'svgz': 'image/svg+xml',
'swf': 'application/x-shockwave-flash',
't': 'text/troff',
'tar': 'application/x-tar',
'tbz': 'application/x-bzip-compressed-tar',
'tcl': 'application/x-tcl',
'tex': 'application/x-tex',
'texi': 'application/x-texinfo',
'texinfo': 'application/x-texinfo',
'text': 'text/plain',
'tif': 'image/tiff',
'tiff': 'image/tiff',
'torrent': 'application/x-bittorrent',
'tr': 'text/troff',
'txt': 'text/plain',
'vcf': 'text/x-vcard',
'vcs': 'text/x-vcalendar',
'vrml': 'model/vrml',
'war': 'application/java-archive',
'wav': 'audio/x-wav',
'wma': 'audio/x-ms-wma',
'wmv': 'video/x-ms-wmv',
'wmx': 'video/x-ms-wmx',
'wrl': 'model/vrml',
'wsdl': 'application/wsdl+xml',
'xbm': 'image/x-xbitmap',
'xhtml': 'application/xhtml+xml',
'xls': 'application/vnd.ms-excel',
'xml': 'application/xml',
'xpm': 'image/x-xpixmap',
'xsl': 'application/xml',
'xslt': 'application/xslt+xml',
'yaml': 'text/yaml',
'yml': 'text/yaml',
'zip': 'application/zip',
'woff': 'application/font-woff',
'woff2': 'application/font-woff',
'otf': 'application/font-sfnt',
'otc': 'application/font-sfnt',
'ttf': 'application/font-sfnt'
};
var options = {
port: process.env.PM2_SERVE_PORT || process.argv[3] || 8080,
host: process.env.PM2_SERVE_HOST || process.argv[4] || '0.0.0.0',
path: path.resolve(process.env.PM2_SERVE_PATH || process.argv[2] || '.'),
spa: process.env.PM2_SERVE_SPA === 'true',
homepage: process.env.PM2_SERVE_HOMEPAGE || '/index.html',
basic_auth: process.env.PM2_SERVE_BASIC_AUTH === 'true' ? {
username: process.env.PM2_SERVE_BASIC_AUTH_USERNAME,
password: process.env.PM2_SERVE_BASIC_AUTH_PASSWORD
} : null,
monitor: process.env.PM2_SERVE_MONITOR
};
if (typeof options.port === 'string') {
options.port = parseInt(options.port) || 8080
}
if (typeof options.monitor === 'string' && options.monitor !== '') {
try {
let fileContent = fs.readFileSync(path.join(process.env.PM2_HOME, 'agent.json5')).toString()
// Handle old configuration with json5
fileContent = fileContent.replace(/\s(\w+):/g, '"$1":')
// parse
let conf = JSON.parse(fileContent)
options.monitorBucket = conf.public_key
} catch (e) {
console.log('Interaction file does not exist')
}
}
// start an HTTP server
http.createServer(function (request, response) {
if (options.basic_auth) {
if (!request.headers.authorization || request.headers.authorization.indexOf('Basic ') === -1) {
return sendBasicAuthResponse(response)
}
var user = parseBasicAuth(request.headers.authorization)
if (user.username !== options.basic_auth.username || user.password !== options.basic_auth.password) {
return sendBasicAuthResponse(response)
}
}
serveFile(request.url, request, response);
}).listen(options.port, options.host, function (err) {
if (err) {
console.error(err);
process.exit(1);
}
console.log('Exposing %s directory on %s:%d', options.path, options.host, options.port);
});
function serveFile(uri, request, response) {
var file = decodeURIComponent(url.parse(uri || request.url).pathname);
if (file === '/' || file === '') {
file = options.homepage;
request.wantHomepage = true;
}
var filePath = path.resolve(options.path + file);
// since we call filesystem directly so we need to verify that the
// url doesn't go outside the serve path
if (filePath.indexOf(options.path) !== 0) {
response.writeHead(403, { 'Content-Type': 'text/html' });
return response.end('403 Forbidden');
}
var contentType = contentTypes[filePath.split('.').pop().toLowerCase()] || 'text/plain';
fs.readFile(filePath, function (error, content) {
if (error) {
if ((!options.spa || file === options.homepage)) {
console.error('[%s] Error while serving %s with content-type %s : %s',
new Date(), filePath, contentType, error.message || error);
}
errorMeter.mark();
if (error.code === 'ENOENT') {
if (options.spa && !request.wantHomepage) {
request.wantHomepage = true;
return serveFile(`/${path.basename(file)}`, request, response);
} else if (options.spa && file !== options.homepage) {
return serveFile(options.homepage, request, response);
}
fs.readFile(options.path + '/404.html', function (err, content) {
content = err ? '404 Not Found' : content;
response.writeHead(404, { 'Content-Type': 'text/html' });
return response.end(content, 'utf-8');
});
return;
}
response.writeHead(500);
return response.end('Sorry, check with the site admin for error: ' + error.code + ' ..\n');
}
// Add CORS headers to allow browsers to fetch data directly
response.writeHead(200, {
'Content-Type': contentType,
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET'
});
if (options.monitorBucket && contentType === 'text/html') {
content = content.toString().replace('</body>', `
<script>
;(function (b,e,n,o,i,t) {
b[o]=b[o]||function(f){(b[o].c=b[o].c||[]).push(f)};
t=e.createElement(i);e=e.getElementsByTagName(i)[0];
t.async=1;t.src=n;e.parentNode.insertBefore(t,e);
}(window,document,'https://apm.pm2.io/pm2-io-apm-browser.v1.js','pm2Ready','script'))
pm2Ready(function(apm) {
apm.setBucket('${options.monitorBucket}')
apm.setApplication('${options.monitor}')
apm.reportTimings()
apm.reportIssues()
})
</script>
</body>
`);
}
response.end(content, 'utf-8');
debug('[%s] Serving %s with content-type %s', Date.now(), filePath, contentType);
});
}
function parseBasicAuth(auth) {
// auth is like `Basic Y2hhcmxlczoxMjM0NQ==`
var tmp = auth.split(' ');
var buf = Buffer.from(tmp[1], 'base64');
var plain = buf.toString();
var creds = plain.split(':');
return {
username: creds[0],
password: creds[1]
}
}
function sendBasicAuthResponse(response) {
response.writeHead(401, {
'Content-Type': 'text/html',
'WWW-Authenticate': 'Basic realm="Authentication service"'
});
return response.end('401 Unauthorized');
}

616
api.hyungi.net/node_modules/pm2/lib/API/Startup.js generated vendored Normal file
View File

@@ -0,0 +1,616 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
var chalk = require('chalk');
var path = require('path');
var fs = require('fs');
var forEachLimit = require('async/forEachLimit');
var eachLimit = require('async/eachLimit');
var Common = require('../Common.js');
var cst = require('../../constants.js');
var util = require('util');
var tmpPath = require('os').tmpdir;
var which = require('../tools/which.js');
var sexec = require('../tools/sexec')
module.exports = function(CLI) {
/**
* If command is launched without root right
* Display helper
*/
function isNotRoot(startup_mode, platform, opts, cb) {
Common.printOut(`${cst.PREFIX_MSG}To ${startup_mode} the Startup Script, copy/paste the following command:`);
if (opts.user) {
console.log('sudo env PATH=$PATH:' + path.dirname(process.execPath) + ' pm2 ' + opts.args[1].name() + ' ' + platform + ' -u ' + opts.user + ' --hp ' + process.env.HOME);
return cb(new Error('You have to run this with elevated rights'));
}
return sexec('whoami', {silent: true}, function(err, stdout, stderr) {
console.log('sudo env PATH=$PATH:' + path.dirname(process.execPath) + ' ' + require.main.filename + ' ' + opts.args[1].name() + ' ' + platform + ' -u ' + stdout.trim() + ' --hp ' + process.env.HOME);
return cb(new Error('You have to run this with elevated rights'));
});
}
/**
* Detect running init system
*/
function detectInitSystem() {
var hash_map = {
'systemctl' : 'systemd',
'update-rc.d': 'upstart',
'chkconfig' : 'systemv',
'rc-update' : 'openrc',
'launchctl' : 'launchd',
'sysrc' : 'rcd',
'rcctl' : 'rcd-openbsd',
'svcadm' : 'smf'
};
var init_systems = Object.keys(hash_map);
for (var i = 0; i < init_systems.length; i++) {
if (which(init_systems[i]) != null) {
break;
}
}
if (i >= init_systems.length) {
Common.printError(cst.PREFIX_MSG_ERR + 'Init system not found');
return null;
}
Common.printOut(cst.PREFIX_MSG + 'Init System found: ' + chalk.bold(hash_map[init_systems[i]]));
return hash_map[init_systems[i]];
}
CLI.prototype.uninstallStartup = function(platform, opts, cb) {
var commands;
var that = this;
var actual_platform = detectInitSystem();
var user = opts.user || process.env.USER || process.env.LOGNAME; // Use LOGNAME on Solaris-like systems
var service_name = (opts.serviceName || 'pm2-' + user);
var openrc_service_name = 'pm2';
var launchd_service_name = (opts.serviceName || 'pm2.' + user);
if (!platform)
platform = actual_platform;
else if (actual_platform && actual_platform !== platform) {
Common.printOut('-----------------------------------------------------------')
Common.printOut(' PM2 detected ' + actual_platform + ' but you precised ' + platform)
Common.printOut(' Please verify that your choice is indeed your init system')
Common.printOut(' If you arent sure, just run : pm2 startup')
Common.printOut('-----------------------------------------------------------')
}
if (platform === null)
throw new Error('Init system not found')
if (!cb) {
cb = function(err, data) {
if (err)
return that.exitCli(cst.ERROR_EXIT);
return that.exitCli(cst.SUCCESS_EXIT);
}
}
if (process.getuid() != 0) {
return isNotRoot('unsetup', platform, opts, cb);
}
if (fs.existsSync('/etc/init.d/pm2-init.sh')) {
platform = 'oldsystem';
}
switch(platform) {
case 'systemd':
commands = [
'systemctl stop ' + service_name,
'systemctl disable ' + service_name,
'rm /etc/systemd/system/' + service_name + '.service'
];
break;
case 'systemv':
commands = [
'chkconfig ' + service_name + ' off',
'rm /etc/init.d/' + service_name
];
break;
case 'oldsystem':
Common.printOut(cst.PREFIX_MSG + 'Disabling and deleting old startup system');
commands = [
'update-rc.d pm2-init.sh disable',
'update-rc.d -f pm2-init.sh remove',
'rm /etc/init.d/pm2-init.sh'
];
break;
case 'openrc':
service_name = openrc_service_name;
commands = [
'/etc/init.d/' + service_name + ' stop',
'rc-update delete ' + service_name + ' default',
'rm /etc/init.d/' + service_name
];
break;
case 'upstart':
commands = [
'update-rc.d ' + service_name + ' disable',
'update-rc.d -f ' + service_name + ' remove',
'rm /etc/init.d/' + service_name
];
break;
case 'launchd':
var destination = path.join(process.env.HOME, 'Library/LaunchAgents/' + launchd_service_name + '.plist');
commands = [
'launchctl remove ' + launchd_service_name + ' || true',
'rm ' + destination
];
break;
case 'rcd':
service_name = (opts.serviceName || 'pm2_' + user);
commands = [
'/usr/local/etc/rc.d/' + service_name + ' stop',
'sysrc -x ' + service_name + '_enable',
'rm /usr/local/etc/rc.d/' + service_name
];
break;
case 'rcd-openbsd':
service_name = (opts.serviceName || 'pm2_' + user);
var destination = path.join('/etc/rc.d', service_name);
commands = [
'rcctl stop ' + service_name,
'rcctl disable ' + service_name,
'rm ' + destination
];
break;
case 'smf':
service_name = (opts.serviceName || 'pm2_' + user);
commands = [
'svcadm disable ' + service_name,
'svccfg delete -f ' + service_name
]
};
sexec(commands.join('&& '), function(code, stdout, stderr) {
Common.printOut(stdout);
Common.printOut(stderr);
if (code == 0) {
Common.printOut(cst.PREFIX_MSG + chalk.bold('Init file disabled.'));
} else {
Common.printOut(cst.ERROR_MSG + chalk.bold('Return code : ' + code));
}
cb(null, {
commands : commands,
platform : platform
});
});
};
/**
* Startup script generation
* @method startup
* @param {string} platform type (centos|redhat|amazon|gentoo|systemd|smf)
*/
CLI.prototype.startup = function(platform, opts, cb) {
var that = this;
var actual_platform = detectInitSystem();
var user = (opts.user || process.env.USER || process.env.LOGNAME); // Use LOGNAME on Solaris-like systems
var service_name = (opts.serviceName || 'pm2-' + user);
var openrc_service_name = 'pm2';
var launchd_service_name = (opts.serviceName || 'pm2.' + user);
if (!platform)
platform = actual_platform;
else if (actual_platform && actual_platform !== platform) {
Common.printOut('-----------------------------------------------------------')
Common.printOut(' PM2 detected ' + actual_platform + ' but you precised ' + platform)
Common.printOut(' Please verify that your choice is indeed your init system')
Common.printOut(' If you arent sure, just run : pm2 startup')
Common.printOut('-----------------------------------------------------------')
}
if (platform == null)
throw new Error('Init system not found');
if (!cb) {
cb = function(err, data) {
if (err)
return that.exitCli(cst.ERROR_EXIT);
return that.exitCli(cst.SUCCESS_EXIT);
}
}
if (process.getuid() != 0) {
return isNotRoot('setup', platform, opts, cb);
}
var destination;
var commands;
var template;
function getTemplate(type) {
return fs.readFileSync(path.join(__dirname, '..', 'templates/init-scripts', type + '.tpl'), {encoding: 'utf8'});
}
switch(platform) {
case 'ubuntu':
case 'centos':
case 'arch':
case 'oracle':
case 'systemd':
if (opts.waitIp)
template = getTemplate('systemd-online');
else
template = getTemplate('systemd');
destination = '/etc/systemd/system/' + service_name + '.service';
commands = [
'systemctl enable ' + service_name
];
break;
case 'ubuntu14':
case 'ubuntu12':
case 'upstart':
template = getTemplate('upstart');
destination = '/etc/init.d/' + service_name;
commands = [
'chmod +x ' + destination,
'mkdir -p /var/lock/subsys',
'touch /var/lock/subsys/' + service_name,
'update-rc.d ' + service_name + ' defaults'
];
break;
case 'systemv':
case 'amazon':
case 'centos6':
template = getTemplate('upstart');
destination = '/etc/init.d/' + service_name;
commands = [
'chmod +x ' + destination,
'mkdir -p /var/lock/subsys',
'touch /var/lock/subsys/' + service_name,
'chkconfig --add ' + service_name,
'chkconfig ' + service_name + ' on',
'initctl list'
];
break;
case 'macos':
case 'darwin':
case 'launchd':
template = getTemplate('launchd');
destination = path.join(process.env.HOME, 'Library/LaunchAgents/' + launchd_service_name + '.plist');
commands = [
'mkdir -p ' + path.join(process.env.HOME, 'Library/LaunchAgents'),
'launchctl load -w ' + destination
]
break;
case 'freebsd':
case 'rcd':
template = getTemplate('rcd');
service_name = (opts.serviceName || 'pm2_' + user);
destination = '/usr/local/etc/rc.d/' + service_name;
commands = [
'chmod 755 ' + destination,
'sysrc ' + service_name + '_enable=YES'
];
break;
case 'openbsd':
case 'rcd-openbsd':
template = getTemplate('rcd-openbsd');
service_name = (opts.serviceName || 'pm2_' + user);
destination = path.join('/etc/rc.d/', service_name);
commands = [
'chmod 755 ' + destination,
'rcctl enable ' + service_name,
'rcctl start ' + service_name
];
break;
case 'openrc':
template = getTemplate('openrc');
service_name = openrc_service_name;
destination = '/etc/init.d/' + service_name;
commands = [
'chmod +x ' + destination,
'rc-update add ' + service_name + ' default'
];
break;
case 'smf':
case 'sunos':
case 'solaris':
template = getTemplate('smf');
service_name = (opts.serviceName || 'pm2_' + user);
destination = path.join(tmpPath(), service_name + '.xml');
commands = [
'svccfg import ' + destination,
'svcadm enable ' + service_name
];
break;
default:
throw new Error('Unknown platform / init system name');
}
/**
* 4# Replace template variable value
*/
var envPath
if (cst.HAS_NODE_EMBEDDED == true)
envPath = util.format('%s:%s', process.env.PATH || '', path.dirname(process.execPath))
else if (new RegExp(path.dirname(process.execPath)).test(process.env.PATH))
envPath = process.env.PATH
else
envPath = util.format('%s:%s', process.env.PATH || '', path.dirname(process.execPath))
template = template.replace(/%PM2_PATH%/g, process.mainModule.filename)
.replace(/%NODE_PATH%/g, envPath)
.replace(/%USER%/g, user)
.replace(/%HOME_PATH%/g, opts.hp ? path.resolve(opts.hp, '.pm2') : cst.PM2_ROOT_PATH)
.replace(/%SERVICE_NAME%/g, service_name);
Common.printOut(chalk.bold('Platform'), platform);
Common.printOut(chalk.bold('Template'));
Common.printOut(template);
Common.printOut(chalk.bold('Target path'));
Common.printOut(destination);
Common.printOut(chalk.bold('Command list'));
Common.printOut(commands);
Common.printOut(cst.PREFIX_MSG + 'Writing init configuration in ' + destination);
try {
fs.writeFileSync(destination, template);
} catch (e) {
console.error(cst.PREFIX_MSG_ERR + 'Failure when trying to write startup script');
console.error(e.message || e);
return cb(e);
}
Common.printOut(cst.PREFIX_MSG + 'Making script booting at startup...');
forEachLimit(commands, 1, function(command, next) {
Common.printOut(cst.PREFIX_MSG + '[-] Executing: %s...', chalk.bold(command));
sexec(command, function(code, stdout, stderr) {
if (code === 0) {
Common.printOut(cst.PREFIX_MSG + chalk.bold('[v] Command successfully executed.'));
return next();
} else {
Common.printOut(chalk.red('[ERROR] Exit code : ' + code))
return next(new Error(command + ' failed, see error above.'));
}
})
}, function(err) {
if (err) {
console.error(cst.PREFIX_MSG_ERR + (err.message || err));
return cb(err);
}
Common.printOut(chalk.bold.blue('+---------------------------------------+'));
Common.printOut(chalk.bold.blue((cst.PREFIX_MSG + 'Freeze a process list on reboot via:' )));
Common.printOut(chalk.bold('$ pm2 save'));
Common.printOut('');
Common.printOut(chalk.bold.blue(cst.PREFIX_MSG + 'Remove init script via:'));
Common.printOut(chalk.bold('$ pm2 unstartup ' + platform));
return cb(null, {
destination : destination,
template : template
});
});
};
/**
* DISABLED FEATURE
* KEEPING METHOD FOR BACKWARD COMPAT
*/
CLI.prototype.autodump = function(cb) {
return cb()
}
/**
* Dump current processes managed by pm2 into DUMP_FILE_PATH file
* @method dump
* @param {} cb
* @return
*/
CLI.prototype.dump = function(force, cb) {
var env_arr = [];
var that = this;
if (typeof(force) === 'function') {
cb = force
force = false
}
if (!cb)
Common.printOut(cst.PREFIX_MSG + 'Saving current process list...');
that.Client.executeRemote('getMonitorData', {}, function(err, list) {
if (err) {
Common.printError('Error retrieving process list: ' + err);
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
}
/**
* Description
* @method fin
* @param {} err
* @return
*/
function fin(err) {
// try to fix issues with empty dump file
// like #3485
if (!force && env_arr.length === 0 && !process.env.FORCE) {
// fix : if no dump file, no process, only module and after pm2 update
if (!fs.existsSync(cst.DUMP_FILE_PATH)) {
that.clearDump(function(){});
}
// if no process in list don't modify dump file
// process list should not be empty
if (cb) {
return cb(new Error('Process list empty, cannot save empty list'));
} else {
Common.printOut(cst.PREFIX_MSG_WARNING + 'PM2 is not managing any process, skipping save...');
Common.printOut(cst.PREFIX_MSG_WARNING + 'To force saving use: pm2 save --force');
that.exitCli(cst.SUCCESS_EXIT);
return;
}
}
// Back up dump file
try {
if (fs.existsSync(cst.DUMP_FILE_PATH)) {
fs.writeFileSync(cst.DUMP_BACKUP_FILE_PATH, fs.readFileSync(cst.DUMP_FILE_PATH));
}
} catch (e) {
console.error(e.stack || e);
Common.printOut(cst.PREFIX_MSG_ERR + 'Failed to back up dump file in %s', cst.DUMP_BACKUP_FILE_PATH);
}
// Overwrite dump file, delete if broken and exit
try {
fs.writeFileSync(cst.DUMP_FILE_PATH, JSON.stringify(env_arr, '', 2));
} catch (e) {
console.error(e.stack || e);
try {
// try to backup file
if (fs.existsSync(cst.DUMP_BACKUP_FILE_PATH)) {
fs.writeFileSync(cst.DUMP_FILE_PATH, fs.readFileSync(cst.DUMP_BACKUP_FILE_PATH));
}
} catch (e) {
// don't keep broken file
fs.unlinkSync(cst.DUMP_FILE_PATH);
console.error(e.stack || e);
}
Common.printOut(cst.PREFIX_MSG_ERR + 'Failed to save dump file in %s', cst.DUMP_FILE_PATH);
return that.exitCli(cst.ERROR_EXIT);
}
if (cb) return cb(null, {success:true});
Common.printOut(cst.PREFIX_MSG + 'Successfully saved in %s', cst.DUMP_FILE_PATH);
return that.exitCli(cst.SUCCESS_EXIT);
}
(function ex(apps) {
if (!apps[0]) return fin(null);
delete apps[0].pm2_env.instances;
delete apps[0].pm2_env.pm_id;
delete apps[0].pm2_env.prev_restart_delay;
if (!apps[0].pm2_env.pmx_module)
env_arr.push(apps[0].pm2_env);
apps.shift();
return ex(apps);
})(list);
});
};
/**
* Remove DUMP_FILE_PATH file and DUMP_BACKUP_FILE_PATH file
* @method dump
* @param {} cb
* @return
*/
CLI.prototype.clearDump = function(cb) {
fs.writeFileSync(cst.DUMP_FILE_PATH, JSON.stringify([]));
if(cb && typeof cb === 'function') return cb();
Common.printOut(cst.PREFIX_MSG + 'Successfully created %s', cst.DUMP_FILE_PATH);
return this.exitCli(cst.SUCCESS_EXIT);
};
/**
* Resurrect processes
* @method resurrect
* @param {} cb
* @return
*/
CLI.prototype.resurrect = function(cb) {
var apps = {};
var that = this;
var processes;
function readDumpFile(dumpFilePath) {
Common.printOut(cst.PREFIX_MSG + 'Restoring processes located in %s', dumpFilePath);
try {
var apps = fs.readFileSync(dumpFilePath);
} catch (e) {
Common.printError(cst.PREFIX_MSG_ERR + 'Failed to read dump file in %s', dumpFilePath);
throw e;
}
return apps;
}
function parseDumpFile(dumpFilePath, apps) {
try {
var processes = Common.parseConfig(apps, 'none');
} catch (e) {
Common.printError(cst.PREFIX_MSG_ERR + 'Failed to parse dump file in %s', dumpFilePath);
try {
fs.unlinkSync(dumpFilePath);
} catch (e) {
console.error(e.stack || e);
}
throw e;
}
return processes;
}
// Read dump file, fall back to backup, delete if broken
try {
apps = readDumpFile(cst.DUMP_FILE_PATH);
processes = parseDumpFile(cst.DUMP_FILE_PATH, apps);
} catch(e) {
try {
apps = readDumpFile(cst.DUMP_BACKUP_FILE_PATH);
processes = parseDumpFile(cst.DUMP_BACKUP_FILE_PATH, apps);
} catch(e) {
Common.printError(cst.PREFIX_MSG_ERR + 'No processes saved; DUMP file doesn\'t exist');
// if (cb) return cb(Common.retErr(e));
// else return that.exitCli(cst.ERROR_EXIT);
return that.speedList();
}
}
that.Client.executeRemote('getMonitorData', {}, function(err, list) {
if (err) {
Common.printError(err);
return that.exitCli(1);
}
var current = [];
var target = [];
list.forEach(function(app) {
if (!current[app.name])
current[app.name] = 0;
current[app.name]++;
});
processes.forEach(function(app) {
if (!target[app.name])
target[app.name] = 0;
target[app.name]++;
});
var tostart = Object.keys(target).filter(function(i) {
return Object.keys(current).indexOf(i) < 0;
})
eachLimit(processes, cst.CONCURRENT_ACTIONS, function(app, next) {
if (tostart.indexOf(app.name) == -1)
return next();
that.Client.executeRemote('prepare', app, function(err, dt) {
if (err)
Common.printError(err);
else
Common.printOut(cst.PREFIX_MSG + 'Process %s restored', app.pm_exec_path);
next();
});
}, function(err) {
return cb ? cb(null, apps) : that.speedList();
});
});
};
}

213
api.hyungi.net/node_modules/pm2/lib/API/UX/helpers.js generated vendored Normal file
View File

@@ -0,0 +1,213 @@
const chalk = require('chalk')
const Helpers = {}
/**
* Converts Byte to Human readable size
* @method bytesToSize
* @param {} bytes
* @param {} precision
* @return
*/
Helpers.bytesToSize = function(bytes, precision) {
var kilobyte = 1024
var megabyte = kilobyte * 1024
var gigabyte = megabyte * 1024
var terabyte = gigabyte * 1024
if ((bytes >= 0) && (bytes < kilobyte)) {
return bytes + 'b '
} else if ((bytes >= kilobyte) && (bytes < megabyte)) {
return (bytes / kilobyte).toFixed(precision) + 'kb '
} else if ((bytes >= megabyte) && (bytes < gigabyte)) {
return (bytes / megabyte).toFixed(precision) + 'mb '
} else if ((bytes >= gigabyte) && (bytes < terabyte)) {
return (bytes / gigabyte).toFixed(precision) + 'gb '
} else if (bytes >= terabyte) {
return (bytes / terabyte).toFixed(precision) + 'tb '
} else {
return bytes + 'b '
}
}
/**
* Color Process state
* @method colorStatus
* @param {} status
* @return
*/
Helpers.colorStatus = function(status) {
switch (status) {
case 'online':
return chalk.green.bold('online')
break
case 'running':
return chalk.green.bold('online')
break
case 'restarting':
return chalk.yellow.bold('restart')
break
case 'created':
return chalk.yellow.bold('created')
break
case 'launching':
return chalk.blue.bold('launching')
break
default:
return chalk.red.bold(status)
}
}
/**
* Safe Push
*/
Helpers.safe_push = function() {
var argv = arguments
var table = argv[0]
for (var i = 1; i < argv.length; ++i) {
var elem = argv[i]
if (elem[Object.keys(elem)[0]] === undefined
|| elem[Object.keys(elem)[0]] === null) {
elem[Object.keys(elem)[0]] = 'N/A'
}
else if (Array.isArray(elem[Object.keys(elem)[0]])) {
elem[Object.keys(elem)[0]].forEach(function(curr, j) {
if (curr === undefined || curr === null)
elem[Object.keys(elem)[0]][j] = 'N/A'
})
}
table.push(elem)
}
}
/**
* Description
* @method timeSince
* @param {} date
* @return BinaryExpression
*/
Helpers.timeSince = function(date) {
var seconds = Math.floor((new Date() - date) / 1000)
var interval = Math.floor(seconds / 31536000)
if (interval > 1) {
return interval + 'Y'
}
interval = Math.floor(seconds / 2592000)
if (interval > 1) {
return interval + 'M'
}
interval = Math.floor(seconds / 86400)
if (interval > 1) {
return interval + 'D'
}
interval = Math.floor(seconds / 3600)
if (interval > 1) {
return interval + 'h'
}
interval = Math.floor(seconds / 60)
if (interval > 1) {
return interval + 'm'
}
return Math.floor(seconds) + 's'
}
/**
* Colorize Metrics
*
* @param {Number} value current value
* @param {Number} warn value threshold
* @param {Number} alert value threshold
* @param {String} prefix value prefix
* @return {String} value
*/
Helpers.colorizedMetric = function(value, warn, alert, prefix) {
var inverted = false
if (alert < warn)
inverted = true
if (!prefix) prefix = ''
if (isNaN(value) === true)
return 'N/A'
if (value == 0)
return 0 + prefix
if (inverted == true) {
if (value > warn)
return chalk.green(value + prefix)
if (value <= warn && value >= alert)
return chalk.bold.yellow(value + prefix)
return chalk.bold.red(value + prefix)
}
if (value < warn)
return chalk.green(value + prefix)
if (value >= warn && value <= alert)
return chalk.bold.yellow(value + prefix)
return chalk.bold.red(value + prefix)
}
/**
* Get nested property
*
* @param {String} propertyName
* @param {Object} obj
* @returns {String} property value
*/
Helpers.getNestedProperty = function(propertyName, obj) {
var parts = propertyName.split('.'),
length = parts.length,
property = obj || {}
for (var i = 0; i < length; i++ ) {
property = property[parts[i]]
}
return property
}
Helpers.openEditor = function (file, opts, cb) {
var spawn = require('child_process').spawn
if (typeof opts === 'function') {
cb = opts
opts = {}
}
if (!opts) opts = {}
var ed = /^win/.test(process.platform) ? 'notepad' : 'vim'
var editor = opts.editor || process.env.VISUAL || process.env.EDITOR || ed
var args = editor.split(/\s+/)
var bin = args.shift()
var ps = spawn(bin, args.concat([ file ]), {
windowsHide: true,
stdio: 'inherit'
})
ps.on('exit', function (code, sig) {
if (typeof cb === 'function') cb(code, sig)
})
}
Helpers.dispKeys = function(kv, target_module) {
Object.keys(kv).forEach(function(key) {
if (target_module != null && target_module != key)
return false
if (typeof(kv[key]) == 'object') {
var obj = {}
console.log(chalk.bold('Module: ') + chalk.bold.blue(key))
Object.keys(kv[key]).forEach(function(sub_key) {
console.log(`$ pm2 set ${key}:${sub_key} ${kv[key][sub_key]}`)
})
}
})
}
module.exports = Helpers

9
api.hyungi.net/node_modules/pm2/lib/API/UX/index.js generated vendored Normal file
View File

@@ -0,0 +1,9 @@
const UX = {
helpers: require('./helpers.js'),
describe: require('./pm2-describe.js'),
list: require('./pm2-ls.js'),
list_min: require('./pm2-ls-minimal.js')
}
module.exports = UX

View File

@@ -0,0 +1,193 @@
const Table = require('cli-tableau')
const chalk = require('chalk')
const UxHelpers = require('./helpers.js')
const Common = require('../../Common.js')
var postModuleInfos = function(module_name, human_info) {
var table = new Table({
style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true}
})
var disp = {}
human_info.unshift(['Module name', module_name])
human_info.forEach(function(info) {
var obj = {}
obj[chalk.bold.cyan(info[0])] = info[1]
table.push(obj)
})
console.log()
console.log(chalk.bold.inverse(' Module %s infos '), module_name)
console.log(table.toString())
}
/**
* Description
* @method describeTable
* @param {Object} proc process list
*/
module.exports = function(proc) {
var table = new Table({
style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true}
})
var pm2_env = proc.pm2_env
var created_at = 'N/A'
if (pm2_env.axm_options && pm2_env.axm_options.human_info) {
postModuleInfos(pm2_env.name, pm2_env.axm_options.human_info)
}
try {
if (pm2_env.created_at != null)
created_at = new Date(pm2_env.created_at).toISOString()
} catch (e) {
}
console.log(chalk.bold.inverse(' Describing process with id %d - name %s '), pm2_env.pm_id, pm2_env.name)
UxHelpers.safe_push(table,
{ 'status' : UxHelpers.colorStatus(pm2_env.status) },
{ 'name': pm2_env.name },
{ 'namespace': pm2_env.namespace },
{ 'version': pm2_env.version },
{ 'restarts' : pm2_env.restart_time },
{ 'uptime' : (pm2_env.pm_uptime && pm2_env.status == 'online') ? UxHelpers.timeSince(pm2_env.pm_uptime) : 0 },
{ 'script path' : pm2_env.pm_exec_path },
{ 'script args' : pm2_env.args ? (typeof pm2_env.args == 'string' ? JSON.parse(pm2_env.args.replace(/'/g, '"')):pm2_env.args).join(' ') : null },
{ 'error log path' : pm2_env.pm_err_log_path },
{ 'out log path' : pm2_env.pm_out_log_path },
{ 'pid path' : pm2_env.pm_pid_path },
{ 'interpreter' : pm2_env.exec_interpreter },
{ 'interpreter args' : pm2_env.node_args.length != 0 ? pm2_env.node_args : null },
{ 'script id' : pm2_env.pm_id },
{ 'exec cwd' : pm2_env.pm_cwd },
{ 'exec mode' : pm2_env.exec_mode },
{ 'node.js version' : pm2_env.node_version },
{ 'node env': pm2_env.env.NODE_ENV },
{ 'watch & reload' : pm2_env.watch ? chalk.green.bold('✔') : '✘' },
{ 'unstable restarts' : pm2_env.unstable_restarts },
{ 'created at' : created_at }
)
if ('pm_log_path' in pm2_env){
table.splice(6, 0, {'entire log path': pm2_env.pm_log_path})
}
if ('cron_restart' in pm2_env){
table.splice(5, 0, {'cron restart': pm2_env.cron_restart})
}
console.log(table.toString())
/**
* Module conf display
*/
if (pm2_env.axm_options &&
pm2_env.axm_options.module_conf &&
Object.keys(pm2_env.axm_options.module_conf).length > 0) {
var table_conf = new Table({
style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true}
})
console.log('Process configuration')
Object.keys(pm2_env.axm_options.module_conf).forEach(function(key) {
var tmp = {}
tmp[key] = pm2_env.axm_options.module_conf[key]
UxHelpers.safe_push(table_conf, tmp)
})
console.log(table_conf.toString())
}
/**
* Versioning metadata
*/
if (pm2_env.versioning) {
var table2 = new Table({
style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true}
})
console.log(chalk.inverse.bold(' Revision control metadata '))
UxHelpers.safe_push(table2,
{ 'revision control' : pm2_env.versioning.type },
{ 'remote url' : pm2_env.versioning.url },
{ 'repository root' : pm2_env.versioning.repo_path },
{ 'last update' : pm2_env.versioning.update_time },
{ 'revision' : pm2_env.versioning.revision },
{ 'comment' : pm2_env.versioning.comment ? pm2_env.versioning.comment.trim().slice(0, 60) : '' },
{ 'branch' : pm2_env.versioning.branch }
)
console.log(table2.toString())
}
if (pm2_env.axm_actions && Object.keys(pm2_env.axm_actions).length > 0) {
var table_actions = new Table({
style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true}
})
console.log(chalk.inverse.bold(' Actions available '))
pm2_env.axm_actions.forEach(function(action_set) {
UxHelpers.safe_push(table_actions, [action_set.action_name])
})
console.log(table_actions.toString())
Common.printOut(chalk.white.italic(' Trigger via: pm2 trigger %s <action_name>\n'), pm2_env.name)
}
if (pm2_env.axm_monitor && Object.keys(pm2_env.axm_monitor).length > 0) {
var table_probes = new Table({
style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true}
})
console.log(chalk.inverse.bold(' Code metrics value '))
Object.keys(pm2_env.axm_monitor).forEach(function(key) {
var obj = {}
var metric_name = pm2_env.axm_monitor[key].hasOwnProperty("value") ? pm2_env.axm_monitor[key].value : pm2_env.axm_monitor[key]
var metric_unit = pm2_env.axm_monitor[key].hasOwnProperty("unit") ? pm2_env.axm_monitor[key].unit : ''
var value = `${metric_name} ${metric_unit}`
obj[key] = value
UxHelpers.safe_push(table_probes, obj)
})
console.log(table_probes.toString())
}
var table_env = new Table({
style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true}
})
console.log(chalk.inverse.bold(' Divergent env variables from local env '))
var _env = Common.safeExtend({}, pm2_env)
var diff_env = {}
Object.keys(process.env).forEach(k => {
if (!_env[k] || _env[k] != process.env[k]) {
diff_env[k] = process.env[k]
}
})
Object.keys(diff_env).forEach(function(key) {
var obj = {}
if (_env[key]) {
// 1. fix env value is not a String and slice is undeinfed
// 2. fix process.stdout.columns is undefined and causes empty string output
// 3. columns defaults to 300 - same as specified in pm2-ls
obj[key] = String(_env[key]).slice(0, (process.stdout.columns || 300) - 60)
UxHelpers.safe_push(table_env, obj)
}
})
console.log(table_env.toString())
console.log()
Common.printOut(chalk.white.italic(' Add your own code metrics: http://bit.ly/code-metrics'))
Common.printOut(chalk.white.italic(' Use `pm2 logs %s [--lines 1000]` to display logs'), pm2_env.name)
Common.printOut(chalk.white.italic(' Use `pm2 env %s` to display environment variables'), pm2_env.pm_id)
Common.printOut(chalk.white.italic(' Use `pm2 monit` to monitor CPU and Memory usage'), pm2_env.name)
}

View File

@@ -0,0 +1,31 @@
const UxHelpers = require('./helpers.js')
const p = require('path')
/**
* Minimal display via pm2 ls -m
* @method miniDisplay
* @param {Object} list process list
*/
module.exports = function(list) {
list.forEach(function(l) {
var mode = l.pm2_env.exec_mode.split('_mode')[0]
var status = l.pm2_env.status
var key = l.pm2_env.name || p.basename(l.pm2_env.pm_exec_path.script)
console.log('+--- %s', key)
console.log('namespace : %s', l.pm2_env.namespace)
console.log('version : %s', l.pm2_env.version)
console.log('pid : %s', l.pid)
console.log('pm2 id : %s', l.pm2_env.pm_id)
console.log('status : %s', status)
console.log('mode : %s', mode)
console.log('restarted : %d', l.pm2_env.restart_time ? l.pm2_env.restart_time : 0)
console.log('uptime : %s', (l.pm2_env.pm_uptime && status == 'online') ? UxHelpers.timeSince(l.pm2_env.pm_uptime) : 0)
console.log('memory usage : %s', l.monit ? UxHelpers.bytesToSize(l.monit.memory, 1) : '')
console.log('error log : %s', l.pm2_env.pm_err_log_path)
console.log('watching : %s', l.pm2_env.watch ? 'yes' : 'no')
console.log('PID file : %s\n', l.pm2_env.pm_pid_path)
})
}

482
api.hyungi.net/node_modules/pm2/lib/API/UX/pm2-ls.js generated vendored Normal file
View File

@@ -0,0 +1,482 @@
const cst = require('../../../constants')
const Common = require('../../Common')
const Configuration = require('../../Configuration')
const UxHelpers = require('./helpers.js')
const chalk = require('chalk')
const Table = require('cli-tableau')
const Passwd = require('../../tools/passwd.js')
const List = {}
const CONDENSED_MODE = (process.stdout.columns || 300) < 134
/**
* Check if dump file contains same apps that the one managed by PM2
*/
function checkIfProcessAreDumped(list) {
try {
var dump_raw = require('fs').readFileSync(cst.DUMP_FILE_PATH)
var dump = JSON.parse(dump_raw)
var apps_dumped = dump.map(proc => proc.name)
var apps_running = list
.filter(proc => proc.pm2_env.pmx_module != true)
.map(proc => proc.name)
var diff = apps_dumped.filter(a => !apps_running.includes(a))
if (diff.length > 0) {
Common.warn(`Current process list is not synchronized with saved list. App ${chalk.bold(diff.join(' '))} differs. Type 'pm2 save' to synchronize.`)
}
else if (apps_dumped.length != apps_running.length) {
Common.warn(`Current process list is not synchronized with saved list. Type 'pm2 save' to synchronize.`)
}
} catch(e) {
}
}
var proc_id = 0
/**
* List Applications and Modules managed by PM2
*/
function listModulesAndAppsManaged(list, commander) {
var name_col_size = 11
if (list && list.length > 0)
name_col_size = (list.reduce((p, c) => (p.name.length > c.name.length) ? p : c)).name.length + 5
var id_width = Math.max(
2 + (Math.max(...list.map((l) => String(l.pm2_env.pm_id || 0).length)) || 0),
4
);
var app_head = {
id: id_width,
name: name_col_size,
namespace: 13,
version: 9,
mode: 9,
pid: 10,
uptime: 8,
'↺': 6,
status: 11,
cpu: 10,
mem: 10,
user: 10,
watching: 10
}
var mod_head = {
id: id_width,
module: 30,
version: 15,
pid: 10,
status: 10,
'↺': 6,
cpu: 10,
mem: 10,
user: 10
}
if (CONDENSED_MODE) {
app_head = {
id: id_width,
name: 20,
mode: 10,
'↺': 6,
status: 11,
cpu: 10,
memory: 10
}
mod_head = {
id: id_width,
name: 20,
status: 10,
cpu: 10,
mem: 10
}
}
var app_table = new Table({
head : Object.keys(app_head),
colWidths: Object.keys(app_head).map(k => app_head[k]),
colAligns : ['left'],
style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true}
})
var module_table = new Table({
head : Object.keys(mod_head),
colWidths: Object.keys(mod_head).map(k => mod_head[k]),
colAligns : ['left'],
style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true}
})
var sortField = 'name', sortOrder = 'asc', sort,
fields = {
name: 'pm2_env.name',
namespace: 'pm2_env.namespace',
pid: 'pid',
id: 'pm_id',
cpu: 'monit.cpu',
memory: 'monit.memory',
uptime: 'pm2_env.pm_uptime',
status: 'pm2_env.status'
}
if (commander && commander.sort) {
sort = commander.sort.split(':');
if(fields[sort[0].toLowerCase()]) {
sortField = sort[0].toLowerCase();
sortOrder = sort.length === 2 ? sort[1] : 'asc';
}
}
list.sort(function(a, b) {
var fieldA = UxHelpers.getNestedProperty(fields[sortField], a)
var fieldB = UxHelpers.getNestedProperty(fields[sortField], b)
if (sortOrder === 'desc') {
if (fieldA > fieldB)
return -1
if (fieldA < fieldB)
return 1
} else {
if (fieldA < fieldB)
return -1
if (fieldA > fieldB)
return 1
}
return 0
})
list.forEach(function(l) {
var obj = {}
if (l.pm2_env.pm_id > proc_id) {
proc_id = l.pm2_env.pm_id
}
var mode = l.pm2_env.exec_mode
var status = l.pm2_env.status
var key = l.pm2_env.pm_id
key = chalk.bold.cyan(key)
if (l.pm2_env.axm_options) {
var is_tracing_enabled = false
if (l.pm2_env.axm_options.tracing &&
typeof(l.pm2_env.axm_options.tracing) == 'boolean' &&
l.pm2_env.axm_options.tracing == true)
is_tracing_enabled = true
if (l.pm2_env.axm_options.tracing &&
l.pm2_env.axm_options.tracing.enabled &&
typeof(l.pm2_env.axm_options.tracing.enabled) == 'boolean' &&
l.pm2_env.axm_options.tracing.enabled == true)
is_tracing_enabled = true
if (is_tracing_enabled == true)
l.pm2_env.name = chalk.green('☵') + ' ' + l.pm2_env.name
if (l.pm2_env._km_monitored)
l.pm2_env.name = chalk.bold.green('◉') + ' ' + l.pm2_env.name
}
if (l.pm2_env.pmx_module == true) {
if (l.pm2_env.name == 'pm2-sysmonit') return
// pm2 ls for Modules
obj[key] = []
obj[key].push(l.name)
// Module version + PID
if (!CONDENSED_MODE) {
var pid = l.pm2_env.axm_options.pid ? l.pm2_env.axm_options.pid : l.pid
obj[key].push(l.pm2_env.version || 'N/A', pid)
}
// Status
obj[key].push(UxHelpers.colorStatus(status))
// Restart
if (!CONDENSED_MODE)
obj[key].push(l.pm2_env.restart_time ? l.pm2_env.restart_time : 0)
// CPU + Memory
obj[key].push(l.monit ? (l.monit.cpu + '%') : 'N/A', l.monit ? UxHelpers.bytesToSize(l.monit.memory, 1) : 'N/A' )
// User
if (!CONDENSED_MODE) {
if (l.pm2_env.uid && typeof(l.pm2_env.uid) == 'number') {
// Resolve user id to username
let users = Passwd.getUsers()
Object.keys(users).forEach(function(username) {
var user = users[username]
if (user.userId == l.pm2_env.uid) {
l.pm2_env.uid = user.username
}
})
}
obj[key].push(chalk.bold(l.pm2_env.uid || l.pm2_env.username))
}
UxHelpers.safe_push(module_table, obj)
}
else {
// pm2 ls for Applications
obj[key] = []
// PM2 ID
obj[key].push(l.pm2_env.name)
// Namespace
if (!CONDENSED_MODE)
obj[key].push(l.pm2_env.namespace)
// Version
if (!CONDENSED_MODE)
obj[key].push(l.pm2_env.version)
// Exec mode
obj[key].push(mode == 'fork_mode' ? chalk.inverse.bold('fork') : chalk.blue.bold('cluster'))
// PID
if (!CONDENSED_MODE)
obj[key].push(l.pid)
// Uptime
if (!CONDENSED_MODE)
obj[key].push((l.pm2_env.pm_uptime && status == 'online') ? UxHelpers.timeSince(l.pm2_env.pm_uptime) : 0)
// Restart
obj[key].push(l.pm2_env.restart_time ? l.pm2_env.restart_time : 0)
// Status
obj[key].push(UxHelpers.colorStatus(status))
// CPU
obj[key].push(l.monit ? l.monit.cpu + '%' : 'N/A')
// Memory
obj[key].push(l.monit ? UxHelpers.bytesToSize(l.monit.memory, 1) : 'N/A')
// User
if (!CONDENSED_MODE) {
if (l.pm2_env.uid && typeof(l.pm2_env.uid) == 'number') {
// Resolve user id to username
let users = Passwd.getUsers()
Object.keys(users).forEach(function(username) {
var user = users[username]
if (user.userId == l.pm2_env.uid) {
l.pm2_env.uid = user.username
}
})
}
obj[key].push(chalk.bold(l.pm2_env.uid || l.pm2_env.username))
}
// Watch status
if (!CONDENSED_MODE)
obj[key].push(l.pm2_env.watch ? chalk.green.bold('enabled') : chalk.grey('disabled'))
UxHelpers.safe_push(app_table, obj)
}
})
// Print Applications Managed
console.log(app_table.toString())
// Print Modules Managed
if (module_table.length > 0) {
console.log(chalk.bold(`Module${module_table.length > 1 ? 's' : ''}`))
console.log(module_table.toString())
}
proc_id++
}
// Container display
function containersListing(sys_infos) {
var stacked_docker = (process.stdout.columns || 100) < 140
var docker_head = {
id: 4,
image: 50,
status: 10,
'↺': 6,
cpu: 10,
mem: 10,
'net I/O ⇵': 11,
'fs I/O ⇵': 11
}
if (stacked_docker) {
docker_head = {
id: 4,
image: 25,
status: 10,
cpu: 10,
mem: 10
}
}
var docker_table = new Table({
colWidths: Object.keys(docker_head).map(k => docker_head[k]),
head : Object.keys(docker_head),
colAligns : ['left'],
style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true}
})
sys_infos.containers.forEach((c) => {
var cpu = c.stats.cpu_percent
var mem = c.stats.mem_percent == 0 ? '0' : c.stats.mem_percent
var id = chalk.bold.cyan(proc_id++)
var state = UxHelpers.colorStatus(c.state)
if (stacked_docker)
docker_table.push([id, c.image, state, `${cpu}%`, `${mem}mb`])
else {
docker_table.push([
id,
c.image,
state,
c.restartCount,
`${cpu == 0 ? '0' : cpu}%`,
`${mem}mb`,
`${c.stats.netIO.rx}/${isNaN(c.stats.netIO.tx) == true ? '0.0' : c.stats.netIO.tx}`,
`${c.stats.blockIO.r}/${c.stats.blockIO.w}`
])
}
})
console.log(chalk.bold(`Container${sys_infos.containers.length > 1 ? 's' : ''}`))
console.log(docker_table.toString())
}
/**
* High resource processes
*/
function listHighResourcesProcesses(sys_infos) {
const CPU_MIN_SHOW = 60
const MEM_MIN_SHOW = 30
var sys_proc_head = ['id', 'cmd', 'pid', 'cpu', 'mem', 'uid']
var sys_proc_table = new Table({
colWidths: [4, CONDENSED_MODE ? 29 : 77, 10, 10, 10, 8],
head : sys_proc_head,
colAligns : ['left'],
style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true}
})
sys_infos.processes.cpu_sorted = sys_infos.processes.cpu_sorted.filter((proc) => {
return proc.cpu > CPU_MIN_SHOW && proc.cmd.includes('node') === false &&
proc.cmd.includes('God Daemon') === false
})
sys_infos.processes.cpu_sorted.forEach(proc => {
var cpu = `${UxHelpers.colorizedMetric(proc.cpu, 40, 70, '%')}`
var mem = `${UxHelpers.colorizedMetric(proc.memory, 40, 70, '%')}`
var cmd = proc.cmd
sys_proc_table.push([chalk.bold.cyan(proc_id++), cmd, proc.pid, cpu, mem, proc.uid])
})
sys_infos.processes.mem_sorted = sys_infos.processes.mem_sorted.filter((proc) => {
return proc.memory > MEM_MIN_SHOW && proc.cmd.includes('node') == false
})
sys_infos.processes.mem_sorted.forEach((proc) => {
var cpu = `${UxHelpers.colorizedMetric(proc.cpu, 40, 70, '%')}`
var mem = `${UxHelpers.colorizedMetric(proc.memory, 40, 70, '%')}`
var cmd = proc.cmd
// if (proc.cmd.length > 50)
// cmd = '…' + proc.cmd.slice(proc.cmd.length - 48, proc.cmd.length)
sys_proc_table.push([chalk.bold.cyan(proc_id++), cmd, proc.pid, cpu, mem, proc.uid])
})
if (sys_infos.processes.cpu_sorted.length >= 1 || sys_infos.processes.mem_sorted.length >= 1) {
console.log(chalk.bold('Intensive Processes'))
console.log(sys_proc_table.toString())
}
}
/**
* Sys info line
*/
function miniMonitBar(sys_infos) {
let sys_metrics = sys_infos.pm2_env.axm_monitor
let cpu = sys_metrics['CPU Usage']
if (typeof(cpu) == 'undefined') return
var sys_summary_line = `${chalk.bold.cyan('host metrics')} `
sys_summary_line += `| ${chalk.bold('cpu')}: ${UxHelpers.colorizedMetric(cpu.value, 40, 70, '%')}`
let temp = sys_metrics['CPU Temperature'].value
if (temp && temp != '-1') {
sys_summary_line += ` ${UxHelpers.colorizedMetric(temp, 50, 70, 'º')}`
}
let mem_total = sys_metrics['RAM Total'].value
let mem_available = sys_metrics['RAM Available'].value
if (mem_total) {
var perc_mem_usage = (((mem_available) / mem_total) * 100).toFixed(1)
sys_summary_line += ` | ${chalk.bold('mem free')}: ${UxHelpers.colorizedMetric(perc_mem_usage, 30, 10, '%')} `
}
let interfaces = Object.keys(sys_metrics).filter(m => m.includes('net') && m != 'net:default').map(i => i.split(':')[2]).filter((iface, i, self) => self.indexOf(iface) === i)
interfaces.forEach(iface => {
if (!sys_metrics[`net:rx_5:${iface}`]) return
sys_summary_line += `| ${chalk.bold(iface)}: `
sys_summary_line += `${UxHelpers.colorizedMetric(sys_metrics[`net:rx_5:${iface}`].value, 10, 20, 'mb/s')} `
sys_summary_line += `${UxHelpers.colorizedMetric(sys_metrics[`net:tx_5:${iface}`].value, 10, 20, 'mb/s')} `
})
if (CONDENSED_MODE == false) {
let read = sys_metrics['Disk Reads'].value
let write = sys_metrics['Disk Writes'].value
sys_summary_line += `| ${chalk.bold('disk')}: ⇓ ${UxHelpers.colorizedMetric(read, 10, 20, 'mb/s')}`
sys_summary_line += `${UxHelpers.colorizedMetric(write, 10, 20, 'mb/s')} `
let disks = Object.keys(sys_metrics).filter(m => m.includes('fs:')).map(i => i.split(':')[2]).filter((iface, i, self) => self.indexOf(iface) === i)
var disk_nb = 0
disks.forEach(fs => {
let use = sys_metrics[`fs:use:${fs}`].value
if (use > 60)
sys_summary_line += `${chalk.grey(fs)} ${UxHelpers.colorizedMetric(use, 80, 90, '%')} `
})
}
sys_summary_line += '|'
console.log(sys_summary_line)
}
/**
* pm2 ls
* @method dispAsTable
* @param {Object} list
* @param {Object} system informations (via pm2 sysmonit/pm2 sysinfos)
*/
module.exports = function(list, commander) {
var pm2_conf = Configuration.getSync('pm2')
if (!list)
return console.log('list empty')
listModulesAndAppsManaged(list, commander)
let sysmonit = list.filter(proc => proc.name == 'pm2-sysmonit')
if (sysmonit && sysmonit[0])
miniMonitBar(sysmonit[0])
checkIfProcessAreDumped(list)
}

382
api.hyungi.net/node_modules/pm2/lib/API/Version.js generated vendored Normal file
View File

@@ -0,0 +1,382 @@
var cst = require('../../constants.js');
var Common = require('../Common.js');
var fs = require('fs');
var eachSeries = require('async/eachSeries');
var child = require('child_process');
var printError = Common.printError;
var printOut = Common.printOut;
module.exports = function(CLI) {
var EXEC_TIMEOUT = 60000; // Default: 1 min
CLI.prototype._pull = function(opts, cb) {
var that = this;
var process_name = opts.process_name;
var reload_type = opts.action;
printOut(cst.PREFIX_MSG + 'Updating repository for process name %s', process_name);
that.Client.getProcessByNameOrId(process_name, function (err, processes) {
if (err || processes.length === 0) {
printError('No processes with this name or id : %s', process_name);
return cb ? cb({msg: 'Process not found: ' + process_name}) : that.exitCli(cst.ERROR_EXIT);
}
var proc = processes[0];
if (!proc.pm2_env.versioning) {
printOut(cst.PREFIX_MSG + 'No versioning system found for process %s', process_name);
return cb ? cb({success:false, msg: 'No versioning system found for process'}) : that.exitCli(cst.SUCCESS_EXIT);
}
require('vizion').update({
folder: proc.pm2_env.versioning.repo_path
}, function(err, meta) {
if (err !== null) {
return cb ? cb({msg:err}) : that.exitCli(cst.ERROR_EXIT);
}
if (meta.success === true) {
getPostUpdateCmds(proc.pm2_env.versioning.repo_path, process_name, function (command_list) {
execCommands(proc.pm2_env.versioning.repo_path, command_list, function(err, res) {
if (err !== null) {
printError(err);
return cb ? cb({msg: meta.output + err}) : that.exitCli(cst.ERROR_EXIT);
}
else {
printOut(cst.PREFIX_MSG + 'Process successfully updated %s', process_name);
printOut(cst.PREFIX_MSG + 'Current commit %s', meta.current_revision);
return that[reload_type](process_name, function(err, procs) {
if (err && cb) return cb(err);
if (err) console.error(err);
return cb ? cb(null, meta.output + res) : that.exitCli(cst.SUCCESS_EXIT);
});
}
});
});
}
else {
printOut(cst.PREFIX_MSG + 'Already up-to-date or an error occured for app: %s', process_name);
return cb ? cb({success:false, msg : 'Already up to date'}) : that.exitCli(cst.SUCCESS_EXIT);
}
return false;
});
return false;
});
};
/**
* CLI method for updating a repository to a specific commit id
* @method pullCommitId
* @param {string} process_name
* @param {string} commit_id
* @return
*/
CLI.prototype.pullCommitId = function(process_name, commit_id, cb) {
var reload_type = 'reload';
var that = this;
printOut(cst.PREFIX_MSG + 'Updating repository for process name %s', process_name);
that.Client.getProcessByNameOrId(process_name, function (err, processes) {
if (err || processes.length === 0) {
printError('No processes with this name or id : %s', process_name);
return cb ? cb({msg: 'Process not found: ' + process_name}) : that.exitCli(cst.ERROR_EXIT);
}
var proc = processes[0];
if (proc.pm2_env.versioning) {
require('vizion').isUpToDate({folder: proc.pm2_env.versioning.repo_path}, function(err, meta) {
if (err !== null)
return cb ? cb({msg:err}) : that.exitCli(cst.ERROR_EXIT);
require('vizion').revertTo(
{revision: commit_id,
folder: proc.pm2_env.versioning.repo_path},
function(err2, meta2) {
if (!err2 && meta2.success) {
getPostUpdateCmds(proc.pm2_env.versioning.repo_path, process_name, function (command_list) {
execCommands(proc.pm2_env.versioning.repo_path, command_list, function(err, res) {
if (err !== null)
{
printError(err);
return cb ? cb({msg:err}) : that.exitCli(cst.ERROR_EXIT);
}
else {
printOut(cst.PREFIX_MSG + 'Process successfully updated %s', process_name);
printOut(cst.PREFIX_MSG + 'Current commit %s', commit_id);
return that[reload_type](process_name, cb);
}
});
});
}
else {
printOut(cst.PREFIX_MSG + 'Already up-to-date or an error occured: %s', process_name);
return cb ? cb(null, {success:meta.success}) : that.exitCli(cst.SUCCESS_EXIT);
}
});
});
}
else {
printOut(cst.PREFIX_MSG + 'No versioning system found for process %s', process_name);
return cb ? cb(null, {success:false}) : that.exitCli(cst.SUCCESS_EXIT);
}
});
};
/**
* CLI method for downgrading a repository to the previous commit (older)
* @method backward
* @param {string} process_name
* @return
*/
CLI.prototype.backward = function(process_name, cb) {
var that = this;
printOut(cst.PREFIX_MSG + 'Downgrading to previous commit repository for process name %s', process_name);
that.Client.getProcessByNameOrId(process_name, function (err, processes) {
if (err || processes.length === 0) {
printError('No processes with this name or id : %s', process_name);
return cb ? cb({msg: 'Process not found: ' + process_name}) : that.exitCli(cst.ERROR_EXIT);
}
var proc = processes[0];
// in case user searched by id/pid
process_name = proc.name;
if (proc.pm2_env.versioning === undefined ||
proc.pm2_env.versioning === null)
return cb({msg : 'Versioning unknown'});
require('vizion').prev({
folder: proc.pm2_env.versioning.repo_path
}, function(err, meta) {
if (err)
return cb ? cb({msg:err, data : meta}) : that.exitCli(cst.ERROR_EXIT);
if (meta.success !== true) {
printOut(cst.PREFIX_MSG + 'No versioning system found for process %s', process_name);
return cb ? cb({msg:err, data : meta}) : that.exitCli(cst.ERROR_EXIT);
}
getPostUpdateCmds(proc.pm2_env.versioning.repo_path, process_name, function (command_list) {
execCommands(proc.pm2_env.versioning.repo_path, command_list, function(err, res) {
if (err !== null) {
require('vizion').next({folder: proc.pm2_env.versioning.repo_path}, function(err2, meta2) {
printError(err);
return cb ? cb({msg: meta.output + err}) : that.exitCli(cst.ERROR_EXIT);
});
return false;
}
printOut(cst.PREFIX_MSG + 'Process successfully updated %s', process_name);
printOut(cst.PREFIX_MSG + 'Current commit %s', meta.current_revision);
that.reload(process_name, function(err, procs) {
if (err) return cb(err);
return cb ? cb(null, meta.output + res) : that.exitCli(cst.SUCCESS_EXIT);
});
});
});
});
});
};
/**
* CLI method for updating a repository to the next commit (more recent)
* @method forward
* @param {string} process_name
* @return
*/
CLI.prototype.forward = function(process_name, cb) {
var that = this;
printOut(cst.PREFIX_MSG + 'Updating to next commit repository for process name %s', process_name);
that.Client.getProcessByNameOrId(process_name, function (err, processes) {
if (err || processes.length === 0) {
printError('No processes with this name or id: %s', process_name);
return cb ? cb({msg: 'Process not found: ' + process_name}) : that.exitCli(cst.ERROR_EXIT);
}
var proc = processes[0];
// in case user searched by id/pid
process_name = proc.name;
if (proc.pm2_env.versioning) {
require('vizion').next({folder: proc.pm2_env.versioning.repo_path}, function(err, meta) {
if (err !== null)
return cb ? cb({msg:err}) : that.exitCli(cst.ERROR_EXIT);
if (meta.success === true) {
getPostUpdateCmds(proc.pm2_env.versioning.repo_path, process_name, function (command_list) {
execCommands(proc.pm2_env.versioning.repo_path, command_list, function(err, res) {
if (err !== null)
{
require('vizion').prev({folder: proc.pm2_env.versioning.repo_path}, function(err2, meta2) {
printError(err);
return cb ? cb({msg:meta.output + err}) : that.exitCli(cst.ERROR_EXIT);
});
}
else {
printOut(cst.PREFIX_MSG + 'Process successfully updated %s', process_name);
printOut(cst.PREFIX_MSG + 'Current commit %s', meta.current_revision);
that.reload(process_name, function(err, procs) {
if (err) return cb(err);
return cb ? cb(null, meta.output + res) : that.exitCli(cst.SUCCESS_EXIT);
});
}
});
});
}
else {
printOut(cst.PREFIX_MSG + 'Already up-to-date or an error occured: %s', process_name);
return cb ? cb(null, {success:meta.success}) : that.exitCli(cst.SUCCESS_EXIT);
}
});
}
else {
printOut(cst.PREFIX_MSG + 'No versioning system found for process %s', process_name);
return cb ? cb({success:false, msg: 'No versioning system found'}) : that.exitCli(cst.SUCCESS_EXIT);
}
});
};
var exec = function (cmd, callback) {
var output = '';
var c = child.exec(cmd, {
env: process.env,
maxBuffer: 3*1024*1024,
timeout: EXEC_TIMEOUT
}, function(err) {
if (callback)
callback(err ? err.code : 0, output);
});
c.stdout.on('data', function(data) {
output += data;
});
c.stderr.on('data', function(data) {
output += data;
});
};
/**
*
* @method execCommands
* @param {string} repo_path
* @param {object} command_list
* @return
*/
var execCommands = function(repo_path, command_list, cb) {
var stdout = '';
eachSeries(command_list, function(command, callback) {
stdout += '\n' + command;
exec('cd '+repo_path+';'+command,
function(code, output) {
stdout += '\n' + output;
if (code === 0)
callback();
else
callback('`'+command+'` failed');
});
}, function(err) {
if (err)
return cb(stdout + '\n' + err);
return cb(null, stdout);
});
}
/**
* Description Search process.json for post-update commands
* @method getPostUpdateCmds
* @param {string} repo_path
* @param {string} proc_name
* @return
*/
var getPostUpdateCmds = function(repo_path, proc_name, cb) {
if (typeof repo_path !== 'string')
return cb([]);
if (repo_path[repo_path.length - 1] !== '/')
repo_path += '/';
var searchForCommands = function(file, callback) {
fs.exists(repo_path+file, function(exists) {
if (exists) {
try {
var conf_string = fs.readFileSync(repo_path + file);
var data = Common.parseConfig(conf_string, repo_path + file);
} catch (e) {
console.error(e.message || e);
}
if (data && data.apps) {
eachSeries(data.apps, function(item, callb) {
if (item.name && item.name === proc_name) {
if (item.post_update && typeof(item.post_update) === 'object') {
if (item.exec_timeout)
EXEC_TIMEOUT = parseInt(item.exec_timeout);
return callb(item.post_update);
}
else {
return callb();
}
}
else
return callb();
}, function(final) {
return callback(final);
});
}
else {
return callback();
}
}
else {
return callback();
}
});
};
eachSeries(['ecosystem.json', 'process.json', 'package.json'], searchForCommands,
function(final) {
return cb(final ? final : []);
});
};
/**
* CLI method for updating a repository
* @method pullAndRestart
* @param {string} process_name name of processes to pull
* @return
*/
CLI.prototype.pullAndRestart = function (process_name, cb) {
this._pull({process_name: process_name, action: 'reload'}, cb);
};
/**
* CLI method for updating a repository
* @method pullAndReload
* @param {string} process_name name of processes to pull
* @return
*/
CLI.prototype.pullAndReload = function (process_name, cb) {
this._pull({process_name: process_name, action: 'reload'}, cb);
};
/**
* CLI method for updating a repository to a specific commit id
* @method pullCommitId
* @param {object} opts
* @return
*/
CLI.prototype._pullCommitId = function (opts, cb) {
this.pullCommitId(opts.pm2_name, opts.commit_id, cb);
};
}

View File

@@ -0,0 +1,12 @@
{
".sh" : "bash",
".py" : "python",
".rb" : "ruby",
".php" : "php",
".pl" : "perl",
".js" : "node",
".coffee" : "coffee",
".ls" : "lsc",
".ts" : "bun",
".tsx" : "bun"
}

View File

@@ -0,0 +1,372 @@
'use strict'
var cst = require('../../../constants.js');
const chalk = require('chalk');
const path = require('path');
const fs = require('fs');
const Table = require('cli-tableau');
const pkg = require('../../../package.json')
const IOAPI = require('@pm2/js-api')
const promptly = require('promptly')
var CLIStrategy = require('./auth-strategies/CliAuth')
var WebStrategy = require('./auth-strategies/WebAuth')
const exec = require('child_process').exec
const OAUTH_CLIENT_ID_WEB = '138558311'
const OAUTH_CLIENT_ID_CLI = '0943857435'
module.exports = class PM2ioHandler {
static usePM2Client (instance) {
this.pm2 = instance
}
static strategy () {
switch (process.platform) {
case 'darwin': {
return new WebStrategy({
client_id: OAUTH_CLIENT_ID_WEB
})
}
case 'win32': {
return new WebStrategy({
client_id: OAUTH_CLIENT_ID_WEB
})
}
case 'linux': {
const isDesktop = process.env.XDG_CURRENT_DESKTOP || process.env.XDG_SESSION_DESKTOP || process.env.DISPLAY
const isSSH = process.env.SSH_TTY || process.env.SSH_CONNECTION
if (isDesktop && !isSSH) {
return new WebStrategy({
client_id: OAUTH_CLIENT_ID_WEB
})
} else {
return new CLIStrategy({
client_id: OAUTH_CLIENT_ID_CLI
})
}
}
default: {
return new CLIStrategy({
client_id: OAUTH_CLIENT_ID_CLI
})
}
}
}
static init () {
this._strategy = this.strategy()
/**
* If you are using a local backend you should give those options :
* {
* services: {
* API: 'http://localhost:3000',
* OAUTH: 'http://localhost:3100'
* }
* }
*/
this.io = new IOAPI().use(this._strategy)
}
static launch (command, opts) {
// first init the strategy and the io client
this.init()
switch (command) {
case 'connect' :
case 'login' :
case 'register' :
case undefined :
case 'authenticate' : {
this.authenticate()
break
}
case 'validate' : {
this.validateAccount(opts)
break
}
case 'help' :
case 'welcome': {
var dt = fs.readFileSync(path.join(__dirname, './pres/welcome'));
console.log(dt.toString());
return process.exit(0)
}
case 'logout': {
this._strategy.isAuthenticated().then(isConnected => {
// try to kill the agent anyway
this.pm2.killAgent(err => {})
if (isConnected === false) {
console.log(`${cst.PM2_IO_MSG} Already disconnected`)
return process.exit(0)
}
this._strategy._retrieveTokens((err, tokens) => {
if (err) {
console.log(`${cst.PM2_IO_MSG} Successfully disconnected`)
return process.exit(0)
}
this._strategy.deleteTokens(this.io).then(_ => {
console.log(`${cst.PM2_IO_MSG} Successfully disconnected`)
return process.exit(0)
}).catch(err => {
console.log(`${cst.PM2_IO_MSG_ERR} Unexpected error: ${err.message}`)
return process.exit(1)
})
})
}).catch(err => {
console.error(`${cst.PM2_IO_MSG_ERR} Failed to logout: ${err.message}`)
console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)
})
break
}
case 'create': {
this._strategy.isAuthenticated().then(res => {
// if the user isn't authenticated, we make them do the whole flow
if (res !== true) {
this.authenticate()
} else {
this.createBucket(this.createBucketHandler.bind(this))
}
}).catch(err => {
console.error(`${cst.PM2_IO_MSG_ERR} Failed to create to the bucket: ${err.message}`)
console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)
})
break
}
case 'web': {
this._strategy.isAuthenticated().then(res => {
// if the user isn't authenticated, we make them do the whole flow
if (res === false) {
console.error(`${cst.PM2_IO_MSG_ERR} You need to be authenticated to do that, please use: pm2 plus login`)
return process.exit(1)
}
this._strategy._retrieveTokens(() => {
return this.openUI()
})
}).catch(err => {
console.error(`${cst.PM2_IO_MSG_ERR} Failed to open the UI: ${err.message}`)
console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)
})
break
}
default : {
console.log(`${cst.PM2_IO_MSG_ERR} Invalid command ${command}, available : login,register,validate,connect or web`)
process.exit(1)
}
}
}
static openUI () {
this.io.bucket.retrieveAll().then(res => {
const buckets = res.data
if (buckets.length === 0) {
return this.createBucket((err, bucket) => {
if (err) {
console.error(`${cst.PM2_IO_MSG_ERR} Failed to connect to the bucket: ${err.message}`)
if (bucket) {
console.error(`${cst.PM2_IO_MSG_ERR} You can retry using: pm2 plus link ${bucket.secret_id} ${bucket.public_id}`)
}
console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)
return process.exit(0)
}
const targetURL = `https://app.pm2.io/#/bucket/${bucket._id}`
console.log(`${cst.PM2_IO_MSG} Please follow the popup or go to this URL :`, '\n', ' ', targetURL)
this.open(targetURL)
return process.exit(0)
})
}
var table = new Table({
style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true},
head : ['Bucket name', 'Plan type']
})
buckets.forEach(function(bucket) {
table.push([bucket.name, bucket.credits.offer_type])
})
console.log(table.toString())
console.log(`${cst.PM2_IO_MSG} If you don't want to open the UI to a bucket, type 'none'`)
const choices = buckets.map(bucket => bucket.name)
choices.push('none')
promptly.choose(`${cst.PM2_IO_MSG} Type the name of the bucket you want to connect to :`, choices, (err, value) => {
if (value === 'none') process.exit(0)
const bucket = buckets.find(bucket => bucket.name === value)
if (bucket === undefined) return process.exit(0)
const targetURL = `https://app.pm2.io/#/bucket/${bucket._id}`
console.log(`${cst.PM2_IO_MSG} Please follow the popup or go to this URL :`, '\n', ' ', targetURL)
this.open(targetURL)
return process.exit(0)
})
})
}
static validateAccount (token) {
this.io.auth.validEmail(token)
.then(res => {
console.log(`${cst.PM2_IO_MSG} Email succesfully validated.`)
console.log(`${cst.PM2_IO_MSG} You can now proceed and use: pm2 plus connect`)
return process.exit(0)
}).catch(err => {
if (err.status === 401) {
console.error(`${cst.PM2_IO_MSG_ERR} Invalid token`)
return process.exit(1)
} else if (err.status === 301) {
console.log(`${cst.PM2_IO_MSG} Email succesfully validated.`)
console.log(`${cst.PM2_IO_MSG} You can now proceed and use: pm2 plus connect`)
return process.exit(0)
}
const msg = err.data ? err.data.error_description || err.data.msg : err.message
console.error(`${cst.PM2_IO_MSG_ERR} Failed to validate your email: ${msg}`)
console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)
return process.exit(1)
})
}
static createBucketHandler (err, bucket) {
if (err) {
console.trace(`${cst.PM2_IO_MSG_ERR} Failed to connect to the bucket: ${err.message}`)
if (bucket) {
console.error(`${cst.PM2_IO_MSG_ERR} You can retry using: pm2 plus link ${bucket.secret_id} ${bucket.public_id}`)
}
console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)
return process.exit(0)
}
if (bucket === undefined) {
return process.exit(0)
}
console.log(`${cst.PM2_IO_MSG} Successfully connected to bucket ${bucket.name}`)
var targetURL = `https://app.pm2.io/#/bucket/${bucket._id}`
console.log(`${cst.PM2_IO_MSG} You can use the web interface over there: ${targetURL}`)
this.open(targetURL)
return process.exit(0)
}
static createBucket (cb) {
console.log(`${cst.PM2_IO_MSG} By default we allow you to trial PM2 Plus for 14 days without any credit card.`)
this.io.bucket.create({
name: 'PM2 Plus Monitoring'
}).then(res => {
const bucket = res.data.bucket
console.log(`${cst.PM2_IO_MSG} Successfully created the bucket`)
this.pm2.link({
public_key: bucket.public_id,
secret_key: bucket.secret_id,
pm2_version: pkg.version
}, (err) => {
if (err) {
return cb(new Error('Failed to connect your local PM2 to your bucket'), bucket)
} else {
return cb(null, bucket)
}
})
}).catch(err => {
return cb(new Error(`Failed to create a bucket: ${err.message}`))
})
}
/**
* Connect the local agent to a specific bucket
* @param {Function} cb
*/
static connectToBucket (cb) {
this.io.bucket.retrieveAll().then(res => {
const buckets = res.data
if (buckets.length === 0) {
return this.createBucket(cb)
}
var table = new Table({
style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true},
head : ['Bucket name', 'Plan type']
})
buckets.forEach(function(bucket) {
table.push([bucket.name, bucket.payment.offer_type])
})
console.log(table.toString())
console.log(`${cst.PM2_IO_MSG} If you don't want to connect to a bucket, type 'none'`)
const choices = buckets.map(bucket => bucket.name)
choices.push('none')
promptly.choose(`${cst.PM2_IO_MSG} Type the name of the bucket you want to connect to :`, choices, (err, value) => {
if (value === 'none') return cb()
const bucket = buckets.find(bucket => bucket.name === value)
if (bucket === undefined) return cb()
this.pm2.link({
public_key: bucket.public_id,
secret_key: bucket.secret_id,
pm2_version: pkg.version
}, (err) => {
return err ? cb(err) : cb(null, bucket)
})
})
})
}
/**
* Authenticate the user with either of the strategy
* @param {Function} cb
*/
static authenticate () {
this._strategy._retrieveTokens((err, tokens) => {
if (err) {
const msg = err.data ? err.data.error_description || err.data.msg : err.message
console.log(`${cst.PM2_IO_MSG_ERR} Unexpected error : ${msg}`)
return process.exit(1)
}
console.log(`${cst.PM2_IO_MSG} Successfully authenticated`)
this.io.user.retrieve().then(res => {
const user = res.data
this.io.user.retrieve().then(res => {
const tmpUser = res.data
console.log(`${cst.PM2_IO_MSG} Successfully validated`)
this.connectToBucket(this.createBucketHandler.bind(this))
})
})
})
}
static open (target, appName, callback) {
let opener
const escape = function (s) {
return s.replace(/"/g, '\\"')
}
if (typeof (appName) === 'function') {
callback = appName
appName = null
}
switch (process.platform) {
case 'darwin': {
opener = appName ? `open -a "${escape(appName)}"` : `open`
break
}
case 'win32': {
opener = appName ? `start "" ${escape(appName)}"` : `start ""`
break
}
default: {
opener = appName ? escape(appName) : `xdg-open`
break
}
}
if (process.env.SUDO_USER) {
opener = 'sudo -u ' + process.env.SUDO_USER + ' ' + opener
}
return exec(`${opener} "${escape(target)}"`, callback)
}
}

View File

@@ -0,0 +1,288 @@
'use strict'
const AuthStrategy = require('@pm2/js-api/src/auth_strategies/strategy')
const querystring = require('querystring');
const http = require('http')
const fs = require('fs')
const url = require('url')
const exec = require('child_process').exec
const tryEach = require('async/tryEach')
const path = require('path')
const os = require('os')
const needle = require('needle')
const chalk = require('chalk')
const cst = require('../../../../constants.js')
const promptly = require('promptly')
module.exports = class CliStrategy extends AuthStrategy {
// the client will try to call this but we handle this part ourselves
retrieveTokens (km, cb) {
this.authenticated = false
this.callback = cb
this.km = km
this.BASE_URI = 'https://id.keymetrics.io';
}
// so the cli know if we need to tell user to login/register
isAuthenticated () {
return new Promise((resolve, reject) => {
if (this.authenticated) return resolve(true)
let tokensPath = cst.PM2_IO_ACCESS_TOKEN
fs.readFile(tokensPath, (err, tokens) => {
if (err && err.code === 'ENOENT') return resolve(false)
if (err) return reject(err)
// verify that the token is valid
try {
tokens = JSON.parse(tokens || '{}')
} catch (err) {
fs.unlinkSync(tokensPath)
return resolve(false)
}
// if the refresh tokens is here, the user could be automatically authenticated
return resolve(typeof tokens.refresh_token === 'string')
})
})
}
verifyToken (refresh) {
return this.km.auth.retrieveToken({
client_id: this.client_id,
refresh_token: refresh
})
}
// called when we are sure the user asked to be logged in
_retrieveTokens (optionalCallback) {
const km = this.km
const cb = this.callback
tryEach([
// try to find the token via the environment
(next) => {
if (!process.env.PM2_IO_TOKEN) {
return next(new Error('No token in env'))
}
this.verifyToken(process.env.PM2_IO_TOKEN)
.then((res) => {
return next(null, res.data)
}).catch(next)
},
// try to find it in the file system
(next) => {
fs.readFile(cst.PM2_IO_ACCESS_TOKEN, (err, tokens) => {
if (err) return next(err)
// verify that the token is valid
tokens = JSON.parse(tokens || '{}')
if (new Date(tokens.expire_at) > new Date(new Date().toISOString())) {
return next(null, tokens)
}
this.verifyToken(tokens.refresh_token)
.then((res) => {
return next(null, res.data)
}).catch(next)
})
},
// otherwise make the whole flow
(next) => {
return this.authenticate((err, data) => {
if (err instanceof Error) return next(err)
// verify that the token is valid
this.verifyToken(data.refresh_token)
.then((res) => {
return next(null, res.data)
}).catch(next)
})
}
], (err, result) => {
// if present run the optional callback
if (typeof optionalCallback === 'function') {
optionalCallback(err, result)
}
if (result.refresh_token) {
this.authenticated = true
let file = cst.PM2_IO_ACCESS_TOKEN
fs.writeFile(file, JSON.stringify(result), () => {
return cb(err, result)
})
} else {
return cb(err, result)
}
})
}
authenticate (cb) {
console.log(`${cst.PM2_IO_MSG} Using non-browser authentication.`)
promptly.confirm(`${cst.PM2_IO_MSG} Do you have a pm2.io account? (y/n)`, (err, answer) => {
// Either login or register
return answer === true ? this.login(cb) : this.register(cb)
})
}
login (cb) {
let retry = () => {
promptly.prompt(`${cst.PM2_IO_MSG} Your username or email: `, (err, username) => {
if (err) return retry();
promptly.password(`${cst.PM2_IO_MSG} Your password: `, { replace : '*' }, (err, password) => {
if (err) return retry();
console.log(`${cst.PM2_IO_MSG} Authenticating ...`)
this._loginUser({
username: username,
password: password
}, (err, data) => {
if (err) {
console.error(`${cst.PM2_IO_MSG_ERR} Failed to authenticate: ${err.message}`)
return retry()
}
return cb(null, data)
})
})
})
}
retry()
}
register (cb) {
console.log(`${cst.PM2_IO_MSG} No problem ! We just need few informations to create your account`)
var retry = () => {
promptly.prompt(`${cst.PM2_IO_MSG} Please choose an username :`, {
validator : this._validateUsername,
retry : true
}, (err, username) => {
promptly.prompt(`${cst.PM2_IO_MSG} Please choose an email :`, {
validator : this._validateEmail,
retry : true
},(err, email) => {
promptly.password(`${cst.PM2_IO_MSG} Please choose a password :`, { replace : '*' }, (err, password) => {
promptly.confirm(`${cst.PM2_IO_MSG} Do you accept the terms and privacy policy (https://pm2.io/legals/terms_conditions.pdf) ? (y/n)`, (err, answer) => {
if (err) {
console.error(chalk.bold.red(err));
return retry()
} else if (answer === false) {
console.error(`${cst.PM2_IO_MSG_ERR} You must accept the terms and privacy policy to contiue.`)
return retry()
}
this._registerUser({
email : email,
password : password,
username : username
}, (err, data) => {
console.log('\n')
if (err) {
console.error(`${cst.PM2_IO_MSG_ERR} Unexpect error: ${err.message}`)
console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)
return process.exit(1)
}
return cb(undefined, data)
})
})
})
})
})
}
retry()
}
/**
* Register function
* @param opts.username
* @param opts.password
* @param opts.email
*/
_registerUser (opts, cb) {
const data = Object.assign(opts, {
password_confirmation: opts.password,
accept_terms: true
})
needle.post(this.BASE_URI + '/api/oauth/register', data, {
json: true,
headers: {
'X-Register-Provider': 'pm2-register',
'x-client-id': this.client_id
}
}, function (err, res, body) {
if (err) return cb(err)
if (body.email && body.email.message) return cb(new Error(body.email.message))
if (body.username && body.username.message) return cb(new Error(body.username.message))
if (!body.access_token) return cb(new Error(body.msg))
return cb(null, {
refresh_token : body.refresh_token.token,
access_token : body.access_token.token
})
});
}
_loginUser (user_info, cb) {
const URL_AUTH = '/api/oauth/authorize?response_type=token&scope=all&client_id=' +
this.client_id + '&redirect_uri=http://localhost:43532';
needle.get(this.BASE_URI + URL_AUTH, (err, res) => {
if (err) return cb(err);
var cookie = res.cookies;
needle.post(this.BASE_URI + '/api/oauth/login', user_info, {
cookies : cookie
}, (err, resp, body) => {
if (err) return cb(err)
if (resp.statusCode != 200) return cb('Wrong credentials')
var location = resp.headers['x-redirect']
needle.get(this.BASE_URI + location, {
cookies : cookie
}, (err, res) => {
if (err) return cb(err);
var refresh_token = querystring.parse(url.parse(res.headers.location).query).access_token;
needle.post(this.BASE_URI + '/api/oauth/token', {
client_id : this.client_id,
grant_type : 'refresh_token',
refresh_token : refresh_token,
scope : 'all'
}, (err, res, body) => {
if (err) return cb(err)
return cb(null, body)
})
})
})
})
}
_validateEmail (email) {
var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if (re.test(email) == false)
throw new Error('Not an email');
return email;
}
_validateUsername (value) {
if (value.length < 6) {
throw new Error('Min length of 6');
}
return value;
};
deleteTokens (km) {
return new Promise((resolve, reject) => {
// revoke the refreshToken
km.auth.revoke()
.then(res => {
// remove the token from the filesystem
let file = cst.PM2_IO_ACCESS_TOKEN
fs.unlinkSync(file)
return resolve(res)
}).catch(reject)
})
}
}

View File

@@ -0,0 +1,187 @@
'use strict'
const cst = require('../../../../constants.js');
const AuthStrategy = require('@pm2/js-api/src/auth_strategies/strategy')
const http = require('http')
const fs = require('fs')
const url = require('url')
const exec = require('child_process').exec
const tryEach = require('async/tryEach');
module.exports = class WebStrategy extends AuthStrategy {
// the client will try to call this but we handle this part ourselves
retrieveTokens (km, cb) {
this.authenticated = false
this.callback = cb
this.km = km
}
// so the cli know if we need to tell user to login/register
isAuthenticated () {
return new Promise((resolve, reject) => {
if (this.authenticated) return resolve(true)
let tokensPath = cst.PM2_IO_ACCESS_TOKEN
fs.readFile(tokensPath, (err, tokens) => {
if (err && err.code === 'ENOENT') return resolve(false)
if (err) return reject(err)
// verify that the token is valid
try {
tokens = JSON.parse(tokens || '{}')
} catch (err) {
fs.unlinkSync(tokensPath)
return resolve(false)
}
// if the refresh tokens is here, the user could be automatically authenticated
return resolve(typeof tokens.refresh_token === 'string')
})
})
}
// called when we are sure the user asked to be logged in
_retrieveTokens (optionalCallback) {
const km = this.km
const cb = this.callback
let verifyToken = (refresh) => {
return km.auth.retrieveToken({
client_id: this.client_id,
refresh_token: refresh
})
}
tryEach([
// try to find the token via the environment
(next) => {
if (!process.env.PM2_IO_TOKEN) {
return next(new Error('No token in env'))
}
verifyToken(process.env.PM2_IO_TOKEN)
.then((res) => {
return next(null, res.data)
}).catch(next)
},
// try to find it in the file system
(next) => {
fs.readFile(cst.PM2_IO_ACCESS_TOKEN, (err, tokens) => {
if (err) return next(err)
// verify that the token is valid
tokens = JSON.parse(tokens || '{}')
if (new Date(tokens.expire_at) > new Date(new Date().toISOString())) {
return next(null, tokens)
}
verifyToken(tokens.refresh_token)
.then((res) => {
return next(null, res.data)
}).catch(next)
})
},
// otherwise make the whole flow
(next) => {
return this.loginViaWeb((data) => {
// verify that the token is valid
verifyToken(data.access_token)
.then((res) => {
return next(null, res.data)
}).catch(err => next(err))
})
}
], (err, result) => {
// if present run the optional callback
if (typeof optionalCallback === 'function') {
optionalCallback(err, result)
}
if (result.refresh_token) {
this.authenticated = true
let file = cst.PM2_IO_ACCESS_TOKEN
fs.writeFile(file, JSON.stringify(result), () => {
return cb(err, result)
})
} else {
return cb(err, result)
}
})
}
loginViaWeb (cb) {
const redirectURL = `${this.oauth_endpoint}${this.oauth_query}`
console.log(`${cst.PM2_IO_MSG} Please follow the popup or go to this URL :`, '\n', ' ', redirectURL)
let shutdown = false
let server = http.createServer((req, res) => {
// only handle one request
if (shutdown === true) return res.end()
shutdown = true
let query = url.parse(req.url, true).query
res.write(`
<head>
<script>
</script>
</head>
<body>
<h2 style="text-align: center">
You can go back to your terminal now :)
</h2>
</body>`)
res.end()
server.close()
return cb(query)
})
server.listen(43532, () => {
this.open(redirectURL)
})
}
deleteTokens (km) {
return new Promise((resolve, reject) => {
// revoke the refreshToken
km.auth.revoke()
.then(res => {
// remove the token from the filesystem
let file = cst.PM2_IO_ACCESS_TOKEN
fs.unlinkSync(file)
return resolve(res)
}).catch(reject)
})
}
open (target, appName, callback) {
let opener
const escape = function (s) {
return s.replace(/"/g, '\\"')
}
if (typeof (appName) === 'function') {
callback = appName
appName = null
}
switch (process.platform) {
case 'darwin': {
opener = appName ? `open -a "${escape(appName)}"` : `open`
break
}
case 'win32': {
opener = appName ? `start "" ${escape(appName)}"` : `start ""`
break
}
default: {
opener = appName ? escape(appName) : `xdg-open`
break
}
}
if (process.env.SUDO_USER) {
opener = 'sudo -u ' + process.env.SUDO_USER + ' ' + opener
}
return exec(`${opener} "${escape(target)}"`, callback)
}
}

View File

@@ -0,0 +1,97 @@
var cst = require('../../../constants.js');
var Common = require('../../Common.js');
const chalk = require('chalk');
const forEach = require('async/forEach');
const open = require('../../tools/open.js');
const Modules = require('../Modules');
function processesAreAlreadyMonitored(CLI, cb) {
CLI.Client.executeRemote('getMonitorData', {}, function(err, list) {
if (err) return cb(false);
var l = list.filter(l => l.pm2_env.km_link == true)
var l2 = list.filter(l => l.name == 'pm2-server-monit')
return cb(l.length > 0 && l2.length > 0 ? true : false)
})
}
module.exports = function(CLI) {
CLI.prototype.openDashboard = function() {
if (!this.gl_interact_infos) {
Common.printError(chalk.bold.white('Agent if offline, type `$ pm2 plus` to log in'));
return this.exitCli(cst.ERROR_EXIT);
}
var uri = `https://app.pm2.io/#/r/${this.gl_interact_infos.public_key}`
console.log(cst.PM2_IO_MSG + ` Opening ${uri}`)
open(uri);
setTimeout(_ => {
this.exitCli();
}, 200);
};
CLI.prototype.clearSetup = function (opts, cb) {
const modules = ['event-loop-inspector']
this.gl_is_km_linked = false
forEach(modules, (_module, next) => {
Modules.uninstall(this, _module, () => {
next()
});
}, (err) => {
this.reload('all', () => {
return cb()
})
})
}
/**
* Install required package and enable flags for current running processes
*/
CLI.prototype.minimumSetup = function (opts, cb) {
var self = this;
this.gl_is_km_linked = true
function install(cb) {
var modules = []
if (opts.type === 'enterprise' || opts.type === 'plus') {
modules = ['pm2-logrotate', 'pm2-server-monit']
if (opts.type === 'enterprise') {
modules.push('deep-metrics')
}
}
forEach(modules, (_module, next) => {
Modules.install(self, _module, {}, () => {
next()
});
}, (err) => {
self.reload('all', () => {
return cb()
})
})
}
processesAreAlreadyMonitored(self, (already_monitored) => {
if (already_monitored) {
console.log(cst.PM2_IO_MSG + ` PM2 ${opts.type || ''} bundle already installed`);
return cb()
}
if (opts.installAll)
return install(cb)
// promptly.confirm(chalk.bold('Install all pm2 plus dependencies ? (y/n)'), (err, answer) => {
// if (!err && answer === true)
return install(cb)
// self.reload('all', () => {
// return cb()
// })
// });
})
}
}

View File

@@ -0,0 +1,126 @@
var cst = require('../../../constants.js');
var Common = require('../../Common.js');
var chalk = require('chalk');
var fs = require('fs');
var KMDaemon = require('@pm2/agent/src/InteractorClient');
var pkg = require('../../../package.json')
module.exports = function(CLI) {
CLI.prototype.linkManagement = function(cmd, public_key, machine, opts, cb) {
var that = this;
// pm2 link stop || kill
if (cmd == 'stop' || cmd == 'kill') {
that.gl_is_km_linked = false
console.log(cst.PM2_IO_MSG + ' Stopping agent...');
return that.killAgent(function(err) {
if (err) {
Common.printError(err);
return process.exit(cst.ERROR_EXIT);
}
console.log(cst.PM2_IO_MSG + ' Stopped');
that.reload('all', () => {
return process.exit(cst.SUCCESS_EXIT);
})
});
}
// pm2 link info
if (cmd == 'info') {
console.log(cst.PM2_IO_MSG + ' Getting agent information...');
that.agentInfos(function(err, infos) {
if (err) {
console.error(cst.PM2_IO_MSG_ERR + ' ' + err.message);
return that.exitCli(cst.ERROR_EXIT);
}
console.log(infos);
return that.exitCli(cst.SUCCESS_EXIT);
});
return false;
}
// pm2 link delete
if (cmd == 'delete') {
that.gl_is_km_linked = false
console.log(cst.PM2_IO_MSG + ' Permanently disable agent...');
that.killAgent(function(err) {
try {
fs.unlinkSync(cst.INTERACTION_CONF);
} catch(e) {
console.log(cst.PM2_IO_MSG + ' No interaction config file found');
return process.exit(cst.SUCCESS_EXIT);
}
console.log(cst.PM2_IO_MSG + ' Agent interaction ended');
if (!cb)
return process.exit(cst.SUCCESS_EXIT);
return cb()
});
return false;
}
if (cmd && !public_key) {
console.error(cst.PM2_IO_MSG + ' Command [%s] unknown or missing public key', cmd);
return process.exit(cst.ERROR_EXIT);
}
// pm2 link xxx yyy
var infos;
if (!cmd) {
infos = null;
}
else
infos = {
public_key : public_key,
secret_key : cmd,
machine_name : machine,
info_node : opts.infoNode || null,
pm2_version: pkg.version
}
that.link(infos, cb)
};
CLI.prototype.link = function(infos, cb) {
var that = this;
process.env.WS_JSON_PATCH = true
KMDaemon.launchAndInteract(cst, infos, function(err, dt) {
if (err) {
Common.printError(cst.PM2_IO_MSG + ' Run `$ pm2 plus` to connect')
return that.exitCli(cst.ERROR_EXIT);
}
console.log(chalk.bold.green('[+] PM2+ activated!'))
if (!cb) {
return that.exitCli(cst.SUCCESS_EXIT);
}
return cb(null, dt)
});
};
CLI.prototype.agentInfos = function(cb) {
KMDaemon.getInteractInfo(this._conf, function(err, data) {
if (err)
return cb(Common.retErr(err));
return cb(null, data);
});
};
CLI.prototype.killAgent = function(cb) {
var that = this;
KMDaemon.killInteractorDaemon(that._conf, function(err) {
if (err)
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.SUCCESS_EXIT);
return cb ? cb(null) : that.exitCli(cst.SUCCESS_EXIT);
});
};
CLI.prototype.unlink = function(cb) {
this.linkManagement('delete', cb);
};
};

View File

@@ -0,0 +1,16 @@
██████╗ ███╗ ███╗██████╗ ██╗ ██╗ ██████╗
██╔══██╗████╗ ████║╚════██╗ ██║ ██╔╝██╔═══██╗
██████╔╝██╔████╔██║ █████╔╝ ██║ ██╔╝ ██║ ██║
██╔═══╝ ██║╚██╔╝██║██╔═══╝ ██║ ██╔╝ ██║ ██║
██║ ██║ ╚═╝ ██║███████╗ ██║██╔╝ ╚██████╔╝
╚═╝ ╚═╝ ╚═╝╚══════╝ ╚═╝╚═╝ ╚═════╝
https://pm2.io/
Harden your Node.js Production Environment
- Real-time Monitoring Web Interface
- Pro Active Alerting System
- Production Profiling for Memory and CPU
- PM2 Runtime High Availability Fallback

View File

@@ -0,0 +1,26 @@
-------------
██████╗ ███╗ ███╗██████╗ ██╗ ██╗ ██████╗
██╔══██╗████╗ ████║╚════██╗ ██║ ██╔╝██╔═══██╗
██████╔╝██╔████╔██║ █████╔╝ ██║ ██╔╝ ██║ ██║
██╔═══╝ ██║╚██╔╝██║██╔═══╝ ██║ ██╔╝ ██║ ██║
██║ ██║ ╚═╝ ██║███████╗ ██║██╔╝ ╚██████╔╝
╚═╝ ╚═╝ ╚═╝╚══════╝ ╚═╝╚═╝ ╚═════╝
https://pm2.io/
Harden your Node.js Production Environment
- Real-time Monitoring Web Interface
- Pro Active Alerting System
- Production Profiling for Memory and CPU
- PM2 Runtime High Availability Fallback
Start using it by typing:
$ pm2 plus
-------------

View File

@@ -0,0 +1,28 @@
-------------
PM2 Plus Edition
PM2 Plus is a monitoring dashboard
specialized for Node.js Apps.
Create an account:
$ pm2 plus register
Connect your local PM2 to the PM2 Plus servers:
$ pm2 plus connect
See our UI with your own realtime dashboard:
$ pm2 plus web
More details available there:
http://pm2.io/plus
-------------
Having complex or specific needs?
You can also checkout our enterprise offer at:
http://pm2.io/enterprise
-------------

View File

@@ -0,0 +1,52 @@
const fs = require('fs');
const forEachLimit = require('async/forEachLimit');
var cst = require('../../../constants.js');
var Common = require('../../Common.js');
module.exports = function(CLI) {
/**
* Monitor Selectively Processes (auto filter in interaction)
* @param String state 'monitor' or 'unmonitor'
* @param String target <pm_id|name|all>
* @param Function cb callback
*/
CLI.prototype.monitorState = function(state, target, cb) {
var that = this;
if (!target) {
Common.printError(cst.PREFIX_MSG_ERR + 'Please specify an <app_name|pm_id>');
return cb ? cb(new Error('argument missing')) : that.exitCli(cst.ERROR_EXIT);
}
function monitor (pm_id, cb) {
// State can be monitor or unmonitor
that.Client.executeRemote(state, pm_id, cb);
}
if (target === 'all') {
that.Client.getAllProcessId(function (err, procs) {
if (err) {
Common.printError(err);
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
}
forEachLimit(procs, 1, monitor, function (err, res) {
return typeof cb === 'function' ? cb(err, res) : that.speedList();
});
});
} else if (!Number.isInteger(parseInt(target))) {
this.Client.getProcessIdByName(target, true, function (err, procs) {
if (err) {
Common.printError(err);
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
}
forEachLimit(procs, 1, monitor, function (err, res) {
return typeof cb === 'function' ? cb(err, res) : that.speedList();
});
});
} else {
monitor(parseInt(target), function (err, res) {
return typeof cb === 'function' ? cb(err, res) : that.speedList();
});
}
};
}

375
api.hyungi.net/node_modules/pm2/lib/API/schema.json generated vendored Normal file
View File

@@ -0,0 +1,375 @@
{
"script": {
"type": "string",
"require": true,
"alias" : "exec",
"docDescription": "Path of the script to launch, required field"
},
"name": {
"type": "string",
"docDefault": "Script filename without the extension (app for app.js)",
"docDescription": "Process name in the process list"
},
"name_prefix": {
"type": "string"
},
"filter_env": {
"type": [
"boolean",
"array",
"string"
],
"docDefault": false,
"docDescription": "Enable filtering global environments"
},
"namespace": {
"type": "string",
"docDefault": "default",
"docDescription": "Process namespace"
},
"install_url": {
"type": "string"
},
"cwd": {
"type": "string",
"docDefault": "CWD of the current environment (from your shell)",
"docDescription": "Current working directory to start the process with"
},
"args": {
"type": [
"array",
"string"
],
"docDescription": "Arguments to pass to the script"
},
"exec_interpreter": {
"type": "string",
"alias": "interpreter",
"docDefault": "node",
"docDescription": "Interpreter absolute path"
},
"node_args": {
"type": [
"array",
"string"
],
"alias": ["interpreterArgs", "interpreter_args"],
"docDescription": "Arguments to pass to the interpreter"
},
"out_file": {
"type": "string",
"alias": ["out", "output", "out_log"],
"docDefault": "~/.pm2/logs/<app_name>-out.log",
"docDescription": "File path for stdout (each line is appended to this file)"
},
"error_file": {
"type": "string",
"alias": ["error", "err", "err_file", "err_log"],
"docDefault": "~/.pm2/logs/<app_name>-error.err",
"docDescription": "File path for stderr (each line is appended to this file)"
},
"log_file": {
"type": [
"boolean",
"string"
],
"alias": "log",
"docDefault": "/dev/null",
"docDescription": "File path for combined stdout and stderr (each line is appended to this file)"
},
"disable_logs": {
"type": "boolean",
"docDefault": false,
"docDescription": "Disable all logs storage"
},
"log_type": {
"type": "string",
"docDescription": "Define a specific log output type, possible value: json"
},
"log_date_format": {
"type": "string",
"docDescription": "Format for log timestamps in day.js format (eg YYYY-MM-DD HH:mm Z)"
},
"time": {
"type": "boolean"
},
"env": {
"type": [
"object",
"string"
],
"docDescription": "Specify environment variables to be injected"
},
"^env_\\S*$": {
"type": [
"object",
"string"
],
"docDescription": "Specify environment variables to be injected when using --env <env_name>"
},
"max_memory_restart": {
"type": [
"string",
"number"
],
"regex": "^\\d+(G|M|K)?$",
"ext_type": "sbyte",
"desc": "it should be a NUMBER - byte, \"[NUMBER]G\"(Gigabyte), \"[NUMBER]M\"(Megabyte) or \"[NUMBER]K\"(Kilobyte)",
"docDescription": "Restart the app if an amount of memory is exceeded (format: /[0-9](K&#124;M&#124;G)?/ K for KB, 'M' for MB, 'G' for GB, default to B)"
},
"pid_file": {
"type": "string",
"alias": "pid",
"docDefault": "~/.pm2/pids/app_name-id.pid",
"docDescription": "File path where the pid of the started process is written by pm2"
},
"restart_delay": {
"type" : "number",
"docDefault": 0,
"docDescription": "Time in ms to wait before restarting a crashing app"
},
"exp_backoff_restart_delay": {
"type": "number",
"docDefault": 0,
"docDescription": "Restart Time in ms to wait before restarting a crashing app"
},
"source_map_support": {
"type": "boolean",
"docDefault": true,
"docDescription": "Enable or disable the source map support"
},
"disable_source_map_support": {
"type": "boolean",
"docDefault": false,
"docDescription": "Enable or disable the source map support"
},
"wait_ready": {
"type": "boolean",
"docDefault": false,
"docDescription": "Make the process wait for a process.send('ready')"
},
"instances": {
"type": "number",
"docDefault": 1,
"docDescription": "Number of instances to be started in cluster mode"
},
"kill_timeout": {
"type": "number",
"docDefault": 1600,
"docDescription": "Time in ms before sending the final SIGKILL signal after SIGINT"
},
"shutdown_with_message": {
"type": "boolean",
"docDefault": false,
"docDescription": "Shutdown an application with process.send('shutdown') instead of process.kill(pid, SIGINT)"
},
"listen_timeout": {
"type": "number",
"docDescription": "Time in ms before forcing a reload if app is still not listening/has still note sent ready"
},
"cron_restart": {
"type": [
"string",
"number"
],
"alias": "cron",
"docDescription": "A cron pattern to restart your app"
},
"merge_logs": {
"type": "boolean",
"alias" : "combine_logs",
"docDefault": false,
"docDescription": "In cluster mode, merge each type of logs into a single file (instead of having one for each cluster)"
},
"vizion": {
"type": "boolean",
"default" : true,
"docDefault" : "True",
"docDescription": "Enable or disable the versioning metadatas (vizion library)"
},
"autostart": {
"type": "boolean",
"default": true,
"docDefault": "True",
"docDescription": "Enable or disable auto start when adding process"
},
"autorestart": {
"type": "boolean",
"default": true,
"docDefault": "True",
"docDescription": "Enable or disable auto restart after process failure"
},
"stop_exit_codes": {
"type": [
"array",
"number"
],
"docDescription": "List of exit codes that should allow the process to stop (skip autorestart)."
},
"watch_delay": {
"type": "number",
"docDefault": "True",
"docDescription": "Restart delay on file change detected"
},
"watch": {
"type": [
"boolean",
"array",
"string"
],
"docDefault": false,
"docDescription": "Enable or disable the watch mode"
},
"ignore_watch": {
"type": [
"array",
"string"
],
"docDescription": "List of paths to ignore (regex)"
},
"watch_options": {
"type": "object",
"docDescription": "Object that will be used as an options with chokidar (refer to chokidar documentation)"
},
"min_uptime": {
"type": [
"number",
"string"
],
"regex": "^\\d+(h|m|s)?$",
"desc": "it should be a NUMBER - milliseconds, \"[NUMBER]h\"(hours), \"[NUMBER]m\"(minutes) or \"[NUMBER]s\"(seconds)",
"min": 100,
"ext_type": "stime",
"docDefault": 1000,
"docDescription": "Minimum uptime of the app to be considered started (format is /[0-9]+(h&#124;m&#124;s)?/, for hours, minutes, seconds, docDefault to ms)"
},
"max_restarts": {
"type": "number",
"min": 0,
"docDefault": 16,
"docDescription": "Number of times a script is restarted when it exits in less than min_uptime"
},
"execute_command": {
"type": "boolean"
},
"exec_mode": {
"type": "string",
"regex": "^(cluster|fork)(_mode)?$",
"desc": "it should be \"cluster\"(\"cluster_mode\") or \"fork\"(\"fork_mode\") only",
"docDefault": "fork",
"docDescription": "Set the execution mode, possible values: fork&#124;cluster"
},
"force": {
"type": "boolean",
"docDefault": false,
"docDescription": "Start a script even if it is already running (only the script path is considered)"
},
"append_env_to_name": {
"type": "boolean",
"docDefault": false,
"docDescription": "Append the environment name to the app name"
},
"post_update": {
"type": "array",
"docDescription": "List of commands executed after a pull/upgrade operation performed from Keymetrics dashboard"
},
"trace": {
"type": [
"boolean"
],
"docDefault": false,
"docDescription": "Enable or disable the transaction tracing"
},
"disable_trace": {
"type": [
"boolean"
],
"docDefault": true,
"docDescription": "Enable or disable the transaction tracing"
},
"v8": {
"type": [
"boolean"
]
},
"event_loop_inspector": {
"type": [
"boolean"
]
},
"deep_monitoring": {
"type": [
"boolean"
]
},
"increment_var": {
"type": "string",
"docDescription": "Specify the name of an environment variable to inject which increments for each cluster"
},
"instance_var": {
"type": "string",
"default": "NODE_APP_INSTANCE",
"docDefault": "NODE_APP_INSTANCE",
"docDescription": "Rename the NODE_APP_INSTANCE environment variable"
},
"pmx": {
"type": ["boolean", "string"],
"default": true,
"docDefault": "True",
"docDescription": "Enable or disable pmx wrapping"
},
"automation": {
"type": "boolean",
"default": true,
"docDefault": "True",
"docDescription": "Enable or disable pmx wrapping"
},
"treekill": {
"type": "boolean",
"default": true,
"docDefault": "True",
"docDescription": "Only kill the main process, not detached children"
},
"port": {
"type": "number",
"docDescription": "Shortcut to inject a PORT environment variable"
},
"username" : {
"type": "string",
"docDescription": "Current user that started the process"
},
"uid": {
"type" : [
"number",
"string"
],
"alias": "user",
"docDefault": "Current user uid",
"docDescription": "Set user id"
},
"gid": {
"type" : [
"number",
"string"
],
"docDefault": "Current user gid",
"docDescription": "Set group id"
},
"windowsHide": {
"type": "boolean",
"docDefault": "True",
"docDescription": "Enable or disable the Windows popup when starting an app",
"default": true
},
"kill_retry_time": {
"type": "number",
"default" : 100
},
"write": {
"type": "boolean"
},
"io": {
"type": "object",
"docDescription": "Specify apm values and configuration"
}
}

779
api.hyungi.net/node_modules/pm2/lib/Client.js generated vendored Normal file
View File

@@ -0,0 +1,779 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
var debug = require('debug')('pm2:client');
var Common = require('./Common.js');
var KMDaemon = require('@pm2/agent/src/InteractorClient');
var rpc = require('pm2-axon-rpc');
var forEach = require('async/forEach');
var axon = require('pm2-axon');
var util = require('util');
var fs = require('fs');
var path = require('path');
var pkg = require('../package.json');
var which = require('./tools/which.js');
function noop() {}
var Client = module.exports = function(opts) {
if (!opts) opts = {};
if (!opts.conf)
this.conf = require('../constants.js');
else {
this.conf = opts.conf;
}
this.daemon_mode = typeof(opts.daemon_mode) === 'undefined' ? true : opts.daemon_mode;
this.pm2_home = this.conf.PM2_ROOT_PATH;
this.secret_key = opts.secret_key;
this.public_key = opts.public_key;
this.machine_name = opts.machine_name;
// Create all folders and files needed
// Client depends to that to interact with PM2 properly
this.initFileStructure(this.conf);
debug('Using RPC file %s', this.conf.DAEMON_RPC_PORT);
debug('Using PUB file %s', this.conf.DAEMON_PUB_PORT);
this.rpc_socket_file = this.conf.DAEMON_RPC_PORT;
this.pub_socket_file = this.conf.DAEMON_PUB_PORT;
};
// @breaking change (noDaemonMode has been drop)
// @todo ret err
Client.prototype.start = function(cb) {
var that = this;
this.pingDaemon(function(daemonAlive) {
if (daemonAlive === true)
return that.launchRPC(function(err, meta) {
return cb(null, {
daemon_mode : that.conf.daemon_mode,
new_pm2_instance : false,
rpc_socket_file : that.rpc_socket_file,
pub_socket_file : that.pub_socket_file,
pm2_home : that.pm2_home
});
});
/**
* No Daemon mode
*/
if (that.daemon_mode === false) {
var Daemon = require('./Daemon.js');
var daemon = new Daemon({
pub_socket_file : that.conf.DAEMON_PUB_PORT,
rpc_socket_file : that.conf.DAEMON_RPC_PORT,
pid_file : that.conf.PM2_PID_FILE_PATH,
ignore_signals : true
});
console.log('Launching in no daemon mode');
daemon.innerStart(function() {
KMDaemon.launchAndInteract(that.conf, {
machine_name : that.machine_name,
public_key : that.public_key,
secret_key : that.secret_key,
pm2_version : pkg.version
}, function(err, data, interactor_proc) {
that.interactor_process = interactor_proc;
});
that.launchRPC(function(err, meta) {
return cb(null, {
daemon_mode : that.conf.daemon_mode,
new_pm2_instance : true,
rpc_socket_file : that.rpc_socket_file,
pub_socket_file : that.pub_socket_file,
pm2_home : that.pm2_home
});
});
});
return false;
}
/**
* Daemon mode
*/
that.launchDaemon(function(err, child) {
if (err) {
Common.printError(err);
return cb ? cb(err) : process.exit(that.conf.ERROR_EXIT);
}
if (!process.env.PM2_DISCRETE_MODE)
Common.printOut(that.conf.PREFIX_MSG + 'PM2 Successfully daemonized');
that.launchRPC(function(err, meta) {
return cb(null, {
daemon_mode : that.conf.daemon_mode,
new_pm2_instance : true,
rpc_socket_file : that.rpc_socket_file,
pub_socket_file : that.pub_socket_file,
pm2_home : that.pm2_home
});
});
});
});
};
// Init file structure of pm2_home
// This includes
// - pm2 pid and log path
// - rpc and pub socket for command execution
Client.prototype.initFileStructure = function (opts) {
if (!fs.existsSync(opts.DEFAULT_LOG_PATH)) {
try {
require('mkdirp').sync(opts.DEFAULT_LOG_PATH);
} catch (e) {
console.error(e.stack || e);
}
}
if (!fs.existsSync(opts.DEFAULT_PID_PATH)) {
try {
require('mkdirp').sync(opts.DEFAULT_PID_PATH);
} catch (e) {
console.error(e.stack || e);
}
}
if (!fs.existsSync(opts.PM2_MODULE_CONF_FILE)) {
try {
fs.writeFileSync(opts.PM2_MODULE_CONF_FILE, "{}");
} catch (e) {
console.error(e.stack || e);
}
}
if (!fs.existsSync(opts.DEFAULT_MODULE_PATH)) {
try {
require('mkdirp').sync(opts.DEFAULT_MODULE_PATH);
} catch (e) {
console.error(e.stack || e);
}
}
if (process.env.PM2_DISCRETE_MODE) {
try {
fs.writeFileSync(path.join(opts.PM2_HOME, 'touch'), Date.now().toString());
} catch(e) {
debug(e.stack || e);
}
}
if (!process.env.PM2_PROGRAMMATIC && !fs.existsSync(path.join(opts.PM2_HOME, 'touch'))) {
var vCheck = require('./VersionCheck.js')
vCheck({
state: 'install',
version: pkg.version
})
var dt = fs.readFileSync(path.join(__dirname, opts.PM2_BANNER));
console.log(dt.toString());
try {
fs.writeFileSync(path.join(opts.PM2_HOME, 'touch'), Date.now().toString());
} catch(e) {
debug(e.stack || e);
}
}
};
Client.prototype.close = function(cb) {
var that = this;
forEach([
that.disconnectRPC.bind(that),
that.disconnectBus.bind(that)
], function(fn, next) {
fn(next)
}, cb);
};
/**
* Launch the Daemon by forking this same file
* The method Client.remoteWrapper will be called
*
* @method launchDaemon
* @param {Object} opts
* @param {Object} [opts.interactor=true] allow to disable interaction on launch
*/
Client.prototype.launchDaemon = function(opts, cb) {
if (typeof(opts) == 'function') {
cb = opts;
opts = {
interactor : true
};
}
var that = this
var ClientJS = path.resolve(path.dirname(module.filename), 'Daemon.js');
var node_args = [];
var out, err;
// if (process.env.TRAVIS) {
// // Redirect PM2 internal err and out to STDERR STDOUT when running with Travis
// out = 1;
// err = 2;
// }
// else {
out = fs.openSync(that.conf.PM2_LOG_FILE_PATH, 'a'),
err = fs.openSync(that.conf.PM2_LOG_FILE_PATH, 'a');
//}
if (this.conf.LOW_MEMORY_ENVIRONMENT) {
var os = require('os');
node_args.push('--gc-global'); // Does full GC (smaller memory footprint)
node_args.push('--max-old-space-size=' + Math.floor(os.totalmem() / 1024 / 1024));
}
// Node.js tuning for better performance
//node_args.push('--expose-gc'); // Allows manual GC in the code
/**
* Add node [arguments] depending on PM2_NODE_OPTIONS env variable
*/
if (process.env.PM2_NODE_OPTIONS)
node_args = node_args.concat(process.env.PM2_NODE_OPTIONS.split(' '));
node_args.push(ClientJS);
if (!process.env.PM2_DISCRETE_MODE)
Common.printOut(that.conf.PREFIX_MSG + 'Spawning PM2 daemon with pm2_home=' + this.pm2_home);
var interpreter = 'node';
if (which('node') == null)
interpreter = process.execPath;
var child = require('child_process').spawn(interpreter, node_args, {
detached : true,
cwd : that.conf.cwd || process.cwd(),
windowsHide: true,
env : Object.assign({
'SILENT' : that.conf.DEBUG ? !that.conf.DEBUG : true,
'PM2_HOME' : that.pm2_home
}, process.env),
stdio : ['ipc', out, err]
});
function onError(e) {
console.error(e.message || e);
return cb ? cb(e.message || e) : false;
}
child.once('error', onError);
child.unref();
child.once('message', function(msg) {
debug('PM2 daemon launched with return message: ', msg);
child.removeListener('error', onError);
child.disconnect();
if (opts && opts.interactor == false)
return cb(null, child);
if (process.env.PM2_NO_INTERACTION == 'true')
return cb(null, child);
/**
* Here the Keymetrics agent is launched automaticcaly if
* it has been already configured before (via pm2 link)
*/
KMDaemon.launchAndInteract(that.conf, {
machine_name : that.machine_name,
public_key : that.public_key,
secret_key : that.secret_key,
pm2_version : pkg.version
}, function(err, data, interactor_proc) {
that.interactor_process = interactor_proc;
return cb(null, child);
});
});
};
/**
* Ping the daemon to know if it alive or not
* @api public
* @method pingDaemon
* @param {} cb
* @return
*/
Client.prototype.pingDaemon = function pingDaemon(cb) {
var req = axon.socket('req');
var client = new rpc.Client(req);
var that = this;
debug('[PING PM2] Trying to connect to server');
client.sock.once('reconnect attempt', function() {
client.sock.close();
debug('Daemon not launched');
process.nextTick(function() {
return cb(false);
});
});
client.sock.once('error', function(e) {
if (e.code === 'EACCES') {
fs.stat(that.conf.DAEMON_RPC_PORT, function(e, stats) {
if (stats.uid === 0) {
console.error(that.conf.PREFIX_MSG_ERR + 'Permission denied, to give access to current user:');
console.log('$ sudo chown ' + process.env.USER + ':' + process.env.USER + ' ' + that.conf.DAEMON_RPC_PORT + ' ' + that.conf.DAEMON_PUB_PORT);
}
else
console.error(that.conf.PREFIX_MSG_ERR + 'Permission denied, check permissions on ' + that.conf.DAEMON_RPC_PORT);
process.exit(1);
});
}
else
console.error(e.message || e);
});
client.sock.once('connect', function() {
client.sock.once('close', function() {
return cb(true);
});
client.sock.close();
debug('Daemon alive');
});
req.connect(this.rpc_socket_file);
};
/**
* Methods to interact with the Daemon via RPC
* This method wait to be connected to the Daemon
* Once he's connected it trigger the command parsing (on ./bin/pm2 file, at the end)
* @method launchRPC
* @params {function} [cb]
* @return
*/
Client.prototype.launchRPC = function launchRPC(cb) {
var self = this;
debug('Launching RPC client on socket file %s', this.rpc_socket_file);
var req = axon.socket('req');
this.client = new rpc.Client(req);
var connectHandler = function() {
self.client.sock.removeListener('error', errorHandler);
debug('RPC Connected to Daemon');
if (cb) {
setTimeout(function() {
cb(null);
}, 4);
}
};
var errorHandler = function(e) {
self.client.sock.removeListener('connect', connectHandler);
if (cb) {
return cb(e);
}
};
this.client.sock.once('connect', connectHandler);
this.client.sock.once('error', errorHandler);
this.client_sock = req.connect(this.rpc_socket_file);
};
/**
* Methods to close the RPC connection
* @callback cb
*/
Client.prototype.disconnectRPC = function disconnectRPC(cb) {
var that = this;
if (!cb) cb = noop;
if (!this.client_sock || !this.client_sock.close) {
this.client = null;
return process.nextTick(function() {
cb(new Error('SUB connection to PM2 is not launched'));
});
}
if (this.client_sock.connected === false ||
this.client_sock.closing === true) {
this.client = null;
return process.nextTick(function() {
cb(new Error('RPC already being closed'));
});
}
try {
var timer;
that.client_sock.once('close', function() {
clearTimeout(timer);
that.client = null;
debug('PM2 RPC cleanly closed');
return cb(null, { msg : 'RPC Successfully closed' });
});
timer = setTimeout(function() {
if (that.client_sock.destroy)
that.client_sock.destroy();
that.client = null;
return cb(null, { msg : 'RPC Successfully closed via timeout' });
}, 200);
that.client_sock.close();
} catch(e) {
debug('Error while disconnecting RPC PM2', e.stack || e);
return cb(e);
}
return false;
};
Client.prototype.launchBus = function launchEventSystem(cb) {
var self = this;
this.sub = axon.socket('sub-emitter');
this.sub_sock = this.sub.connect(this.pub_socket_file);
this.sub_sock.once('connect', function() {
return cb(null, self.sub, self.sub_sock);
});
};
Client.prototype.disconnectBus = function disconnectBus(cb) {
if (!cb) cb = noop;
var that = this;
if (!this.sub_sock || !this.sub_sock.close) {
that.sub = null;
return process.nextTick(function() {
cb(null, { msg : 'bus was not connected'});
});
}
if (this.sub_sock.connected === false ||
this.sub_sock.closing === true) {
that.sub = null;
return process.nextTick(function() {
cb(new Error('SUB connection is already being closed'));
});
}
try {
var timer;
that.sub_sock.once('close', function() {
that.sub = null;
clearTimeout(timer);
debug('PM2 PUB cleanly closed');
return cb();
});
timer = setTimeout(function() {
if (Client.sub_sock.destroy)
that.sub_sock.destroy();
return cb();
}, 200);
this.sub_sock.close();
} catch(e) {
return cb(e);
}
};
/**
* Description
* @method gestExposedMethods
* @param {} cb
* @return
*/
Client.prototype.getExposedMethods = function getExposedMethods(cb) {
this.client.methods(cb);
};
/**
* Description
* @method executeRemote
* @param {} method
* @param {} env
* @param {} fn
* @return
*/
Client.prototype.executeRemote = function executeRemote(method, app_conf, fn) {
var self = this;
// stop watch on stop | env is the process id
if (method.indexOf('stop') !== -1) {
this.stopWatch(method, app_conf);
}
// stop watching when process is deleted
else if (method.indexOf('delete') !== -1) {
this.stopWatch(method, app_conf);
}
// stop everything on kill
else if (method.indexOf('kill') !== -1) {
this.stopWatch('deleteAll', app_conf);
}
else if (method.indexOf('restartProcessId') !== -1 && process.argv.indexOf('--watch') > -1) {
delete app_conf.env.current_conf.watch;
this.toggleWatch(method, app_conf);
}
if (!this.client || !this.client.call) {
this.start(function(error) {
if (error) {
if (fn)
return fn(error);
console.error(error);
return process.exit(0);
}
if (self.client) {
return self.client.call(method, app_conf, fn);
}
});
return false;
}
debug('Calling daemon method pm2:%s on rpc socket:%s', method, this.rpc_socket_file);
return this.client.call(method, app_conf, fn);
};
Client.prototype.notifyGod = function(action_name, id, cb) {
this.executeRemote('notifyByProcessId', {
id : id,
action_name : action_name,
manually : true
}, function() {
debug('God notified');
return cb ? cb() : false;
});
};
Client.prototype.killDaemon = function killDaemon(fn) {
var timeout;
var that = this;
function quit() {
that.close(function() {
return fn ? fn(null, {success:true}) : false;
});
}
// under unix, we listen for signal (that is send by daemon to notify us that its shuting down)
if (process.platform !== 'win32' && process.platform !== 'win64') {
process.once('SIGQUIT', function() {
debug('Received SIGQUIT from pm2 daemon');
clearTimeout(timeout);
quit();
});
}
else {
// if under windows, try to ping the daemon to see if it still here
setTimeout(function() {
that.pingDaemon(function(alive) {
if (!alive) {
clearTimeout(timeout);
return quit();
}
});
}, 250)
}
timeout = setTimeout(function() {
quit();
}, 3000);
// Kill daemon
this.executeRemote('killMe', {pid : process.pid});
};
/**
* Description
* @method toggleWatch
* @param {String} pm2 method name
* @param {Object} application environment, should include id
* @param {Function} callback
*/
Client.prototype.toggleWatch = function toggleWatch(method, env, fn) {
debug('Calling toggleWatch');
this.client.call('toggleWatch', method, env, function() {
return fn ? fn() : false;
});
};
/**
* Description
* @method startWatch
* @param {String} pm2 method name
* @param {Object} application environment, should include id
* @param {Function} callback
*/
Client.prototype.startWatch = function restartWatch(method, env, fn) {
debug('Calling startWatch');
this.client.call('startWatch', method, env, function() {
return fn ? fn() : false;
});
};
/**
* Description
* @method stopWatch
* @param {String} pm2 method name
* @param {Object} application environment, should include id
* @param {Function} callback
*/
Client.prototype.stopWatch = function stopWatch(method, env, fn) {
debug('Calling stopWatch');
this.client.call('stopWatch', method, env, function() {
return fn ? fn() : false;
});
};
Client.prototype.getAllProcess = function(cb) {
var found_proc = [];
this.executeRemote('getMonitorData', {}, function(err, procs) {
if (err) {
Common.printError('Error retrieving process list: ' + err);
return cb(err);
}
return cb(null, procs);
});
};
Client.prototype.getAllProcessId = function(cb) {
var found_proc = [];
this.executeRemote('getMonitorData', {}, function(err, procs) {
if (err) {
Common.printError('Error retrieving process list: ' + err);
return cb(err);
}
return cb(null, procs.map(proc => proc.pm_id));
});
};
Client.prototype.getAllProcessIdWithoutModules = function(cb) {
var found_proc = [];
this.executeRemote('getMonitorData', {}, function(err, procs) {
if (err) {
Common.printError('Error retrieving process list: ' + err);
return cb(err);
}
var proc_ids = procs
.filter(proc => !proc.pm2_env.pmx_module)
.map(proc => proc.pm_id)
return cb(null, proc_ids);
});
};
Client.prototype.getProcessIdByName = function(name, force_all, cb) {
var found_proc = [];
var full_details = {};
if (typeof(cb) === 'undefined') {
cb = force_all;
force_all = false;
}
if (typeof(name) == 'number')
name = name.toString();
this.executeRemote('getMonitorData', {}, function(err, list) {
if (err) {
Common.printError('Error retrieving process list: ' + err);
return cb(err);
}
list.forEach(function(proc) {
if (proc.pm2_env.name == name || proc.pm2_env.pm_exec_path == path.resolve(name)) {
found_proc.push(proc.pm_id);
full_details[proc.pm_id] = proc;
}
});
return cb(null, found_proc, full_details);
});
};
Client.prototype.getProcessIdsByNamespace = function(namespace, force_all, cb) {
var found_proc = [];
var full_details = {};
if (typeof(cb) === 'undefined') {
cb = force_all;
force_all = false;
}
if (typeof(namespace) == 'number')
namespace = namespace.toString();
this.executeRemote('getMonitorData', {}, function(err, list) {
if (err) {
Common.printError('Error retrieving process list: ' + err);
return cb(err);
}
list.forEach(function(proc) {
if (proc.pm2_env.namespace == namespace) {
found_proc.push(proc.pm_id);
full_details[proc.pm_id] = proc;
}
});
return cb(null, found_proc, full_details);
});
};
Client.prototype.getProcessByName = function(name, cb) {
var found_proc = [];
this.executeRemote('getMonitorData', {}, function(err, list) {
if (err) {
Common.printError('Error retrieving process list: ' + err);
return cb(err);
}
list.forEach(function(proc) {
if (proc.pm2_env.name == name ||
proc.pm2_env.pm_exec_path == path.resolve(name)) {
found_proc.push(proc);
}
});
return cb(null, found_proc);
});
};
Client.prototype.getProcessByNameOrId = function (nameOrId, cb) {
var foundProc = [];
this.executeRemote('getMonitorData', {}, function (err, list) {
if (err) {
Common.printError('Error retrieving process list: ' + err);
return cb(err);
}
list.forEach(function (proc) {
if (proc.pm2_env.name === nameOrId ||
proc.pm2_env.pm_exec_path === path.resolve(nameOrId) ||
proc.pid === parseInt(nameOrId) ||
proc.pm2_env.pm_id === parseInt(nameOrId)) {
foundProc.push(proc);
}
});
return cb(null, foundProc);
});
};

904
api.hyungi.net/node_modules/pm2/lib/Common.js generated vendored Normal file
View File

@@ -0,0 +1,904 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
/**
* Common Utilities ONLY USED IN ->CLI<-
*/
var fs = require('fs');
var path = require('path');
var os = require('os');
var util = require('util');
var chalk = require('chalk');
var fclone = require('fclone');
var semver = require('semver');
var dayjs = require('dayjs');
var execSync = require('child_process').execSync;
var isBinary = require('./tools/isbinaryfile.js');
var cst = require('../constants.js');
var extItps = require('./API/interpreter.json');
var Config = require('./tools/Config');
var pkg = require('../package.json');
var which = require('./tools/which.js');
var Common = module.exports;
function homedir() {
var env = process.env;
var home = env.HOME;
var user = env.LOGNAME || env.USER || env.LNAME || env.USERNAME;
if (process.platform === 'win32') {
return env.USERPROFILE || env.HOMEDRIVE + env.HOMEPATH || home || null;
}
if (process.platform === 'darwin') {
return home || (user ? '/Users/' + user : null);
}
if (process.platform === 'linux') {
return home || (process.getuid() === 0 ? '/root' : (user ? '/home/' + user : null));
}
return home || null;
}
function resolveHome(filepath) {
if (filepath[0] === '~') {
return path.join(homedir(), filepath.slice(1));
}
return filepath;
}
Common.determineSilentCLI = function() {
// pm2 should ignore -s --silent -v if they are after '--'
var variadicArgsDashesPos = process.argv.indexOf('--');
var s1opt = process.argv.indexOf('--silent')
var s2opt = process.argv.indexOf('-s')
if (process.env.PM2_SILENT || (variadicArgsDashesPos > -1 &&
(s1opt != -1 && s1opt < variadicArgsDashesPos) &&
(s2opt != -1 != s2opt < variadicArgsDashesPos)) ||
(variadicArgsDashesPos == -1 && (s1opt > -1 || s2opt > -1))) {
for (var key in console){
var code = key.charCodeAt(0);
if (code >= 97 && code <= 122){
console[key] = function(){};
}
}
process.env.PM2_DISCRETE_MODE = true;
}
}
Common.printVersion = function() {
var variadicArgsDashesPos = process.argv.indexOf('--');
if (process.argv.indexOf('-v') > -1 && process.argv.indexOf('-v') < variadicArgsDashesPos) {
console.log(pkg.version);
process.exit(0);
}
}
Common.lockReload = function() {
try {
var t1 = fs.readFileSync(cst.PM2_RELOAD_LOCKFILE).toString();
// Check if content and if time < 30 return locked
// Else if content detected (lock file staled), allow and rewritte
if (t1 && t1 != '') {
var diff = dayjs().diff(parseInt(t1));
if (diff < cst.RELOAD_LOCK_TIMEOUT)
return diff;
}
} catch(e) {}
try {
// Write latest timestamp
fs.writeFileSync(cst.PM2_RELOAD_LOCKFILE, dayjs().valueOf().toString());
return 0;
} catch(e) {
console.error(e.message || e);
}
};
Common.unlockReload = function() {
try {
fs.writeFileSync(cst.PM2_RELOAD_LOCKFILE, '');
} catch(e) {
console.error(e.message || e);
}
};
/**
* Resolve app paths and replace missing values with defaults.
* @method prepareAppConf
* @param app {Object}
* @param {} cwd
* @param {} outputter
* @return app
*/
Common.prepareAppConf = function(opts, app) {
/**
* Minimum validation
*/
if (!app.script)
return new Error('No script path - aborting');
var cwd = null;
if (app.cwd) {
cwd = path.resolve(app.cwd);
process.env.PWD = app.cwd;
}
if (!app.node_args) {
app.node_args = [];
}
if (app.port && app.env) {
app.env.PORT = app.port;
}
// CWD option resolving
cwd && (cwd[0] != '/') && (cwd = path.resolve(process.cwd(), cwd));
cwd = cwd || opts.cwd;
// Full path script resolution
app.pm_exec_path = path.resolve(cwd, app.script);
// If script does not exist after resolution
if (!fs.existsSync(app.pm_exec_path)) {
var ckd;
// Try resolve command available in $PATH
if ((ckd = which(app.script))) {
if (typeof(ckd) !== 'string')
ckd = ckd.toString();
app.pm_exec_path = ckd;
}
else
// Throw critical error
return new Error(`Script not found: ${app.pm_exec_path}`);
}
/**
* Auto detect .map file and enable source map support automatically
*/
if (app.disable_source_map_support != true) {
try {
fs.accessSync(app.pm_exec_path + '.map', fs.R_OK);
app.source_map_support = true;
} catch(e) {}
delete app.disable_source_map_support;
}
delete app.script;
// Set current env by first adding the process environment and then extending/replacing it
// with env specified on command-line or JSON file.
var env = {};
/**
* Do not copy internal pm2 environment variables if acting on process
* is made from a programmatic script started by PM2 or if a pm_id is present in env
*/
if (cst.PM2_PROGRAMMATIC || process.env.pm_id)
Common.safeExtend(env, process.env);
else
env = process.env;
function filterEnv (envObj) {
if (app.filter_env == true)
return {}
if (typeof app.filter_env === 'string') {
delete envObj[app.filter_env]
return envObj
}
var new_env = {};
var allowedKeys = app.filter_env.reduce((acc, current) =>
acc.filter( item => !item.includes(current)), Object.keys(envObj))
allowedKeys.forEach( key => new_env[key] = envObj[key]);
return new_env
}
app.env = [
{}, (app.filter_env && app.filter_env.length > 0) ? filterEnv(process.env) : env, app.env || {}
].reduce(function(e1, e2){
return Object.assign(e1, e2);
});
app.pm_cwd = cwd;
// Interpreter
try {
Common.sink.resolveInterpreter(app);
} catch(e) {
return e
}
// Exec mode and cluster stuff
Common.sink.determineExecMode(app);
/**
* Scary
*/
var formated_app_name = app.name.replace(/[^a-zA-Z0-9\\.\\-]/g, '-');
['log', 'out', 'error', 'pid'].forEach(function(f){
var af = app[f + '_file'], ps, ext = (f == 'pid' ? 'pid':'log'), isStd = !~['log', 'pid'].indexOf(f);
if (af) af = resolveHome(af);
if ((f == 'log' && typeof af == 'boolean' && af) || (f != 'log' && !af)) {
ps = [cst['DEFAULT_' + ext.toUpperCase() + '_PATH'], formated_app_name + (isStd ? '-' + f : '') + '.' + ext];
} else if ((f != 'log' || (f == 'log' && af)) && af !== 'NULL' && af !== '/dev/null') {
ps = [cwd, af];
var dir = path.dirname(path.resolve(cwd, af));
if (!fs.existsSync(dir)) {
Common.printError(cst.PREFIX_MSG_WARNING + 'Folder does not exist: ' + dir);
Common.printOut(cst.PREFIX_MSG + 'Creating folder: ' + dir);
try {
require('mkdirp').sync(dir);
} catch (err) {
Common.printError(cst.PREFIX_MSG_ERR + 'Could not create folder: ' + path.dirname(af));
throw new Error('Could not create folder');
}
}
}
// PM2 paths
if (af !== 'NULL' && af !== '/dev/null') {
ps && (app['pm_' + (isStd ? f.substr(0, 3) + '_' : '') + ext + '_path'] = path.resolve.apply(null, ps));
} else if (path.sep === '\\') {
app['pm_' + (isStd ? f.substr(0, 3) + '_' : '') + ext + '_path'] = '\\\\.\\NUL';
} else {
app['pm_' + (isStd ? f.substr(0, 3) + '_' : '') + ext + '_path'] = '/dev/null';
}
delete app[f + '_file'];
});
return app;
};
/**
* Definition of known config file extensions with their type
*/
Common.knonwConfigFileExtensions = {
'.json': 'json',
'.yml': 'yaml',
'.yaml': 'yaml',
'.config.js': 'js',
'.config.cjs': 'js',
'.config.mjs': 'mjs'
}
/**
* Check if filename is a configuration file
* @param {string} filename
* @return {mixed} null if not conf file, json or yaml if conf
*/
Common.isConfigFile = function (filename) {
if (typeof (filename) !== 'string')
return null;
for (let extension in Common.knonwConfigFileExtensions) {
if (filename.indexOf(extension) !== -1) {
return Common.knonwConfigFileExtensions[extension];
}
}
return null;
};
Common.getConfigFileCandidates = function (name) {
return Object.keys(Common.knonwConfigFileExtensions).map((extension) => name + extension);
}
/**
* Parses a config file like ecosystem.config.js. Supported formats: JS, JSON, JSON5, YAML.
* @param {string} confString contents of the config file
* @param {string} filename path to the config file
* @return {Object} config object
*/
Common.parseConfig = function(confObj, filename) {
var yamljs = require('js-yaml');
var vm = require('vm');
var isConfigFile = Common.isConfigFile(filename);
if (!filename ||
filename == 'pipe' ||
filename == 'none' ||
isConfigFile == 'json') {
var code = '(' + confObj + ')';
var sandbox = {};
return vm.runInThisContext(code, sandbox, {
filename: path.resolve(filename),
displayErrors: false,
timeout: 1000
});
}
else if (isConfigFile == 'yaml') {
return yamljs.load(confObj.toString());
}
else if (isConfigFile == 'js' || isConfigFile == 'mjs') {
var confPath = require.resolve(path.resolve(filename));
delete require.cache[confPath];
return require(confPath);
}
};
Common.retErr = function(e) {
if (!e)
return new Error('Unidentified error');
if (e instanceof Error)
return e;
return new Error(e);
}
Common.sink = {};
Common.sink.determineCron = function(app) {
if (app.cron_restart == 0 || app.cron_restart == '0') {
Common.printOut(cst.PREFIX_MSG + 'disabling cron restart');
return
}
if (app.cron_restart) {
const Croner = require('croner');
try {
Common.printOut(cst.PREFIX_MSG + 'cron restart at ' + app.cron_restart);
Croner(app.cron_restart);
} catch(ex) {
return new Error(`Cron pattern error: ${ex.message}`);
}
}
};
/**
* Handle alias (fork <=> fork_mode, cluster <=> cluster_mode)
*/
Common.sink.determineExecMode = function(app) {
if (app.exec_mode)
app.exec_mode = app.exec_mode.replace(/^(fork|cluster)$/, '$1_mode');
/**
* Here we put the default exec mode
*/
if (!app.exec_mode &&
(app.instances >= 1 || app.instances === 0 || app.instances === -1) &&
app.exec_interpreter.indexOf('node') > -1) {
app.exec_mode = 'cluster_mode';
} else if (!app.exec_mode) {
app.exec_mode = 'fork_mode';
}
if (typeof app.instances == 'undefined')
app.instances = 1;
};
var resolveNodeInterpreter = function(app) {
if (app.exec_mode && app.exec_mode.indexOf('cluster') > -1) {
Common.printError(cst.PREFIX_MSG_WARNING + chalk.bold.yellow('Choosing the Node.js version in cluster mode is not supported'));
return false;
}
var nvm_path = cst.IS_WINDOWS ? process.env.NVM_HOME : process.env.NVM_DIR;
if (!nvm_path) {
Common.printError(cst.PREFIX_MSG_ERR + chalk.red('NVM is not available in PATH'));
Common.printError(cst.PREFIX_MSG_ERR + chalk.red('Fallback to node in PATH'));
var msg = cst.IS_WINDOWS
? 'https://github.com/coreybutler/nvm-windows/releases/'
: '$ curl https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash';
Common.printOut(cst.PREFIX_MSG_ERR + chalk.bold('Install NVM:\n' + msg));
}
else {
var node_version = app.exec_interpreter.split('@')[1];
var path_to_node = cst.IS_WINDOWS
? '/v' + node_version + '/node.exe'
: semver.satisfies(node_version, '>= 0.12.0')
? '/versions/node/v' + node_version + '/bin/node'
: '/v' + node_version + '/bin/node';
var nvm_node_path = path.join(nvm_path, path_to_node);
try {
fs.accessSync(nvm_node_path);
} catch(e) {
Common.printOut(cst.PREFIX_MSG + 'Installing Node v%s', node_version);
var nvm_bin = path.join(nvm_path, 'nvm.' + (cst.IS_WINDOWS ? 'exe' : 'sh'));
var nvm_cmd = cst.IS_WINDOWS
? nvm_bin + ' install ' + node_version
: '. ' + nvm_bin + ' ; nvm install ' + node_version;
Common.printOut(cst.PREFIX_MSG + 'Executing: %s', nvm_cmd);
execSync(nvm_cmd, {
cwd: path.resolve(process.cwd()),
env: process.env,
maxBuffer: 20 * 1024 * 1024
});
// in order to support both arch, nvm for Windows renames 'node.exe' to:
// 'node32.exe' for x32 arch
// 'node64.exe' for x64 arch
if (cst.IS_WINDOWS)
nvm_node_path = nvm_node_path.replace(/node/, 'node' + process.arch.slice(1))
}
Common.printOut(cst.PREFIX_MSG + chalk.green.bold('Setting Node to v%s (path=%s)'),
node_version,
nvm_node_path);
app.exec_interpreter = nvm_node_path;
}
};
/**
* Resolve interpreter
*/
Common.sink.resolveInterpreter = function(app) {
var noInterpreter = !app.exec_interpreter;
var extName = path.extname(app.pm_exec_path);
var betterInterpreter = extItps[extName];
// No interpreter defined and correspondance in schema hashmap
if (noInterpreter && betterInterpreter) {
app.exec_interpreter = betterInterpreter;
if (betterInterpreter == "python") {
if (which('python') == null) {
if (which('python3') == null)
Common.printError(cst.PREFIX_MSG_WARNING + chalk.bold.yellow('python and python3 binaries not available in PATH'));
else
app.exec_interpreter = 'python3';
}
}
}
// Else if no Interpreter detect if process is binary
else if (noInterpreter)
app.exec_interpreter = isBinary(app.pm_exec_path) ? 'none' : 'node';
else if (app.exec_interpreter.indexOf('node@') > -1)
resolveNodeInterpreter(app);
if (app.exec_interpreter.indexOf('python') > -1)
app.env.PYTHONUNBUFFERED = '1'
if (app.exec_interpreter == 'lsc') {
app.exec_interpreter = path.resolve(__dirname, '../node_modules/.bin/lsc');
}
if (app.exec_interpreter == 'coffee') {
app.exec_interpreter = path.resolve(__dirname, '../node_modules/.bin/coffee');
}
if (app.exec_interpreter != 'none' && which(app.exec_interpreter) == null) {
// If node is not present
if (app.exec_interpreter == 'node') {
Common.warn(`Using builtin node.js version on version ${process.version}`)
app.exec_interpreter = cst.BUILTIN_NODE_PATH
}
else
throw new Error(`Interpreter ${app.exec_interpreter} is NOT AVAILABLE in PATH. (type 'which ${app.exec_interpreter}' to double check.)`)
}
return app;
};
Common.deepCopy = Common.serialize = Common.clone = function(obj) {
if (obj === null || obj === undefined) return {};
return fclone(obj);
};
Common.errMod = function(msg) {
if (process.env.PM2_SILENT || process.env.PM2_PROGRAMMATIC === 'true') return false;
if (msg instanceof Error)
return console.error(msg.message);
return console.error(`${cst.PREFIX_MSG_MOD_ERR}${msg}`);
}
Common.err = function(msg) {
if (process.env.PM2_SILENT || process.env.PM2_PROGRAMMATIC === 'true') return false;
if (msg instanceof Error)
return console.error(`${cst.PREFIX_MSG_ERR}${msg.message}`);
return console.error(`${cst.PREFIX_MSG_ERR}${msg}`);
}
Common.printError = function(msg) {
if (process.env.PM2_SILENT || process.env.PM2_PROGRAMMATIC === 'true') return false;
if (msg instanceof Error)
return console.error(msg.message);
return console.error.apply(console, arguments);
};
Common.log = function(msg) {
if (process.env.PM2_SILENT || process.env.PM2_PROGRAMMATIC === 'true') return false;
return console.log(`${cst.PREFIX_MSG}${msg}`);
}
Common.info = function(msg) {
if (process.env.PM2_SILENT || process.env.PM2_PROGRAMMATIC === 'true') return false;
return console.log(`${cst.PREFIX_MSG_INFO}${msg}`);
}
Common.warn = function(msg) {
if (process.env.PM2_SILENT || process.env.PM2_PROGRAMMATIC === 'true') return false;
return console.log(`${cst.PREFIX_MSG_WARNING}${msg}`);
}
Common.logMod = function(msg) {
if (process.env.PM2_SILENT || process.env.PM2_PROGRAMMATIC === 'true') return false;
return console.log(`${cst.PREFIX_MSG_MOD}${msg}`);
}
Common.printOut = function() {
if (process.env.PM2_SILENT === 'true' || process.env.PM2_PROGRAMMATIC === 'true') return false;
return console.log.apply(console, arguments);
};
/**
* Raw extend
*/
Common.extend = function(destination, source) {
if (typeof destination !== 'object') {
destination = {};
}
if (!source || typeof source !== 'object') {
return destination;
}
Object.keys(source).forEach(function(new_key) {
if (source[new_key] != '[object Object]')
destination[new_key] = source[new_key];
});
return destination;
};
/**
* This is useful when starting script programmatically
*/
Common.safeExtend = function(origin, add){
if (!add || typeof add != 'object') return origin;
//Ignore PM2's set environment variables from the nested env
var keysToIgnore = ['name', 'exec_mode', 'env', 'args', 'pm_cwd', 'exec_interpreter', 'pm_exec_path', 'node_args', 'pm_out_log_path', 'pm_err_log_path', 'pm_pid_path', 'pm_id', 'status', 'pm_uptime', 'created_at', 'windowsHide', 'username', 'merge_logs', 'kill_retry_time', 'prev_restart_delay', 'instance_var', 'unstable_restarts', 'restart_time', 'axm_actions', 'pmx_module', 'command', 'watch', 'filter_env', 'versioning', 'vizion_runing', 'MODULE_DEBUG', 'pmx', 'axm_options', 'created_at', 'watch', 'vizion', 'axm_dynamic', 'axm_monitor', 'instances', 'automation', 'autostart', 'autorestart', 'stop_exit_codes', 'unstable_restart', 'treekill', 'exit_code', 'vizion'];
var keys = Object.keys(add);
var i = keys.length;
while (i--) {
//Only copy stuff into the env that we don't have already.
if(keysToIgnore.indexOf(keys[i]) == -1 && add[keys[i]] != '[object Object]')
origin[keys[i]] = add[keys[i]];
}
return origin;
};
/**
* Extend the app.env object of with the properties taken from the
* app.env_[envName] and deploy configuration.
* Also update current json attributes
*
* Used only for Configuration file processing
*
* @param {Object} app The app object.
* @param {string} envName The given environment name.
* @param {Object} deployConf Deployment configuration object (from JSON file or whatever).
* @returns {Object} The app.env variables object.
*/
Common.mergeEnvironmentVariables = function(app_env, env_name, deploy_conf) {
var app = fclone(app_env);
var new_conf = {
env : {}
}
// Stringify possible object
for (var key in app.env) {
if (typeof app.env[key] == 'object') {
app.env[key] = JSON.stringify(app.env[key]);
}
}
/**
* Extra configuration update
*/
Object.assign(new_conf, app);
if (env_name) {
// First merge variables from deploy.production.env object as least priority.
if (deploy_conf && deploy_conf[env_name] && deploy_conf[env_name]['env']) {
Object.assign(new_conf.env, deploy_conf[env_name]['env']);
}
Object.assign(new_conf.env, app.env);
// Then, last and highest priority, merge the app.env_production object.
if ('env_' + env_name in app) {
Object.assign(new_conf.env, app['env_' + env_name]);
}
else {
Common.printOut(cst.PREFIX_MSG_WARNING + chalk.bold('Environment [%s] is not defined in process file'), env_name);
}
}
delete new_conf.exec_mode
var res = {
current_conf: {}
}
Object.assign(res, new_conf.env);
Object.assign(res.current_conf, new_conf);
// #2541 force resolution of node interpreter
if (app.exec_interpreter &&
app.exec_interpreter.indexOf('@') > -1) {
resolveNodeInterpreter(app);
res.current_conf.exec_interpreter = app.exec_interpreter
}
return res
}
/**
* This function will resolve paths, option and environment
* CALLED before 'prepare' God call (=> PROCESS INITIALIZATION)
* @method resolveAppAttributes
* @param {Object} opts
* @param {Object} opts.cwd
* @param {Object} opts.pm2_home
* @param {Object} appConf application configuration
* @return app
*/
Common.resolveAppAttributes = function(opts, conf) {
var conf_copy = fclone(conf);
var app = Common.prepareAppConf(opts, conf_copy);
if (app instanceof Error) {
throw new Error(app.message);
}
return app;
}
/**
* Verify configurations
* Called on EVERY Operation (start/restart/reload/stop...)
* @param {Array} appConfs
* @returns {Array}
*/
Common.verifyConfs = function(appConfs) {
if (!appConfs || appConfs.length == 0) {
return [];
}
// Make sure it is an Array.
appConfs = [].concat(appConfs);
var verifiedConf = [];
for (var i = 0; i < appConfs.length; i++) {
var app = appConfs[i];
if (app.exec_mode)
app.exec_mode = app.exec_mode.replace(/^(fork|cluster)$/, '$1_mode');
// JSON conf: alias cmd to script
if (app.cmd && !app.script) {
app.script = app.cmd
delete app.cmd
}
// JSON conf: alias command to script
if (app.command && !app.script) {
app.script = app.command
delete app.command
}
if (!app.env) {
app.env = {}
}
// Render an app name if not existing.
Common.renderApplicationName(app);
if (app.execute_command == true) {
app.exec_mode = 'fork'
delete app.execute_command
}
app.username = Common.getCurrentUsername();
/**
* If command is like pm2 start "python xx.py --ok"
* Then automatically start the script with bash -c and set a name eq to command
*/
if (app.script && app.script.indexOf(' ') > -1 && cst.IS_WINDOWS === false) {
var _script = app.script;
if (which('bash')) {
app.script = 'bash';
app.args = ['-c', _script];
if (!app.name) {
app.name = _script
}
}
else if (which('sh')) {
app.script = 'sh';
app.args = ['-c', _script];
if (!app.name) {
app.name = _script
}
}
else {
warn('bash or sh not available in $PATH, keeping script as is')
}
}
/**
* Add log_date_format by default
*/
if (app.time || process.env.ASZ_MODE) {
app.log_date_format = 'YYYY-MM-DDTHH:mm:ss'
}
/**
* Checks + Resolve UID/GID
* comes from pm2 --uid <> --gid <> or --user
*/
if (app.uid || app.gid || app.user) {
// 1/ Check if windows
if (cst.IS_WINDOWS === true) {
Common.printError(cst.PREFIX_MSG_ERR + '--uid and --git does not works on windows');
return new Error('--uid and --git does not works on windows');
}
// 2/ Verify that user is root (todo: verify if other has right)
if (process.env.NODE_ENV != 'test' && process.getuid && process.getuid() !== 0) {
Common.printError(cst.PREFIX_MSG_ERR + 'To use --uid and --gid please run pm2 as root');
return new Error('To use UID and GID please run PM2 as root');
}
// 3/ Resolve user info via /etc/password
var passwd = require('./tools/passwd.js')
var users
try {
users = passwd.getUsers()
} catch(e) {
Common.printError(e);
return new Error(e);
}
var user_info = users[app.uid || app.user]
if (!user_info) {
Common.printError(`${cst.PREFIX_MSG_ERR} User ${app.uid || app.user} cannot be found`);
return new Error(`${cst.PREFIX_MSG_ERR} User ${app.uid || app.user} cannot be found`);
}
app.env.HOME = user_info.homedir
app.uid = parseInt(user_info.userId)
// 4/ Resolve group id if gid is specified
if (app.gid) {
var groups
try {
groups = passwd.getGroups()
} catch(e) {
Common.printError(e);
return new Error(e);
}
var group_info = groups[app.gid]
if (!group_info) {
Common.printError(`${cst.PREFIX_MSG_ERR} Group ${app.gid} cannot be found`);
return new Error(`${cst.PREFIX_MSG_ERR} Group ${app.gid} cannot be found`);
}
app.gid = parseInt(group_info.id)
} else {
app.gid = parseInt(user_info.groupId)
}
}
/**
* Specific options of PM2.io
*/
if (process.env.PM2_DEEP_MONITORING) {
app.deep_monitoring = true;
}
if (app.automation == false) {
app.pmx = false;
}
if (app.disable_trace) {
app.trace = false
delete app.disable_trace;
}
/**
* Instances params
*/
if (app.instances == 'max') {
app.instances = 0;
}
if (typeof(app.instances) === 'string') {
app.instances = parseInt(app.instances) || 0;
}
if (app.exec_mode != 'cluster_mode' &&
!app.instances &&
typeof(app.merge_logs) == 'undefined') {
app.merge_logs = true;
}
var ret;
if (app.cron_restart) {
if ((ret = Common.sink.determineCron(app)) instanceof Error)
return ret;
}
/**
* Now validation configuration
*/
var ret = Config.validateJSON(app);
if (ret.errors && ret.errors.length > 0){
ret.errors.forEach(function(err) { warn(err) });
return new Error(ret.errors);
}
verifiedConf.push(ret.config);
}
return verifiedConf;
}
/**
* Get current username
* Called on EVERY starting app
*
* @returns {String}
*/
Common.getCurrentUsername = function(){
var current_user = '';
if (os.userInfo) {
try {
current_user = os.userInfo().username;
} catch (err) {
// For the case of unhandled error for uv_os_get_passwd
// https://github.com/Unitech/pm2/issues/3184
}
}
if(current_user === '') {
current_user = process.env.USER || process.env.LNAME || process.env.USERNAME || process.env.SUDO_USER || process.env.C9_USER || process.env.LOGNAME;
}
return current_user;
}
/**
* Render an app name if not existing.
* @param {Object} conf
*/
Common.renderApplicationName = function(conf){
if (!conf.name && conf.script){
conf.name = conf.script !== undefined ? path.basename(conf.script) : 'undefined';
var lastDot = conf.name.lastIndexOf('.');
if (lastDot > 0){
conf.name = conf.name.slice(0, lastDot);
}
}
}
/**
* Show warnings
* @param {String} warning
*/
function warn(warning){
Common.printOut(cst.PREFIX_MSG_WARNING + warning);
}

304
api.hyungi.net/node_modules/pm2/lib/Configuration.js generated vendored Normal file
View File

@@ -0,0 +1,304 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
var Configuration = module.exports = {};
var fs = require('fs');
var Common = require('./Common');
var eachSeries = require('async/eachSeries');
var cst = require('../constants.js');
function splitKey(key) {
var values = [key];
if (key.indexOf('.') > -1)
values = key.match(/(?:[^."]+|"[^"]*")+/g).map(function(dt) { return dt.replace(/"/g, '') });
else if (key.indexOf(':') > -1)
values = key.match(/(?:[^:"]+|"[^"]*")+/g).map(function(dt) { return dt.replace(/"/g, '') });
return values;
}
function serializeConfiguration(json_conf) {
return JSON.stringify(json_conf, null, 4)
}
Configuration.set = function(key, value, cb) {
fs.readFile(cst.PM2_MODULE_CONF_FILE, function(err, data) {
if (err) return cb(err);
var json_conf = JSON.parse(data);
var values = splitKey(key);
if (values.length > 0) {
var levels = values;
var tmp = json_conf;
levels.forEach(function(key, index) {
if (index == levels.length -1)
tmp[key] = value;
else if (!tmp[key]) {
tmp[key] = {};
tmp = tmp[key];
}
else {
if (typeof(tmp[key]) != 'object')
tmp[key] = {};
tmp = tmp[key];
}
});
}
else {
if (json_conf[key] && typeof(json_conf[key]) === 'string')
Common.printOut(cst.PREFIX_MSG + 'Replacing current value key %s by %s', key, value);
json_conf[key] = value;
}
fs.writeFile(cst.PM2_MODULE_CONF_FILE, serializeConfiguration(json_conf), function(err, data) {
if (err) return cb(err);
return cb(null, json_conf);
});
return false;
});
};
Configuration.unset = function(key, cb) {
fs.readFile(cst.PM2_MODULE_CONF_FILE, function(err, data) {
if (err) return cb(err);
var json_conf = JSON.parse(data);
var values = splitKey(key);
if (values.length > 0) {
var levels = values;
var tmp = json_conf;
levels.forEach(function(key, index) {
if (index == levels.length -1)
delete tmp[key];
else if (!tmp[key]) {
tmp[key] = {};
tmp = tmp[key];
}
else {
if (typeof(tmp[key]) != 'object')
tmp[key] = {};
tmp = tmp[key];
}
});
}
else
delete json_conf[key];
if (err) return cb(err);
if (key === 'all')
json_conf = {};
fs.writeFile(cst.PM2_MODULE_CONF_FILE, serializeConfiguration(json_conf), function(err, data) {
if (err) return cb(err);
return cb(null, json_conf);
});
return false;
});
}
Configuration.setSyncIfNotExist = function(key, value) {
try {
var conf = JSON.parse(fs.readFileSync(cst.PM2_MODULE_CONF_FILE));
} catch(e) {
return null;
}
var values = splitKey(key);
var exists = false;
if (values.length > 1 && conf && conf[values[0]]) {
exists = Object.keys(conf[values[0]]).some(function(key) {
if (key == values[1])
return true;
return false;
});
}
if (exists === false)
return Configuration.setSync(key, value);
return null;
};
Configuration.setSync = function(key, value) {
try {
var data = fs.readFileSync(cst.PM2_MODULE_CONF_FILE);
} catch(e) {
return null;
}
var json_conf = JSON.parse(data);
var values = splitKey(key);
if (values.length > 0) {
var levels = values;
var tmp = json_conf;
levels.forEach(function(key, index) {
if (index == levels.length -1)
tmp[key] = value;
else if (!tmp[key]) {
tmp[key] = {};
tmp = tmp[key];
}
else {
if (typeof(tmp[key]) != 'object')
tmp[key] = {};
tmp = tmp[key];
}
});
}
else {
if (json_conf[key] && typeof(json_conf[key]) === 'string')
Common.printOut(cst.PREFIX_MSG + 'Replacing current value key %s by %s', key, value);
json_conf[key] = value;
}
if (key === 'all')
json_conf = {};
try {
fs.writeFileSync(cst.PM2_MODULE_CONF_FILE, serializeConfiguration(json_conf));
return json_conf;
} catch(e) {
console.error(e.message);
return null;
}
};
Configuration.unsetSync = function(key) {
try {
var data = fs.readFileSync(cst.PM2_MODULE_CONF_FILE);
} catch(e) {
return null;
}
var json_conf = JSON.parse(data);
var values = splitKey(key);
if (values.length > 0) {
var levels = values;
var tmp = json_conf;
levels.forEach(function(key, index) {
if (index == levels.length -1)
delete tmp[key];
else if (!tmp[key]) {
tmp[key] = {};
tmp = tmp[key];
}
else {
if (typeof(tmp[key]) != 'object')
tmp[key] = {};
tmp = tmp[key];
}
});
}
else
delete json_conf[key];
if (key === 'all')
json_conf = {};
try {
fs.writeFileSync(cst.PM2_MODULE_CONF_FILE, serializeConfiguration(json_conf));
} catch(e) {
console.error(e.message);
return null;
}
};
Configuration.multiset = function(serial, cb) {
var arrays = [];
serial = serial.match(/(?:[^ "]+|"[^"]*")+/g);
while (serial.length > 0)
arrays.push(serial.splice(0, 2));
eachSeries(arrays, function(el, next) {
Configuration.set(el[0], el[1], next);
}, cb);
};
Configuration.get = function(key, cb) {
Configuration.getAll(function(err, data) {
var climb = splitKey(key);
climb.some(function(val) {
if (!data[val]) {
data = null;
return true;
}
data = data[val];
return false;
});
if (!data) return cb({err : 'Unknown key'}, null);
return cb(null, data);
});
};
Configuration.getSync = function(key) {
try {
var data = Configuration.getAllSync();
} catch(e) {
return null;
}
var climb = splitKey(key);
climb.some(function(val) {
if (!data[val]) {
data = null;
return true;
}
data = data[val];
return false;
});
if (!data) return null;
return data;
};
Configuration.getAll = function(cb) {
fs.readFile(cst.PM2_MODULE_CONF_FILE, function(err, data) {
if (err) return cb(err);
return cb(null, JSON.parse(data));
});
};
Configuration.getAllSync = function() {
try {
return JSON.parse(fs.readFileSync(cst.PM2_MODULE_CONF_FILE));
} catch(e) {
console.error(e.stack || e);
return {};
}
};

455
api.hyungi.net/node_modules/pm2/lib/Daemon.js generated vendored Normal file
View File

@@ -0,0 +1,455 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
var debug = require('debug')('pm2:daemon');
var pkg = require('../package.json');
var cst = require('../constants.js');
var rpc = require('pm2-axon-rpc');
var axon = require('pm2-axon');
var domain = require('domain');
var Utility = require('./Utility.js');
var util = require('util');
var fs = require('fs');
var God = require('./God');
var eachLimit = require('async/eachLimit');
var fmt = require('./tools/fmt.js');
var semver = require('semver');
var Daemon = module.exports = function(opts) {
if (!opts) opts = {};
this.ignore_signals = opts.ignore_signals || false;
this.rpc_socket_ready = false;
this.pub_socket_ready = false;
this.pub_socket_file = opts.pub_socket_file || cst.DAEMON_PUB_PORT;
this.rpc_socket_file = opts.rpc_socket_file || cst.DAEMON_RPC_PORT;
this.pid_path = opts.pid_file || cst.PM2_PID_FILE_PATH;
};
Daemon.prototype.start = function() {
var that = this;
var d = domain.create();
d.once('error', function(err) {
fmt.sep();
fmt.title('PM2 global error caught');
fmt.field('Time', new Date());
console.error(err.message);
console.error(err.stack);
fmt.sep();
console.error('[PM2] Resurrecting PM2');
var path = cst.IS_WINDOWS ? __dirname + '/../bin/pm2' : process.env['_'];
var fork_new_pm2 = require('child_process').spawn('node', [path, 'update'], {
detached: true,
windowsHide: true,
stdio: 'inherit'
});
fork_new_pm2.on('close', function() {
console.log('PM2 successfully forked');
process.exit(0);
})
});
d.run(function() {
that.innerStart();
});
}
Daemon.prototype.innerStart = function(cb) {
var that = this;
if (!cb) cb = function() {
fmt.sep();
fmt.title('New PM2 Daemon started');
fmt.field('Time', new Date());
fmt.field('PM2 version', pkg.version);
fmt.field('Node.js version', process.versions.node);
fmt.field('Current arch', process.arch);
fmt.field('PM2 home', cst.PM2_HOME);
fmt.field('PM2 PID file', that.pid_path);
fmt.field('RPC socket file', that.rpc_socket_file);
fmt.field('BUS socket file', that.pub_socket_file);
fmt.field('Application log path', cst.DEFAULT_LOG_PATH);
fmt.field('Worker Interval', cst.WORKER_INTERVAL);
fmt.field('Process dump file', cst.DUMP_FILE_PATH);
fmt.field('Concurrent actions', cst.CONCURRENT_ACTIONS);
fmt.field('SIGTERM timeout', cst.KILL_TIMEOUT);
fmt.sep();
};
// Write Daemon PID into file
try {
fs.writeFileSync(that.pid_path, process.pid.toString());
} catch (e) {
console.error(e.stack || e);
}
if (this.ignore_signals != true)
this.handleSignals();
/**
* Pub system for real time notifications
*/
this.pub = axon.socket('pub-emitter');
this.pub_socket = this.pub.bind(this.pub_socket_file);
this.pub_socket.once('bind', function() {
fs.chmod(that.pub_socket_file, '775', function(e) {
if (e) console.error(e);
try {
if (process.env.PM2_SOCKET_USER && process.env.PM2_SOCKET_GROUP)
fs.chown(that.pub_socket_file,
parseInt(process.env.PM2_SOCKET_USER),
parseInt(process.env.PM2_SOCKET_GROUP), function(e) {
if (e) console.error(e);
});
} catch(e) {
console.error(e);
}
});
that.pub_socket_ready = true;
that.sendReady(cb);
});
/**
* Rep/Req - RPC system to interact with God
*/
this.rep = axon.socket('rep');
var server = new rpc.Server(this.rep);
this.rpc_socket = this.rep.bind(this.rpc_socket_file);
this.rpc_socket.once('bind', function() {
fs.chmod(that.rpc_socket_file, '775', function(e) {
if (e) console.error(e);
try {
if (process.env.PM2_SOCKET_USER && process.env.PM2_SOCKET_GROUP)
fs.chown(that.rpc_socket_file,
parseInt(process.env.PM2_SOCKET_USER),
parseInt(process.env.PM2_SOCKET_GROUP), function(e) {
if (e) console.error(e);
});
} catch(e) {
console.error(e);
}
});
that.rpc_socket_ready = true;
that.sendReady(cb);
});
/**
* Memory Snapshot
*/
function profile(type, msg, cb) {
if (semver.satisfies(process.version, '< 8'))
return cb(null, { error: 'Node.js is not on right version' })
var cmd
if (type === 'cpu') {
cmd = {
enable: 'Profiler.enable',
start: 'Profiler.start',
stop: 'Profiler.stop',
disable: 'Profiler.disable'
}
}
if (type == 'mem') {
cmd = {
enable: 'HeapProfiler.enable',
start: 'HeapProfiler.startSampling',
stop: 'HeapProfiler.stopSampling',
disable: 'HeapProfiler.disable'
}
}
const inspector = require('inspector')
var session = new inspector.Session()
session.connect()
var timeout = msg.timeout || 5000
session.post(cmd.enable, (err, data) => {
if (err) return cb(null, { error: err.message || err })
console.log(`Starting ${cmd.start}`)
session.post(cmd.start, (err, data) => {
if (err) return cb(null, { error: err.message || err })
setTimeout(() => {
session.post(cmd.stop, (err, data) => {
if (err) return cb(null, { error: err.message || err })
const profile = data.profile
console.log(`Stopping ${cmd.stop}`)
session.post(cmd.disable)
fs.writeFile(msg.pwd, JSON.stringify(profile), (err) => {
if (err) return cb(null, { error: err.message || err })
return cb(null, { file : msg.pwd })
})
})
}, timeout)
})
})
}
server.expose({
killMe : that.close.bind(this),
profileCPU : profile.bind(this, 'cpu'),
profileMEM : profile.bind(this, 'mem'),
prepare : God.prepare,
getMonitorData : God.getMonitorData,
startProcessId : God.startProcessId,
stopProcessId : God.stopProcessId,
restartProcessId : God.restartProcessId,
deleteProcessId : God.deleteProcessId,
sendLineToStdin : God.sendLineToStdin,
softReloadProcessId : God.softReloadProcessId,
reloadProcessId : God.reloadProcessId,
duplicateProcessId : God.duplicateProcessId,
resetMetaProcessId : God.resetMetaProcessId,
stopWatch : God.stopWatch,
startWatch : God.startWatch,
toggleWatch : God.toggleWatch,
notifyByProcessId : God.notifyByProcessId,
notifyKillPM2 : God.notifyKillPM2,
monitor : God.monitor,
unmonitor : God.unmonitor,
msgProcess : God.msgProcess,
sendDataToProcessId : God.sendDataToProcessId,
sendSignalToProcessId : God.sendSignalToProcessId,
sendSignalToProcessName : God.sendSignalToProcessName,
ping : God.ping,
getVersion : God.getVersion,
getReport : God.getReport,
reloadLogs : God.reloadLogs
});
this.startLogic();
}
Daemon.prototype.close = function(opts, cb) {
var that = this;
God.bus.emit('pm2:kill', {
status : 'killed',
msg : 'pm2 has been killed via CLI'
});
if (God.system_infos_proc !== null)
God.system_infos_proc.kill()
/**
* Cleanly kill pm2
*/
that.rpc_socket.close(function() {
that.pub_socket.close(function() {
// notify cli that the daemon is shuting down (only under unix since windows doesnt handle signals)
if (cst.IS_WINDOWS === false) {
try {
process.kill(parseInt(opts.pid), 'SIGQUIT');
} catch(e) {
console.error('Could not send SIGQUIT to CLI');
}
}
try {
fs.unlinkSync(that.pid_path);
} catch(e) {}
console.log('PM2 successfully stopped');
setTimeout(function() {
process.exit(cst.SUCCESS_EXIT);
}, 2);
});
});
}
Daemon.prototype.handleSignals = function() {
var that = this;
process.on('SIGTERM', that.gracefullExit.bind(this));
process.on('SIGINT', that.gracefullExit.bind(this));
process.on('SIGHUP', function() {});
process.on('SIGQUIT', that.gracefullExit.bind(this));
process.on('SIGUSR2', function() {
God.reloadLogs({}, function() {});
});
}
Daemon.prototype.sendReady = function(cb) {
// Send ready message to Client
if (this.rpc_socket_ready == true && this.pub_socket_ready == true) {
cb(null, {
pid : process.pid,
pm2_version : pkg.version
});
if (typeof(process.send) != 'function')
return false;
process.send({
online : true,
success : true,
pid : process.pid,
pm2_version : pkg.version
});
};
}
Daemon.prototype.gracefullExit = function() {
var that = this;
// never execute multiple gracefullExit simultaneously
// this can lead to loss of some apps in dump file
if (this.isExiting) return
this.isExiting = true
God.bus.emit('pm2:kill', {
status : 'killed',
msg : 'pm2 has been killed by SIGNAL'
});
console.log('pm2 has been killed by signal, dumping process list before exit...');
if (God.system_infos_proc !== null)
God.system_infos_proc.kill()
God.dumpProcessList(function() {
var processes = God.getFormatedProcesses();
eachLimit(processes, 1, function(proc, next) {
console.log('Deleting process %s', proc.pm2_env.pm_id);
God.deleteProcessId(proc.pm2_env.pm_id, function() {
return next();
});
}, function(err) {
try {
fs.unlinkSync(that.pid_path);
} catch(e) {}
setTimeout(function() {
that.isExiting = false
console.log('Exited peacefully');
process.exit(cst.SUCCESS_EXIT);
}, 2);
});
});
}
Daemon.prototype.startLogic = function() {
var that = this;
/**
* Action treatment specifics
* Attach actions to pm2_env.axm_actions variables (name + options)
*/
God.bus.on('axm:action', function axmActions(msg) {
var pm2_env = msg.process;
var exists = false;
var axm_action = msg.data;
if (!pm2_env || !God.clusters_db[pm2_env.pm_id])
return console.error('AXM ACTION Unknown id %s', pm2_env.pm_id);
if (!God.clusters_db[pm2_env.pm_id].pm2_env.axm_actions)
God.clusters_db[pm2_env.pm_id].pm2_env.axm_actions = [];
God.clusters_db[pm2_env.pm_id].pm2_env.axm_actions.forEach(function(actions) {
if (actions.action_name == axm_action.action_name)
exists = true;
});
if (exists === false) {
debug('Adding action', axm_action);
God.clusters_db[pm2_env.pm_id].pm2_env.axm_actions.push(axm_action);
}
msg = null;
});
/**
* Configure module
*/
God.bus.on('axm:option:configuration', function axmMonitor(msg) {
if (!msg.process)
return console.error('[axm:option:configuration] no process defined');
if (!God.clusters_db[msg.process.pm_id])
return console.error('[axm:option:configuration] Unknown id %s', msg.process.pm_id);
try {
// Application Name nverride
if (msg.data.name)
God.clusters_db[msg.process.pm_id].pm2_env.name = msg.data.name;
Object.keys(msg.data).forEach(function(conf_key) {
God.clusters_db[msg.process.pm_id].pm2_env.axm_options[conf_key] = Utility.clone(msg.data[conf_key]);
});
} catch(e) {
console.error(e.stack || e);
}
msg = null;
});
/**
* Process monitoring data (probes)
*/
God.bus.on('axm:monitor', function axmMonitor(msg) {
if (!msg.process)
return console.error('[axm:monitor] no process defined');
if (!msg.process || !God.clusters_db[msg.process.pm_id])
return console.error('AXM MONITOR Unknown id %s', msg.process.pm_id);
Object.assign(God.clusters_db[msg.process.pm_id].pm2_env.axm_monitor, Utility.clone(msg.data));
msg = null;
});
/**
* Broadcast messages
*/
God.bus.onAny(function(event, data_v) {
if (['axm:action',
'axm:monitor',
'axm:option:setPID',
'axm:option:configuration'].indexOf(event) > -1) {
data_v = null;
return false;
}
that.pub.emit(event, Utility.clone(data_v));
data_v = null;
});
};
if (require.main === module) {
process.title = process.env.PM2_DAEMON_TITLE || 'PM2 v' + pkg.version + ': God Daemon (' + process.env.PM2_HOME + ')';
var daemon = new Daemon();
daemon.start();
}

37
api.hyungi.net/node_modules/pm2/lib/Event.js generated vendored Normal file
View File

@@ -0,0 +1,37 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
var Utility = require('./Utility.js');
module.exports = function(God) {
God.notify = function(action_name, data, manually) {
God.bus.emit('process:event', {
event : action_name,
manually : typeof(manually) == 'undefined' ? false : true,
process : Utility.formatCLU(data),
at : Utility.getDate()
});
};
God.notifyByProcessId = function(opts, cb) {
if (typeof(opts.id) === 'undefined') { return cb(new Error('process id missing')); }
var proc = God.clusters_db[opts.id];
if (!proc) { return cb(new Error('process id doesnt exists')); }
God.bus.emit('process:event', {
event : opts.action_name,
manually : typeof(opts.manually) == 'undefined' ? false : true,
process : Utility.formatCLU(proc),
at : Utility.getDate()
});
process.nextTick(function() {
return cb ? cb(null) : false;
});
return false;
};
};

588
api.hyungi.net/node_modules/pm2/lib/God.js generated vendored Normal file
View File

@@ -0,0 +1,588 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
/******************************
* ______ _______ ______
* | __ \ | |__ |
* | __/ | __|
* |___| |__|_|__|______|
*
* Main Daemon side file
*
******************************/
var cluster = require('cluster');
var numCPUs = require('os').cpus() ? require('os').cpus().length : 1;
var path = require('path');
var EventEmitter2 = require('eventemitter2').EventEmitter2;
var fs = require('fs');
var vizion = require('vizion');
var debug = require('debug')('pm2:god');
var Utility = require('./Utility');
var cst = require('../constants.js');
var timesLimit = require('async/timesLimit');
var Configuration = require('./Configuration.js');
/**
* Override cluster module configuration
*/
cluster.setupMaster({
windowsHide: true,
exec : path.resolve(path.dirname(module.filename), 'ProcessContainer.js')
});
/**
* Expose God
*/
var God = module.exports = {
next_id : 0,
clusters_db : {},
configuration: {},
started_at : Date.now(),
system_infos_proc: null,
system_infos: null,
bus : new EventEmitter2({
wildcard: true,
delimiter: ':',
maxListeners: 1000
})
};
Utility.overrideConsole(God.bus);
/**
* Populate God namespace
*/
require('./Event.js')(God);
require('./God/Methods.js')(God);
require('./God/ForkMode.js')(God);
require('./God/ClusterMode.js')(God);
require('./God/Reload')(God);
require('./God/ActionMethods')(God);
require('./Watcher')(God);
God.init = function() {
require('./Worker.js')(this)
God.system_infos_proc = null
this.configuration = Configuration.getSync('pm2')
setTimeout(function() {
God.Worker.start()
}, 500)
}
God.writeExitSeparator = function(pm2_env, code, signal) {
try {
var exit_sep = `[PM2][${new Date().toISOString()}] app exited`
if (code)
exit_sep += `itself with exit code: ${code}`
if (signal)
exit_sep += `by an external signal: ${signal}`
exit_sep += '\n'
if (pm2_env.pm_out_log_path)
fs.writeFileSync(pm2_env.pm_out_log_path, exit_sep)
if (pm2_env.pm_err_log_path)
fs.writeFileSync(pm2_env.pm_err_log_path, exit_sep)
if (pm2_env.pm_log_path)
fs.writeFileSync(pm2_env.pm_log_path, exit_sep)
} catch(e) {
}
}
/**
* Init new process
*/
God.prepare = function prepare (env, cb) {
// generate a new unique id for each processes
env.env.unique_id = Utility.generateUUID()
// if the app is standalone, no multiple instance
if (typeof env.instances === 'undefined') {
env.vizion_running = false;
if (env.env && env.env.vizion_running) env.env.vizion_running = false;
if (env.status == cst.STOPPED_STATUS) {
env.pm_id = God.getNewId()
var clu = {
pm2_env : env,
process: {
}
}
God.clusters_db[env.pm_id] = clu
God.registerCron(env)
return cb(null, [ God.clusters_db[env.pm_id] ])
}
return God.executeApp(env, function (err, clu) {
if (err) return cb(err);
God.notify('start', clu, true);
return cb(null, [ Utility.clone(clu) ]);
});
}
// find how many replicate the user want
env.instances = parseInt(env.instances);
if (env.instances === 0) {
env.instances = numCPUs;
} else if (env.instances < 0) {
env.instances += numCPUs;
}
if (env.instances <= 0) {
env.instances = 1;
}
timesLimit(env.instances, 1, function (n, next) {
env.vizion_running = false;
if (env.env && env.env.vizion_running) {
env.env.vizion_running = false;
}
God.injectVariables(env, function inject (err, _env) {
if (err) return next(err);
return God.executeApp(Utility.clone(_env), function (err, clu) {
if (err) return next(err);
God.notify('start', clu, true);
// here call next wihtout an array because
// async.times aggregate the result into an array
return next(null, Utility.clone(clu));
});
});
}, cb);
};
/**
* Launch the specified script (present in env)
* @api private
* @method executeApp
* @param {Mixed} env
* @param {Function} cb
* @return Literal
*/
God.executeApp = function executeApp(env, cb) {
var env_copy = Utility.clone(env);
Utility.extend(env_copy, env_copy.env);
env_copy['status'] = env.autostart ? cst.LAUNCHING_STATUS : cst.STOPPED_STATUS;
env_copy['pm_uptime'] = Date.now();
env_copy['axm_actions'] = [];
env_copy['axm_monitor'] = {};
env_copy['axm_options'] = {};
env_copy['axm_dynamic'] = {};
env_copy['vizion_running'] =
env_copy['vizion_running'] !== undefined ? env_copy['vizion_running'] : false;
if (!env_copy.created_at)
env_copy['created_at'] = Date.now();
/**
* Enter here when it's the first time that the process is created
* 1 - Assign a new id
* 2 - Reset restart time and unstable_restarts
* 3 - Assign a log file name depending on the id
* 4 - If watch option is set, look for changes
*/
if (env_copy['pm_id'] === undefined) {
env_copy['pm_id'] = God.getNewId();
env_copy['restart_time'] = 0;
env_copy['unstable_restarts'] = 0;
// add -pm_id to pid file
env_copy.pm_pid_path = env_copy.pm_pid_path.replace(/-[0-9]+\.pid$|\.pid$/g, '-' + env_copy['pm_id'] + '.pid');
// If merge option, dont separate the logs
if (!env_copy['merge_logs']) {
['', '_out', '_err'].forEach(function(k){
var key = 'pm' + k + '_log_path';
env_copy[key] && (env_copy[key] = env_copy[key].replace(/-[0-9]+\.log$|\.log$/g, '-' + env_copy['pm_id'] + '.log'));
});
}
// Initiate watch file
if (env_copy['watch']) {
God.watch.enable(env_copy);
}
}
God.registerCron(env_copy)
if (env_copy['autostart'] === false) {
var clu = {pm2_env: env_copy, process: {pid: 0}};
God.clusters_db[env_copy.pm_id] = clu;
return cb(null, clu);
}
/** Callback when application is launched */
var readyCb = function ready(proc) {
// If vizion enabled run versioning retrieval system
if (proc.pm2_env.vizion !== false && proc.pm2_env.vizion !== "false")
God.finalizeProcedure(proc);
else
God.notify('online', proc);
if (proc.pm2_env.status !== cst.ERRORED_STATUS)
proc.pm2_env.status = cst.ONLINE_STATUS
console.log(`App [${proc.pm2_env.name}:${proc.pm2_env.pm_id}] online`);
if (cb) cb(null, proc);
}
if (env_copy.exec_mode === 'cluster_mode') {
/**
* Cluster mode logic (for NodeJS apps)
*/
God.nodeApp(env_copy, function nodeApp(err, clu) {
if (cb && err) return cb(err);
if (err) return false;
var old_env = God.clusters_db[clu.pm2_env.pm_id];
if (old_env) {
old_env = null;
God.clusters_db[clu.pm2_env.pm_id] = null;
}
God.clusters_db[clu.pm2_env.pm_id] = clu;
clu.once('error', function(err) {
console.error(err.stack || err);
try {
clu.destroy && clu.destroy();
}
catch (e) {
console.error(e.stack || e);
God.handleExit(clu, cst.ERROR_EXIT);
}
});
clu.once('disconnect', function() {
console.log('App name:%s id:%s disconnected', clu.pm2_env.name, clu.pm2_env.pm_id);
});
clu.once('exit', function cluExit(code, signal) {
//God.writeExitSeparator(clu.pm2_env, code, signal)
God.handleExit(clu, code || 0, signal || 'SIGINT');
});
return clu.once('online', function () {
if (!clu.pm2_env.wait_ready)
return readyCb(clu);
// Timeout if the ready message has not been sent before listen_timeout
var ready_timeout = setTimeout(function() {
God.bus.removeListener('process:msg', listener)
return readyCb(clu)
}, clu.pm2_env.listen_timeout || cst.GRACEFUL_LISTEN_TIMEOUT);
var listener = function (packet) {
if (packet.raw === 'ready' &&
packet.process.name === clu.pm2_env.name &&
packet.process.pm_id === clu.pm2_env.pm_id) {
clearTimeout(ready_timeout);
God.bus.removeListener('process:msg', listener)
return readyCb(clu)
}
}
God.bus.on('process:msg', listener);
});
});
}
else {
/**
* Fork mode logic
*/
God.forkMode(env_copy, function forkMode(err, clu) {
if (cb && err) return cb(err);
if (err) return false;
var old_env = God.clusters_db[clu.pm2_env.pm_id];
if (old_env) old_env = null;
God.clusters_db[env_copy.pm_id] = clu;
clu.once('error', function cluError(err) {
console.error(err.stack || err);
try {
clu.kill && clu.kill();
}
catch (e) {
console.error(e.stack || e);
God.handleExit(clu, cst.ERROR_EXIT);
}
});
clu.once('exit', function cluClose(code, signal) {
//God.writeExitSeparator(clu.pm2_env, code, signal)
if (clu.connected === true)
clu.disconnect && clu.disconnect();
clu._reloadLogs = null;
return God.handleExit(clu, code || 0, signal);
});
if (!clu.pm2_env.wait_ready)
return readyCb(clu);
// Timeout if the ready message has not been sent before listen_timeout
var ready_timeout = setTimeout(function() {
God.bus.removeListener('process:msg', listener)
return readyCb(clu)
}, clu.pm2_env.listen_timeout || cst.GRACEFUL_LISTEN_TIMEOUT);
var listener = function (packet) {
if (packet.raw === 'ready' &&
packet.process.name === clu.pm2_env.name &&
packet.process.pm_id === clu.pm2_env.pm_id) {
clearTimeout(ready_timeout);
God.bus.removeListener('process:msg', listener)
return readyCb(clu)
}
}
God.bus.on('process:msg', listener);
});
}
return false;
};
/**
* Handle logic when a process exit (Node or Fork)
* @method handleExit
* @param {} clu
* @param {} exit_code
* @return
*/
God.handleExit = function handleExit(clu, exit_code, kill_signal) {
console.log(`App [${clu.pm2_env.name}:${clu.pm2_env.pm_id}] exited with code [${exit_code}] via signal [${kill_signal || 'SIGINT'}]`)
var proc = this.clusters_db[clu.pm2_env.pm_id];
if (!proc) {
console.error('Process undefined ? with process id ', clu.pm2_env.pm_id);
return false;
}
var stopExitCodes = proc.pm2_env.stop_exit_codes !== undefined && proc.pm2_env.stop_exit_codes !== null ? proc.pm2_env.stop_exit_codes : [];
if (!Array.isArray(stopExitCodes)) {
stopExitCodes = [stopExitCodes];
}
var stopping = (proc.pm2_env.status == cst.STOPPING_STATUS
|| proc.pm2_env.status == cst.STOPPED_STATUS
|| proc.pm2_env.status == cst.ERRORED_STATUS)
|| (proc.pm2_env.autorestart === false || proc.pm2_env.autorestart === "false")
|| (stopExitCodes.map((strOrNum) => typeof strOrNum === 'string' ? parseInt(strOrNum, 10) : strOrNum)
.includes(exit_code));
var overlimit = false;
if (stopping) proc.process.pid = 0;
// Reset probes and actions
if (proc.pm2_env.axm_actions) proc.pm2_env.axm_actions = [];
if (proc.pm2_env.axm_monitor) proc.pm2_env.axm_monitor = {};
if (proc.pm2_env.status != cst.ERRORED_STATUS &&
proc.pm2_env.status != cst.STOPPING_STATUS)
proc.pm2_env.status = cst.STOPPED_STATUS;
if (proc.pm2_env.pm_id.toString().indexOf('_old_') !== 0) {
try {
fs.unlinkSync(proc.pm2_env.pm_pid_path);
} catch (e) {
debug('Error when unlinking pid file', e);
}
}
/**
* Avoid infinite reloop if an error is present
*/
// If the process has been created less than 15seconds ago
// And if the process has an uptime less than a second
var min_uptime = typeof(proc.pm2_env.min_uptime) !== 'undefined' ? proc.pm2_env.min_uptime : 1000;
var max_restarts = typeof(proc.pm2_env.max_restarts) !== 'undefined' ? proc.pm2_env.max_restarts : 16;
if ((Date.now() - proc.pm2_env.created_at) < (min_uptime * max_restarts)) {
if ((Date.now() - proc.pm2_env.pm_uptime) < min_uptime) {
// Increment unstable restart
proc.pm2_env.unstable_restarts += 1;
}
}
if (proc.pm2_env.unstable_restarts >= max_restarts) {
// Too many unstable restart in less than 15 seconds
// Set the process as 'ERRORED'
// And stop restarting it
proc.pm2_env.status = cst.ERRORED_STATUS;
proc.process.pid = 0;
console.log('Script %s had too many unstable restarts (%d). Stopped. %j',
proc.pm2_env.pm_exec_path,
proc.pm2_env.unstable_restarts,
proc.pm2_env.status);
God.notify('restart overlimit', proc);
proc.pm2_env.unstable_restarts = 0;
proc.pm2_env.created_at = null;
overlimit = true;
}
if (typeof(exit_code) !== 'undefined') proc.pm2_env.exit_code = exit_code;
God.notify('exit', proc);
if (God.pm2_being_killed) {
//console.log('[HandleExit] PM2 is being killed, stopping restart procedure...');
return false;
}
var restart_delay = 0;
if (proc.pm2_env.restart_delay !== undefined &&
!isNaN(parseInt(proc.pm2_env.restart_delay))) {
proc.pm2_env.status = cst.WAITING_RESTART;
restart_delay = parseInt(proc.pm2_env.restart_delay);
}
if (proc.pm2_env.exp_backoff_restart_delay !== undefined &&
!isNaN(parseInt(proc.pm2_env.exp_backoff_restart_delay))) {
proc.pm2_env.status = cst.WAITING_RESTART;
if (!proc.pm2_env.prev_restart_delay) {
proc.pm2_env.prev_restart_delay = proc.pm2_env.exp_backoff_restart_delay
restart_delay = proc.pm2_env.exp_backoff_restart_delay
}
else {
proc.pm2_env.prev_restart_delay = Math.floor(Math.min(15000, proc.pm2_env.prev_restart_delay * 1.5))
restart_delay = proc.pm2_env.prev_restart_delay
}
console.log(`App [${clu.pm2_env.name}:${clu.pm2_env.pm_id}] will restart in ${restart_delay}ms`)
}
if (!stopping && !overlimit) {
//make this property unenumerable
Object.defineProperty(proc.pm2_env, 'restart_task', {configurable: true, writable: true});
proc.pm2_env.restart_task = setTimeout(function() {
proc.pm2_env.restart_time += 1;
God.executeApp(proc.pm2_env);
}, restart_delay);
}
return false;
};
/**
* @method finalizeProcedure
* @param proc {Object}
* @return
*/
God.finalizeProcedure = function finalizeProcedure(proc) {
var last_path = '';
var current_path = proc.pm2_env.cwd || path.dirname(proc.pm2_env.pm_exec_path);
var proc_id = proc.pm2_env.pm_id;
proc.pm2_env.version = Utility.findPackageVersion(proc.pm2_env.pm_exec_path || proc.pm2_env.cwd);
if (proc.pm2_env.vizion_running === true) {
debug('Vizion is already running for proc id: %d, skipping this round', proc_id);
return God.notify('online', proc);
}
proc.pm2_env.vizion_running = true;
vizion.analyze({folder : current_path}, function recur_path(err, meta){
var proc = God.clusters_db[proc_id];
if (err)
debug(err.stack || err);
if (!proc ||
!proc.pm2_env ||
proc.pm2_env.status == cst.STOPPED_STATUS ||
proc.pm2_env.status == cst.STOPPING_STATUS ||
proc.pm2_env.status == cst.ERRORED_STATUS) {
return console.error('Cancelling versioning data parsing');
}
proc.pm2_env.vizion_running = false;
if (!err) {
proc.pm2_env.versioning = meta;
proc.pm2_env.versioning.repo_path = current_path;
God.notify('online', proc);
}
else if (err && current_path === last_path) {
proc.pm2_env.versioning = null;
God.notify('online', proc);
}
else {
last_path = current_path;
current_path = path.dirname(current_path);
proc.pm2_env.vizion_running = true;
vizion.analyze({folder : current_path}, recur_path);
}
return false;
});
};
/**
* Inject variables into processes
* @param {Object} env environnement to be passed to the process
* @param {Function} cb invoked with <err, env>
*/
God.injectVariables = function injectVariables (env, cb) {
// allow to override the key of NODE_APP_INSTANCE if wanted
var instanceKey = process.env.PM2_PROCESS_INSTANCE_VAR || env.instance_var;
// we need to find the last NODE_APP_INSTANCE used
var instances = Object.keys(God.clusters_db)
.map(function (procId) {
return God.clusters_db[procId];
}).filter(function (proc) {
return proc.pm2_env.name === env.name &&
typeof proc.pm2_env[instanceKey] !== 'undefined';
}).map(function (proc) {
return proc.pm2_env[instanceKey];
}).sort(function (a, b) {
return b - a;
});
// default to last one + 1
var instanceNumber = typeof instances[0] === 'undefined' ? 0 : instances[0] + 1;
// but try to find a one available
for (var i = 0; i < instances.length; i++) {
if (instances.indexOf(i) === -1) {
instanceNumber = i;
break;
}
}
env[instanceKey] = instanceNumber;
// if using increment_var, we need to increment it
if (env.increment_var) {
var lastIncrement = Object.keys(God.clusters_db)
.map(function (procId) {
return God.clusters_db[procId];
}).filter(function (proc) {
return proc.pm2_env.name === env.name &&
typeof proc.pm2_env[env.increment_var] !== 'undefined';
}).map(function (proc) {
return Number(proc.pm2_env[env.increment_var]);
}).sort(function (a, b) {
return b - a;
})[0];
// inject a incremental variable
var defaut = Number(env.env[env.increment_var]) || 0;
env[env.increment_var] = typeof lastIncrement === 'undefined' ? defaut : lastIncrement + 1;
env.env[env.increment_var] = env[env.increment_var];
}
return cb(null, env);
};
God.init()

View File

@@ -0,0 +1,909 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
'use strict';
/**
* @file ActionMethod like restart, stop, monitor... are here
* @author Alexandre Strzelewicz <as@unitech.io>
* @project PM2
*/
var fs = require('fs');
var path = require('path');
var eachLimit = require('async/eachLimit');
var os = require('os');
var p = path;
var cst = require('../../constants.js');
var pkg = require('../../package.json');
var pidusage = require('pidusage');
var util = require('util');
var debug = require('debug')('pm2:ActionMethod');
var Utility = require('../Utility');
/**
* Description
* @method exports
* @param {} God
* @return
*/
module.exports = function(God) {
/**
* Description
* @method getMonitorData
* @param {} env
* @param {} cb
* @return
*/
God.getMonitorData = function getMonitorData(env, cb) {
var processes = God.getFormatedProcesses();
var pids = processes.filter(filterBadProcess)
.map(function(pro, i) {
var pid = getProcessId(pro)
return pid;
})
// No pids, return empty statistics
if (pids.length === 0) {
return cb(null, processes.map(function(pro) {
pro['monit'] = {
memory : 0,
cpu : 0
};
return pro
}))
}
pidusage(pids, function retPidUsage(err, statistics) {
// Just log, we'll set empty statistics
if (err) {
console.error('Error caught while calling pidusage');
console.error(err);
return cb(null, processes.map(function(pro) {
pro['monit'] = {
memory : 0,
cpu : 0
};
return pro
}))
}
if (!statistics) {
console.error('Statistics is not defined!')
return cb(null, processes.map(function(pro) {
pro['monit'] = {
memory : 0,
cpu : 0
};
return pro
}))
}
processes = processes.map(function(pro) {
if (filterBadProcess(pro) === false) {
pro['monit'] = {
memory : 0,
cpu : 0
};
return pro;
}
var pid = getProcessId(pro);
var stat = statistics[pid];
if (!stat) {
pro['monit'] = {
memory : 0,
cpu : 0
};
return pro;
}
pro['monit'] = {
memory: stat.memory,
cpu: Math.round(stat.cpu * 10) / 10
};
return pro;
});
cb(null, processes);
});
};
/**
* Description
* @method dumpProcessList
* @param {} cb
* @return
*/
God.dumpProcessList = function(cb) {
var process_list = [];
var apps = Utility.clone(God.getFormatedProcesses());
var that = this;
// Don't override the actual dump file if process list is empty
// unless user explicitely did `pm2 dump`.
// This often happens when PM2 crashed, we don't want to override
// the dump file with an empty list of process.
if (!apps[0]) {
debug('[PM2] Did not override dump file because list of processes is empty');
return cb(null, {success:true, process_list: process_list});
}
function fin(err) {
// try to fix issues with empty dump file
// like #3485
if (process_list.length === 0) {
// fix : if no dump file, no process, only module and after pm2 update
if (!fs.existsSync(cst.DUMP_FILE_PATH) && typeof that.clearDump === 'function') {
that.clearDump(function(){});
}
// if no process in list don't modify dump file
// process list should not be empty
return cb(null, {success:true, process_list: process_list});
}
// Back up dump file
try {
if (fs.existsSync(cst.DUMP_FILE_PATH)) {
fs.writeFileSync(cst.DUMP_BACKUP_FILE_PATH, fs.readFileSync(cst.DUMP_FILE_PATH));
}
} catch (e) {
console.error(e.stack || e);
}
// Overwrite dump file, delete if broken
try {
fs.writeFileSync(cst.DUMP_FILE_PATH, JSON.stringify(process_list));
} catch (e) {
console.error(e.stack || e);
try {
// try to backup file
if (fs.existsSync(cst.DUMP_BACKUP_FILE_PATH)) {
fs.writeFileSync(cst.DUMP_FILE_PATH, fs.readFileSync(cst.DUMP_BACKUP_FILE_PATH));
}
} catch (e) {
// don't keep broken file
fs.unlinkSync(cst.DUMP_FILE_PATH);
console.error(e.stack || e);
}
}
return cb(null, {success:true, process_list: process_list});
}
function saveProc(apps) {
if (!apps[0])
return fin(null);
delete apps[0].pm2_env.instances;
delete apps[0].pm2_env.pm_id;
// Do not dump modules
if (!apps[0].pm2_env.pmx_module)
process_list.push(apps[0].pm2_env);
apps.shift();
return saveProc(apps);
}
saveProc(apps);
};
/**
* Description
* @method ping
* @param {} env
* @param {} cb
* @return CallExpression
*/
God.ping = function(env, cb) {
return cb(null, {msg : 'pong'});
};
/**
* Description
* @method notifyKillPM2
*/
God.notifyKillPM2 = function() {
God.pm2_being_killed = true;
};
/**
* Duplicate a process
* @method duplicateProcessId
* @param {} id
* @param {} cb
* @return CallExpression
*/
God.duplicateProcessId = function(id, cb) {
if (!(id in God.clusters_db))
return cb(God.logAndGenerateError(id + ' id unknown'), {});
if (!God.clusters_db[id] || !God.clusters_db[id].pm2_env)
return cb(God.logAndGenerateError('Error when getting proc || proc.pm2_env'), {});
var proc = Utility.clone(God.clusters_db[id].pm2_env);
delete proc.created_at;
delete proc.pm_id;
delete proc.unique_id;
// generate a new unique id for new process
proc.unique_id = Utility.generateUUID()
God.injectVariables(proc, function inject (_err, proc) {
return God.executeApp(Utility.clone(proc), function (err, clu) {
if (err) return cb(err);
God.notify('start', clu, true);
return cb(err, Utility.clone(clu));
});
});
};
/**
* Start a stopped process by ID
* @method startProcessId
* @param {} id
* @param {} cb
* @return CallExpression
*/
God.startProcessId = function(id, cb) {
if (!(id in God.clusters_db))
return cb(God.logAndGenerateError(id + ' id unknown'), {});
var proc = God.clusters_db[id];
if (proc.pm2_env.status == cst.ONLINE_STATUS)
return cb(God.logAndGenerateError('process already online'), {});
if (proc.pm2_env.status == cst.LAUNCHING_STATUS)
return cb(God.logAndGenerateError('process already started'), {});
if (proc.process && proc.process.pid)
return cb(God.logAndGenerateError('Process with pid ' + proc.process.pid + ' already exists'), {});
return God.executeApp(God.clusters_db[id].pm2_env, function(err, proc) {
return cb(err, Utility.clone(proc));
});
};
/**
* Stop a process and set it on state 'stopped'
* @method stopProcessId
* @param {} id
* @param {} cb
* @return Literal
*/
God.stopProcessId = function(id, cb) {
if (typeof id == 'object' && 'id' in id)
id = id.id;
if (!(id in God.clusters_db))
return cb(God.logAndGenerateError(id + ' : id unknown'), {});
var proc = God.clusters_db[id];
//clear time-out restart task
clearTimeout(proc.pm2_env.restart_task);
if (proc.pm2_env.status == cst.STOPPED_STATUS) {
proc.process.pid = 0;
return cb(null, God.getFormatedProcess(id));
}
// state == 'none' means that the process is not online yet
if (proc.state && proc.state === 'none')
return setTimeout(function() { God.stopProcessId(id, cb); }, 250);
console.log('Stopping app:%s id:%s', proc.pm2_env.name, proc.pm2_env.pm_id);
proc.pm2_env.status = cst.STOPPING_STATUS;
if (!proc.process.pid) {
console.error('app=%s id=%d does not have a pid', proc.pm2_env.name, proc.pm2_env.pm_id);
proc.pm2_env.status = cst.STOPPED_STATUS;
return cb(null, { error : true, message : 'could not kill process w/o pid'});
}
God.killProcess(proc.process.pid, proc.pm2_env, function(err) {
proc.pm2_env.status = cst.STOPPED_STATUS;
God.notify('exit', proc);
if (err && err.type && err.type === 'timeout') {
console.error('app=%s id=%d pid=%s could not be stopped',
proc.pm2_env.name,
proc.pm2_env.pm_id,
proc.process.pid);
proc.pm2_env.status = cst.ERRORED_STATUS;
return cb(null, God.getFormatedProcess(id));
}
if (proc.pm2_env.pm_id.toString().indexOf('_old_') !== 0) {
try {
fs.unlinkSync(proc.pm2_env.pm_pid_path);
} catch (e) {}
}
if (proc.pm2_env.axm_actions) proc.pm2_env.axm_actions = [];
if (proc.pm2_env.axm_monitor) proc.pm2_env.axm_monitor = {};
proc.process.pid = 0;
return cb(null, God.getFormatedProcess(id));
});
};
God.resetMetaProcessId = function(id, cb) {
if (!(id in God.clusters_db))
return cb(God.logAndGenerateError(id + ' id unknown'), {});
if (!God.clusters_db[id] || !God.clusters_db[id].pm2_env)
return cb(God.logAndGenerateError('Error when getting proc || proc.pm2_env'), {});
God.clusters_db[id].pm2_env.created_at = Utility.getDate();
God.clusters_db[id].pm2_env.unstable_restarts = 0;
God.clusters_db[id].pm2_env.restart_time = 0;
return cb(null, God.getFormatedProcesses());
};
/**
* Delete a process by id
* It will stop it and remove it from the database
* @method deleteProcessId
* @param {} id
* @param {} cb
* @return Literal
*/
God.deleteProcessId = function(id, cb) {
God.deleteCron(id);
God.stopProcessId(id, function(err, proc) {
if (err) return cb(God.logAndGenerateError(err), {});
// ! transform to slow object
delete God.clusters_db[id];
if (Object.keys(God.clusters_db).length == 0)
God.next_id = 0;
return cb(null, proc);
});
return false;
};
/**
* Restart a process ID
* If the process is online it will not put it on state stopped
* but directly kill it and let God restart it
* @method restartProcessId
* @param {} id
* @param {} cb
* @return Literal
*/
God.restartProcessId = function(opts, cb) {
var id = opts.id;
var env = opts.env || {};
if (typeof(id) === 'undefined')
return cb(God.logAndGenerateError('opts.id not passed to restartProcessId', opts));
if (!(id in God.clusters_db))
return cb(God.logAndGenerateError('God db process id unknown'), {});
var proc = God.clusters_db[id];
God.resetState(proc.pm2_env);
God.deleteCron(id);
/**
* Merge new application configuration on restart
* Same system in reloadProcessId and softReloadProcessId
*/
Utility.extend(proc.pm2_env.env, env);
Utility.extendExtraConfig(proc, opts);
if (God.pm2_being_killed) {
return cb(God.logAndGenerateError('[RestartProcessId] PM2 is being killed, stopping restart procedure...'));
}
if (proc.pm2_env.status === cst.ONLINE_STATUS || proc.pm2_env.status === cst.LAUNCHING_STATUS) {
God.stopProcessId(id, function(err) {
if (God.pm2_being_killed)
return cb(God.logAndGenerateError('[RestartProcessId] PM2 is being killed, stopping restart procedure...'));
proc.pm2_env.restart_time += 1;
return God.startProcessId(id, cb);
});
return false;
}
else {
debug('[restart] process not online, starting it');
return God.startProcessId(id, cb);
}
};
/**
* Restart all process by name
* @method restartProcessName
* @param {} name
* @param {} cb
* @return Literal
*/
God.restartProcessName = function(name, cb) {
var processes = God.findByName(name);
if (processes && processes.length === 0)
return cb(God.logAndGenerateError('Unknown process'), {});
eachLimit(processes, cst.CONCURRENT_ACTIONS, function(proc, next) {
if (God.pm2_being_killed)
return next('[Watch] PM2 is being killed, stopping restart procedure...');
if (proc.pm2_env.status === cst.ONLINE_STATUS)
return God.restartProcessId({id:proc.pm2_env.pm_id}, next);
else if (proc.pm2_env.status !== cst.STOPPING_STATUS
&& proc.pm2_env.status !== cst.LAUNCHING_STATUS)
return God.startProcessId(proc.pm2_env.pm_id, next);
else
return next(util.format('[Watch] Process name %s is being stopped so I won\'t restart it', name));
}, function(err) {
if (err) return cb(God.logAndGenerateError(err));
return cb(null, God.getFormatedProcesses());
});
return false;
};
/**
* Send system signal to process id
* @method sendSignalToProcessId
* @param {} opts
* @param {} cb
* @return CallExpression
*/
God.sendSignalToProcessId = function(opts, cb) {
var id = opts.process_id;
var signal = opts.signal;
if (!(id in God.clusters_db))
return cb(God.logAndGenerateError(id + ' id unknown'), {});
var proc = God.clusters_db[id];
//God.notify('send signal ' + signal, proc, true);
try {
process.kill(God.clusters_db[id].process.pid, signal);
} catch(e) {
return cb(God.logAndGenerateError('Error when sending signal (signal unknown)'), {});
}
return cb(null, God.getFormatedProcesses());
};
/**
* Send system signal to all processes by name
* @method sendSignalToProcessName
* @param {} opts
* @param {} cb
* @return
*/
God.sendSignalToProcessName = function(opts, cb) {
var processes = God.findByName(opts.process_name);
var signal = opts.signal;
if (processes && processes.length === 0)
return cb(God.logAndGenerateError('Unknown process name'), {});
eachLimit(processes, cst.CONCURRENT_ACTIONS, function(proc, next) {
if (proc.pm2_env.status == cst.ONLINE_STATUS || proc.pm2_env.status == cst.LAUNCHING_STATUS) {
try {
process.kill(proc.process.pid, signal);
} catch(e) {
return next(e);
}
}
return setTimeout(next, 200);
}, function(err) {
if (err) return cb(God.logAndGenerateError(err), {});
return cb(null, God.getFormatedProcesses());
});
};
/**
* Stop watching daemon
* @method stopWatch
* @param {} method
* @param {} value
* @param {} fn
* @return
*/
God.stopWatch = function(method, value, fn) {
var env = null;
if (method == 'stopAll' || method == 'deleteAll') {
var processes = God.getFormatedProcesses();
processes.forEach(function(proc) {
God.clusters_db[proc.pm_id].pm2_env.watch = false;
God.watch.disable(proc.pm2_env);
});
} else {
if (method.indexOf('ProcessId') !== -1) {
env = God.clusters_db[value];
} else if (method.indexOf('ProcessName') !== -1) {
env = God.clusters_db[God.findByName(value)];
}
if (env) {
God.watch.disable(env.pm2_env);
env.pm2_env.watch = false;
}
}
return fn(null, {success:true});
};
/**
* Toggle watching daemon
* @method toggleWatch
* @param {String} method
* @param {Object} application environment, should include id
* @param {Function} callback
*/
God.toggleWatch = function(method, value, fn) {
var env = null;
if (method == 'restartProcessId') {
env = God.clusters_db[value.id];
} else if(method == 'restartProcessName') {
env = God.clusters_db[God.findByName(value)];
}
if (env) {
env.pm2_env.watch = !env.pm2_env.watch;
if (env.pm2_env.watch)
God.watch.enable(env.pm2_env);
else
God.watch.disable(env.pm2_env);
}
return fn(null, {success:true});
};
/**
* Start Watch
* @method startWatch
* @param {String} method
* @param {Object} application environment, should include id
* @param {Function} callback
*/
God.startWatch = function(method, value, fn) {
var env = null;
if (method == 'restartProcessId') {
env = God.clusters_db[value.id];
} else if(method == 'restartProcessName') {
env = God.clusters_db[God.findByName(value)];
}
if (env) {
if (env.pm2_env.watch)
return fn(null, {success:true, notrestarted:true});
God.watch.enable(env.pm2_env);
//env.pm2_env.env.watch = true;
env.pm2_env.watch = true;
}
return fn(null, {success:true});
};
/**
* Description
* @method reloadLogs
* @param {} opts
* @param {} cb
* @return CallExpression
*/
God.reloadLogs = function(opts, cb) {
console.log('Reloading logs...');
var processIds = Object.keys(God.clusters_db);
processIds.forEach(function (id) {
var cluster = God.clusters_db[id];
console.log('Reloading logs for process id %d', id);
if (cluster && cluster.pm2_env) {
// Cluster mode
if (cluster.send && cluster.pm2_env.exec_mode == 'cluster_mode') {
try {
cluster.send({
type:'log:reload'
});
} catch(e) {
console.error(e.message || e);
}
}
// Fork mode
else if (cluster._reloadLogs) {
cluster._reloadLogs(function(err) {
if (err) God.logAndGenerateError(err);
});
}
}
});
return cb(null, {});
};
/**
* Send Line To Stdin
* @method sendLineToStdin
* @param Object packet
* @param String pm_id Process ID
* @param String line Line to send to process stdin
*/
God.sendLineToStdin = function(packet, cb) {
if (typeof(packet.pm_id) == 'undefined' || !packet.line)
return cb(God.logAndGenerateError('pm_id or line field missing'), {});
var pm_id = packet.pm_id;
var line = packet.line;
var proc = God.clusters_db[pm_id];
if (!proc)
return cb(God.logAndGenerateError('Process with ID <' + pm_id + '> unknown.'), {});
if (proc.pm2_env.exec_mode == 'cluster_mode')
return cb(God.logAndGenerateError('Cannot send line to processes in cluster mode'), {});
if (proc.pm2_env.status != cst.ONLINE_STATUS && proc.pm2_env.status != cst.LAUNCHING_STATUS)
return cb(God.logAndGenerateError('Process with ID <' + pm_id + '> offline.'), {});
try {
proc.stdin.write(line, function() {
return cb(null, {
pm_id : pm_id,
line : line
});
});
} catch(e) {
return cb(God.logAndGenerateError(e), {});
}
}
/**
* @param {object} packet
* @param {function} cb
*/
God.sendDataToProcessId = function(packet, cb) {
if (typeof(packet.id) == 'undefined' ||
typeof(packet.data) == 'undefined' ||
!packet.topic)
return cb(God.logAndGenerateError('ID, DATA or TOPIC field is missing'), {});
var pm_id = packet.id;
var data = packet.data;
var proc = God.clusters_db[pm_id];
if (!proc)
return cb(God.logAndGenerateError('Process with ID <' + pm_id + '> unknown.'), {});
if (proc.pm2_env.status != cst.ONLINE_STATUS && proc.pm2_env.status != cst.LAUNCHING_STATUS)
return cb(God.logAndGenerateError('Process with ID <' + pm_id + '> offline.'), {});
try {
proc.send(packet);
}
catch(e) {
return cb(God.logAndGenerateError(e), {});
}
return cb(null, {
success: true,
data : packet
});
};
/**
* Send Message to Process by id or name
* @method msgProcess
* @param {} cmd
* @param {} cb
* @return Literal
*/
God.msgProcess = function(cmd, cb) {
if ('id' in cmd) {
var id = cmd.id;
if (!(id in God.clusters_db))
return cb(God.logAndGenerateError(id + ' id unknown'), {});
var proc = God.clusters_db[id];
var action_exist = false;
proc.pm2_env.axm_actions.forEach(function(action) {
if (action.action_name == cmd.msg) {
action_exist = true;
// Reset output buffer
action.output = [];
}
});
if (action_exist == false) {
return cb(God.logAndGenerateError('Action doesn\'t exist ' + cmd.msg + ' for ' + proc.pm2_env.name), {});
}
if (proc.pm2_env.status == cst.ONLINE_STATUS || proc.pm2_env.status == cst.LAUNCHING_STATUS) {
/*
* Send message
*/
if (cmd.opts == null && !cmd.uuid)
proc.send(cmd.msg);
else
proc.send(cmd);
return cb(null, { process_count : 1, success : true });
}
else
return cb(God.logAndGenerateError(id + ' : id offline'), {});
}
else if ('name' in cmd) {
/*
* As names are not unique in case of cluster, this
* will send msg to all process matching 'name'
*/
var name = cmd.name;
var arr = Object.keys(God.clusters_db);
var sent = 0;
(function ex(arr) {
if (arr[0] == null || !arr) {
return cb(null, {
process_count : sent,
success : true
});
}
var id = arr[0];
if (!God.clusters_db[id] || !God.clusters_db[id].pm2_env) {
arr.shift();
return ex(arr);
}
var proc_env = God.clusters_db[id].pm2_env;
const isActionAvailable = proc_env.axm_actions.find(action => action.action_name === cmd.msg) !== undefined
// if action doesn't exist for this app
// try with the next one
if (isActionAvailable === false) {
arr.shift();
return ex(arr);
}
if ((p.basename(proc_env.pm_exec_path) == name ||
proc_env.name == name ||
proc_env.namespace == name ||
name == 'all') &&
(proc_env.status == cst.ONLINE_STATUS ||
proc_env.status == cst.LAUNCHING_STATUS)) {
proc_env.axm_actions.forEach(function(action) {
if (action.action_name == cmd.msg) {
action_exist = true;
}
});
if (action_exist == false || proc_env.axm_actions.length == 0) {
arr.shift();
return ex(arr);
}
if (cmd.opts == null)
God.clusters_db[id].send(cmd.msg);
else
God.clusters_db[id].send(cmd);
sent++;
arr.shift();
return ex(arr);
}
else {
arr.shift();
return ex(arr);
}
return false;
})(arr);
}
else return cb(God.logAndGenerateError('method requires name or id field'), {});
return false;
};
/**
* Description
* @method getVersion
* @param {} env
* @param {} cb
* @return CallExpression
*/
God.getVersion = function(env, cb) {
process.nextTick(function() {
return cb(null, pkg.version);
});
};
God.monitor = function Monitor(pm_id, cb) {
if (!God.clusters_db[pm_id] || !God.clusters_db[pm_id].pm2_env)
return cb(new Error('Unknown pm_id'));
God.clusters_db[pm_id].pm2_env._km_monitored = true;
return cb(null, { success : true, pm_id : pm_id });
}
God.unmonitor = function Monitor(pm_id, cb) {
if (!God.clusters_db[pm_id] || !God.clusters_db[pm_id].pm2_env)
return cb(new Error('Unknown pm_id'));
God.clusters_db[pm_id].pm2_env._km_monitored = false;
return cb(null, { success : true, pm_id : pm_id });
}
God.getReport = function(arg, cb) {
var report = {
pm2_version : pkg.version,
node_version : 'N/A',
node_path : process.env['_'] || 'not found',
argv0 : process.argv0,
argv : process.argv,
user : process.env.USER,
uid : (cst.IS_WINDOWS === false && process.geteuid) ? process.geteuid() : 'N/A',
gid : (cst.IS_WINDOWS === false && process.getegid) ? process.getegid() : 'N/A',
env : process.env,
managed_apps : Object.keys(God.clusters_db).length,
started_at : God.started_at
};
if (process.versions && process.versions.node) {
report.node_version = process.versions.node;
}
process.nextTick(function() {
return cb(null, report);
});
};
};
function filterBadProcess(pro) {
if (pro.pm2_env.status !== cst.ONLINE_STATUS) {
return false;
}
if (pro.pm2_env.axm_options && pro.pm2_env.axm_options.pid) {
if (isNaN(pro.pm2_env.axm_options.pid)) {
return false;
}
}
return true;
}
function getProcessId(pro) {
var pid = pro.pid
if (pro.pm2_env.axm_options && pro.pm2_env.axm_options.pid) {
pid = pro.pm2_env.axm_options.pid;
}
return pid
}

97
api.hyungi.net/node_modules/pm2/lib/God/ClusterMode.js generated vendored Normal file
View File

@@ -0,0 +1,97 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
'use strict';
/**
* @file Cluster execution functions related
* @author Alexandre Strzelewicz <as@unitech.io>
* @project PM2
*/
var cluster = require('cluster');
var Utility = require('../Utility.js');
var pkg = require('../../package.json');
/**
* Description
* @method exports
* @param {} God
* @return
*/
module.exports = function ClusterMode(God) {
/**
* For Node apps - Cluster mode
* It will wrap the code and enable load-balancing mode
* @method nodeApp
* @param {} env_copy
* @param {} cb
* @return Literal
*/
God.nodeApp = function nodeApp(env_copy, cb){
var clu = null;
console.log(`App [${env_copy.name}:${env_copy.pm_id}] starting in -cluster mode-`)
if (env_copy.node_args && Array.isArray(env_copy.node_args)) {
cluster.settings.execArgv = env_copy.node_args;
}
env_copy._pm2_version = pkg.version;
try {
// node.js cluster clients can not receive deep-level objects or arrays in the forked process, e.g.:
// { "args": ["foo", "bar"], "env": { "foo1": "bar1" }} will be parsed to
// { "args": "foo, bar", "env": "[object Object]"}
// So we passing a stringified JSON here.
clu = cluster.fork({pm2_env: JSON.stringify(env_copy), windowsHide: true});
} catch(e) {
God.logAndGenerateError(e);
return cb(e);
}
clu.pm2_env = env_copy;
/**
* Broadcast message to God
*/
clu.on('message', function cluMessage(msg) {
/*********************************
* If you edit this function
* Do the same in ForkMode.js !
*********************************/
if (msg.data && msg.type) {
return God.bus.emit(msg.type ? msg.type : 'process:msg', {
at : Utility.getDate(),
data : msg.data,
process : {
pm_id : clu.pm2_env.pm_id,
name : clu.pm2_env.name,
rev : (clu.pm2_env.versioning && clu.pm2_env.versioning.revision) ? clu.pm2_env.versioning.revision : null,
namespace : clu.pm2_env.namespace
}
});
}
else {
if (typeof msg == 'object' && 'node_version' in msg) {
clu.pm2_env.node_version = msg.node_version;
return false;
}
return God.bus.emit('process:msg', {
at : Utility.getDate(),
raw : msg,
process : {
pm_id : clu.pm2_env.pm_id,
name : clu.pm2_env.name,
namespace : clu.pm2_env.namespace
}
});
}
});
return cb(null, clu);
};
};

293
api.hyungi.net/node_modules/pm2/lib/God/ForkMode.js generated vendored Normal file
View File

@@ -0,0 +1,293 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
'use strict';
/**
* @file Fork execution related functions
* @author Alexandre Strzelewicz <as@unitech.io>
* @project PM2
*/
var log = require('debug')('pm2:fork_mode');
var fs = require('fs');
var Utility = require('../Utility.js');
var path = require('path');
var dayjs = require('dayjs');
var semver = require('semver')
/**
* Description
* @method exports
* @param {} God
* @return
*/
module.exports = function ForkMode(God) {
/**
* For all apps - FORK MODE
* fork the app
* @method forkMode
* @param {} pm2_env
* @param {} cb
* @return
*/
God.forkMode = function forkMode(pm2_env, cb) {
var command = '';
var args = [];
console.log(`App [${pm2_env.name}:${pm2_env.pm_id}] starting in -fork mode-`)
var spawn = require('child_process').spawn;
var interpreter = pm2_env.exec_interpreter || 'node';
var pidFile = pm2_env.pm_pid_path;
if (interpreter !== 'none') {
command = interpreter;
if (pm2_env.node_args && Array.isArray(pm2_env.node_args)) {
args = args.concat(pm2_env.node_args);
}
// Deprecated - to remove at some point
if (process.env.PM2_NODE_OPTIONS) {
args = args.concat(process.env.PM2_NODE_OPTIONS.split(' '));
}
if (interpreter === 'node' || RegExp('node$').test(interpreter)) {
args.push(path.resolve(path.dirname(module.filename), '..', 'ProcessContainerFork.js'));
}
else
args.push(pm2_env.pm_exec_path);
}
else {
command = pm2_env.pm_exec_path;
args = [ ];
}
if (pm2_env.args) {
args = args.concat(pm2_env.args);
}
// piping stream o file
var stds = {
out: pm2_env.pm_out_log_path,
err: pm2_env.pm_err_log_path
};
// entire log std if necessary.
if ('pm_log_path' in pm2_env){
stds.std = pm2_env.pm_log_path;
}
log("stds: %j", stds);
Utility.startLogging(stds, function(err, result) {
if (err) {
God.logAndGenerateError(err);
return cb(err);
};
try {
var options = {
env : pm2_env,
detached : true,
cwd : pm2_env.pm_cwd || process.cwd(),
stdio : ['pipe', 'pipe', 'pipe', 'ipc'] //Same as fork() in node core
}
if (typeof(pm2_env.windowsHide) === "boolean") {
options.windowsHide = pm2_env.windowsHide;
} else {
options.windowsHide = true;
}
if (pm2_env.uid) {
options.uid = pm2_env.uid
}
if (pm2_env.gid) {
options.gid = pm2_env.gid
}
var cspr = spawn(command, args, options);
} catch(e) {
God.logAndGenerateError(e);
return cb(e);
}
if (!cspr || !cspr.stderr || !cspr.stdout) {
var fatalError = new Error('Process could not be forked properly, check your system health')
God.logAndGenerateError(fatalError);
return cb(fatalError);
}
cspr.process = {};
cspr.process.pid = cspr.pid;
cspr.pm2_env = pm2_env;
function transformLogToJson(pm2_env, type, data) {
return JSON.stringify({
message : data.toString(),
timestamp : pm2_env.log_date_format ? dayjs().format(pm2_env.log_date_format) : new Date().toISOString(),
type : type,
process_id : cspr.pm2_env.pm_id,
app_name : cspr.pm2_env.name
}) + '\n'
}
function prefixLogWithDate(pm2_env, data) {
var log_data = []
log_data = data.toString().split('\n')
if (log_data.length > 1)
log_data.pop()
log_data = log_data.map(line => `${dayjs().format(pm2_env.log_date_format)}: ${line}\n`)
log_data = log_data.join('')
return log_data
}
cspr.stderr.on('data', function forkErrData(data) {
var log_data = null;
// via --out /dev/null --err /dev/null
if (pm2_env.disable_logs === true) return false;
if (pm2_env.log_type && pm2_env.log_type === 'json')
log_data = transformLogToJson(pm2_env, 'err', data)
else if (pm2_env.log_date_format)
log_data = prefixLogWithDate(pm2_env, data)
else
log_data = data.toString();
God.bus.emit('log:err', {
process : {
pm_id : cspr.pm2_env.pm_id,
name : cspr.pm2_env.name,
rev : (cspr.pm2_env.versioning && cspr.pm2_env.versioning.revision) ? cspr.pm2_env.versioning.revision : null,
namespace : cspr.pm2_env.namespace
},
at : Utility.getDate(),
data : log_data
});
if (Utility.checkPathIsNull(pm2_env.pm_err_log_path) &&
(!pm2_env.pm_log_path || Utility.checkPathIsNull(pm2_env.pm_log_path))) {
return false;
}
stds.std && stds.std.write && stds.std.write(log_data);
stds.err && stds.err.write && stds.err.write(log_data);
});
cspr.stdout.on('data', function forkOutData(data) {
var log_data = null;
if (pm2_env.disable_logs === true)
return false;
if (pm2_env.log_type && pm2_env.log_type === 'json')
log_data = transformLogToJson(pm2_env, 'out', data)
else if (pm2_env.log_date_format)
log_data = prefixLogWithDate(pm2_env, data)
else
log_data = data.toString()
God.bus.emit('log:out', {
process : {
pm_id : cspr.pm2_env.pm_id,
name : cspr.pm2_env.name,
rev : (cspr.pm2_env.versioning && cspr.pm2_env.versioning.revision) ? cspr.pm2_env.versioning.revision : null,
namespace : cspr.pm2_env.namespace
},
at : Utility.getDate(),
data : log_data
});
if (Utility.checkPathIsNull(pm2_env.pm_out_log_path) &&
(!pm2_env.pm_log_path || Utility.checkPathIsNull(pm2_env.pm_log_path)))
return false;
stds.std && stds.std.write && stds.std.write(log_data);
stds.out && stds.out.write && stds.out.write(log_data);
});
/**
* Broadcast message to God
*/
cspr.on('message', function forkMessage(msg) {
/*********************************
* If you edit this function
* Do the same in ClusterMode.js !
*********************************/
if (msg.data && msg.type) {
process.nextTick(function() {
return God.bus.emit(msg.type ? msg.type : 'process:msg', {
at : Utility.getDate(),
data : msg.data,
process : {
pm_id : cspr.pm2_env.pm_id,
name : cspr.pm2_env.name,
versioning : cspr.pm2_env.versioning,
namespace : cspr.pm2_env.namespace
}
});
});
}
else {
if (typeof msg == 'object' && 'node_version' in msg) {
cspr.pm2_env.node_version = msg.node_version;
return false;
}
return God.bus.emit('process:msg', {
at : Utility.getDate(),
raw : msg,
process : {
pm_id : cspr.pm2_env.pm_id,
name : cspr.pm2_env.name,
namespace : cspr.pm2_env.namespace
}
});
}
});
try {
var pid = cspr.pid
if (typeof(pid) !== 'undefined')
fs.writeFileSync(pidFile, pid.toString());
} catch (e) {
console.error(e.stack || e);
}
cspr.once('exit', function forkClose(status) {
try {
for(var k in stds){
if (stds[k] && stds[k].destroy) stds[k].destroy();
else if (stds[k] && stds[k].end) stds[k].end();
else if (stds[k] && stds[k].close) stds[k].close();
stds[k] = stds[k]._file;
}
} catch(e) { God.logAndGenerateError(e);}
});
cspr._reloadLogs = function(cb) {
try {
for (var k in stds){
if (stds[k] && stds[k].destroy) stds[k].destroy();
else if (stds[k] && stds[k].end) stds[k].end();
else if (stds[k] && stds[k].close) stds[k].close();
stds[k] = stds[k]._file;
}
} catch(e) { God.logAndGenerateError(e);}
//cspr.removeAllListeners();
Utility.startLogging(stds, cb);
};
cspr.unref();
return cb(null, cspr);
});
};
};

265
api.hyungi.net/node_modules/pm2/lib/God/Methods.js generated vendored Normal file
View File

@@ -0,0 +1,265 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
'use strict';
/**
* @file Utilities for PM2
* @author Alexandre Strzelewicz <as@unitech.io>
* @project PM2
*/
var p = require('path');
var treekill = require('../TreeKill');
var cst = require('../../constants.js');
/**
* Description
* @method exports
* @param {} God
* @return
*/
module.exports = function(God) {
/**
* Description
* @method logAndGenerateError
* @param {} err
* @return NewExpression
*/
God.logAndGenerateError = function(err) {
// Is an Error object
if (err instanceof Error) {
console.trace(err);
return err;
}
// Is a JSON or simple string
console.error(err);
return new Error(err);
};
/**
* Utility functions
* @method getProcesses
* @return MemberExpression
*/
God.getProcesses = function() {
return God.clusters_db;
};
God.getFormatedProcess = function getFormatedProcesses(id) {
if (God.clusters_db[id])
return {
pid : God.clusters_db[id].process.pid,
name : God.clusters_db[id].pm2_env.name,
pm2_env : God.clusters_db[id].pm2_env,
pm_id : God.clusters_db[id].pm2_env.pm_id
};
return {};
};
/**
* Get formated processes
* @method getFormatedProcesses
* @return {Array} formated processes
*/
God.getFormatedProcesses = function getFormatedProcesses() {
var keys = Object.keys(God.clusters_db);
var arr = new Array();
var kl = keys.length;
for (var i = 0; i < kl; i++) {
var key = keys[i];
if (!God.clusters_db[key]) continue;
// Avoid _old type pm_ids
if (isNaN(God.clusters_db[key].pm2_env.pm_id)) continue;
arr.push({
pid : God.clusters_db[key].process.pid,
name : God.clusters_db[key].pm2_env.name,
pm2_env : God.clusters_db[key].pm2_env,
pm_id : God.clusters_db[key].pm2_env.pm_id
})
}
return arr;
};
/**
* Description
* @method findProcessById
* @param {} id
* @return ConditionalExpression
*/
God.findProcessById = function findProcessById(id) {
return God.clusters_db[id] ? God.clusters_db[id] : null;
};
/**
* Description
* @method findByName
* @param {} name
* @return arr
*/
God.findByName = function(name) {
var db = God.clusters_db;
var arr = [];
if (name == 'all') {
for (var key in db) {
// Avoid _old_proc process style
if (typeof(God.clusters_db[key].pm2_env.pm_id) === 'number')
arr.push(db[key]);
}
return arr;
}
for (var key in db) {
if (God.clusters_db[key].pm2_env.name == name ||
God.clusters_db[key].pm2_env.pm_exec_path == p.resolve(name)) {
arr.push(db[key]);
}
}
return arr;
};
/**
* Check if a process is alive in system processes
* Return TRUE if process online
* @method checkProcess
* @param {} pid
* @return
*/
God.checkProcess = function(pid) {
if (!pid) return false;
try {
// Sending 0 signal do not kill the process
process.kill(pid, 0);
return true;
}
catch (err) {
return false;
}
};
/**
* Description
* @method processIsDead
* @param {} pid
* @param {} cb
* @return Literal
*/
God.processIsDead = function(pid, pm2_env, cb, sigkill) {
if (!pid) return cb({type : 'param:missing', msg : 'no pid passed'});
var timeout = null;
var kill_timeout = (pm2_env && pm2_env.kill_timeout) ? pm2_env.kill_timeout : cst.KILL_TIMEOUT;
var mode = pm2_env.exec_mode;
var timer = setInterval(function() {
if (God.checkProcess(pid) === false) {
console.log('pid=%d msg=process killed', pid);
clearTimeout(timeout);
clearInterval(timer);
return cb(null, true);
}
console.log('pid=%d msg=failed to kill - retrying in %dms', pid, pm2_env.kill_retry_time);
return false;
}, pm2_env.kill_retry_time);
timeout = setTimeout(function() {
clearInterval(timer);
if (sigkill) {
console.log('Process with pid %d could not be killed', pid);
return cb({type : 'timeout', msg : 'timeout'});
}
else {
console.log('Process with pid %d still alive after %sms, sending it SIGKILL now...', pid, kill_timeout);
if (pm2_env.treekill !== true) {
try {
process.kill(parseInt(pid), 'SIGKILL');
} catch(e) {
console.error('[SimpleKill][SIGKILL] %s pid can not be killed', pid, e.stack, e.message);
}
return God.processIsDead(pid, pm2_env, cb, true);
}
else {
treekill(parseInt(pid), 'SIGKILL', function(err) {
return God.processIsDead(pid, pm2_env, cb, true);
});
}
}
}, kill_timeout);
return false;
};
/**
* Description
* @method killProcess
* @param int pid
* @param Object pm2_env
* @param function cb
* @return CallExpression
*/
God.killProcess = function(pid, pm2_env, cb) {
if (!pid) return cb({msg : 'no pid passed or null'});
if (typeof(pm2_env.pm_id) === 'number' &&
(cst.KILL_USE_MESSAGE || pm2_env.shutdown_with_message == true)) {
var proc = God.clusters_db[pm2_env.pm_id];
if (proc && proc.send) {
try {
proc.send('shutdown');
} catch (e) {
console.error(`[AppKill] Cannot send "shutdown" message to ${pid}`)
console.error(e.stack, e.message)
}
return God.processIsDead(pid, pm2_env, cb);
}
else {
console.log(`[AppKill] ${pid} pid cannot be notified with send()`)
}
}
if (pm2_env.treekill !== true) {
try {
process.kill(parseInt(pid), cst.KILL_SIGNAL);
} catch(e) {
console.error('[SimpleKill] %s pid can not be killed', pid, e.stack, e.message);
}
return God.processIsDead(pid, pm2_env, cb);
}
else {
treekill(parseInt(pid), cst.KILL_SIGNAL, function(err) {
return God.processIsDead(pid, pm2_env, cb);
});
}
};
/**
* Description
* @method getNewId
* @return UpdateExpression
*/
God.getNewId = function() {
return God.next_id++;
};
/**
* When a process is restarted or reloaded reset fields
* to monitor unstable starts
* @method resetState
* @param {} pm2_env
* @return
*/
God.resetState = function(pm2_env) {
pm2_env.created_at = Date.now();
pm2_env.unstable_restarts = 0;
pm2_env.prev_restart_delay = 0;
};
};

240
api.hyungi.net/node_modules/pm2/lib/God/Reload.js generated vendored Normal file
View File

@@ -0,0 +1,240 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
'use strict';
/**
* @file Reload functions related
* @author Alexandre Strzelewicz <as@unitech.io>
* @project PM2
*/
var cst = require('../../constants.js');
var Utility = require('../Utility.js');
/**
* softReload will wait permission from process to exit
* @method softReload
* @param {} God
* @param {} id
* @param {} cb
* @return Literal
*/
function softReload(God, id, cb) {
var t_key = '_old_' + id;
// Move old worker to tmp id
God.clusters_db[t_key] = God.clusters_db[id];
delete God.clusters_db[id];
var old_worker = God.clusters_db[t_key];
// Deep copy
var new_env = Utility.clone(old_worker.pm2_env);
// Reset created_at and unstable_restarts
God.resetState(new_env);
new_env.restart_time += 1;
old_worker.pm2_env.pm_id = t_key;
old_worker.pm_id = t_key;
God.executeApp(new_env, function(err, new_worker) {
if (err) return cb(err);
var timer = null;
var onListen = function () {
clearTimeout(timer);
softCleanDeleteProcess();
console.log('-softReload- New worker listening');
};
// Bind to know when the new process is up
new_worker.once('listening', onListen);
timer = setTimeout(function() {
new_worker.removeListener('listening', onListen);
softCleanDeleteProcess();
}, new_env.listen_timeout || cst.GRACEFUL_LISTEN_TIMEOUT);
// Remove old worker properly
var softCleanDeleteProcess = function () {
var cleanUp = function () {
clearTimeout(timer);
console.log('-softReload- Old worker disconnected');
return God.deleteProcessId(t_key, cb);
};
old_worker.once('disconnect', cleanUp);
try {
if (old_worker.state != 'dead' && old_worker.state != 'disconnected')
old_worker.send && old_worker.send('shutdown');
else {
clearTimeout(timer);
console.error('Worker %d is already disconnected', old_worker.pm2_env.pm_id);
return God.deleteProcessId(t_key, cb);
}
} catch(e) {
clearTimeout(timer);
console.error('Worker %d is already disconnected', old_worker.pm2_env.pm_id);
return God.deleteProcessId(t_key, cb);
}
timer = setTimeout(function () {
old_worker.removeListener('disconnect', cleanUp);
return God.deleteProcessId(t_key, cb);
}, cst.GRACEFUL_TIMEOUT);
return false;
};
return false;
});
return false;
};
/**
* hardReload will reload without waiting permission from process
* @method hardReload
* @param {} God
* @param {} id
* @param {} cb
* @return Literal
*/
function hardReload(God, id, wait_msg, cb) {
var t_key = '_old_' + id;
// Move old worker to tmp id
God.clusters_db[t_key] = God.clusters_db[id];
delete God.clusters_db[id];
var old_worker = God.clusters_db[t_key];
// Deep copy
var new_env = Utility.clone(old_worker.pm2_env);
new_env.restart_time += 1;
// Reset created_at and unstable_restarts
God.resetState(new_env);
old_worker.pm2_env.pm_id = t_key;
old_worker.pm_id = t_key;
var timer = null;
var readySignalSent = false;
var onListen = function () {
clearTimeout(timer);
readySignalSent = true;
console.log('-reload- New worker listening');
return God.deleteProcessId(t_key, cb);
};
var listener = function (packet) {
if (packet.raw === 'ready' &&
packet.process.name === old_worker.pm2_env.name &&
packet.process.pm_id === id) {
God.bus.removeListener('process:msg', listener);
return onListen();
}
};
if (wait_msg !== 'listening') {
God.bus.on('process:msg', listener);
}
God.executeApp(new_env, function(err, new_worker) {
if (err) return cb(err);
// Bind to know when the new process is up
if (wait_msg === 'listening') {
new_worker.once('listening', onListen);
}
timer = setTimeout(function() {
if (readySignalSent) {
return;
}
if (wait_msg === 'listening')
new_worker.removeListener(wait_msg, onListen);
else
God.bus.removeListener('process:msg', listener);
return God.deleteProcessId(t_key, cb);
}, new_env.listen_timeout || cst.GRACEFUL_LISTEN_TIMEOUT);
return false;
});
return false;
};
/**
* Description
* @method exports
* @param {} God
* @return
*/
module.exports = function(God) {
/**
* Reload
* @method softReloadProcessId
* @param {} id
* @param {} cb
* @return CallExpression
*/
God.softReloadProcessId = function(opts, cb) {
var id = opts.id;
var env = opts.env || {};
if (!(id in God.clusters_db))
return cb(new Error(`pm_id ${id} not available in ${id}`));
if (God.clusters_db[id].pm2_env.status == cst.ONLINE_STATUS &&
God.clusters_db[id].pm2_env.exec_mode == 'cluster_mode' &&
!God.clusters_db[id].pm2_env.wait_ready) {
Utility.extend(God.clusters_db[id].pm2_env.env, opts.env);
Utility.extendExtraConfig(God.clusters_db[id], opts);
return softReload(God, id, cb);
}
else {
console.log('Process %s in a stopped status, starting it', id);
return God.restartProcessId(opts, cb);
}
};
/**
* Reload
* @method reloadProcessId
* @param {} id
* @param {} cb
* @return CallExpression
*/
God.reloadProcessId = function(opts, cb) {
var id = opts.id;
var env = opts.env || {};
if (!(id in God.clusters_db))
return cb(new Error('PM2 ID unknown'));
if (God.clusters_db[id].pm2_env.status == cst.ONLINE_STATUS &&
God.clusters_db[id].pm2_env.exec_mode == 'cluster_mode') {
Utility.extend(God.clusters_db[id].pm2_env.env, opts.env);
Utility.extendExtraConfig(God.clusters_db[id], opts);
var wait_msg = God.clusters_db[id].pm2_env.wait_ready ? 'ready' : 'listening';
return hardReload(God, id, wait_msg, cb);
}
else {
console.log('Process %s in a stopped status, starting it', id);
return God.restartProcessId(opts, cb);
}
};
};

76
api.hyungi.net/node_modules/pm2/lib/HttpInterface.js generated vendored Normal file
View File

@@ -0,0 +1,76 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
var http = require('http');
var os = require('os');
var pm2 = require('../index.js');
var urlT = require('url');
var cst = require('../constants.js');
// Default, attach to default local PM2
pm2.connect(function() {
startWebServer(pm2);
});
function startWebServer(pm2) {
http.createServer(function (req, res) {
// Add CORS headers to allow browsers to fetch data directly
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With');
res.setHeader('Access-Control-Allow-Methods', 'GET');
// We always send json
res.setHeader('Content-Type','application/json');
var path = urlT.parse(req.url).pathname;
if (path == '/') {
// Main monit route
pm2.list(function(err, list) {
if (err) {
return res.send(err);
}
var data = {
system_info: { hostname: os.hostname(),
uptime: os.uptime()
},
monit: { loadavg: os.loadavg(),
total_mem: os.totalmem(),
free_mem: os.freemem(),
cpu: os.cpus(),
interfaces: os.networkInterfaces()
},
processes: list
};
if (cst.WEB_STRIP_ENV_VARS === true) {
for (var i = data.processes.length - 1; i >= 0; i--) {
var proc = data.processes[i];
// Strip important environment variables
if (typeof proc.pm2_env === 'undefined' && typeof proc.pm2_env.env === 'undefined') return;
delete proc.pm2_env.env;
}
}
res.statusCode = 200;
res.write(JSON.stringify(data));
return res.end();
})
}
else {
// 404
res.statusCode = 404;
res.write(JSON.stringify({err : '404'}));
return res.end();
}
}).listen(process.env.PM2_WEB_PORT || cst.WEB_PORT, cst.WEB_IPADDR, function() {
console.log('Web interface listening on %s:%s', cst.WEB_IPADDR, cst.WEB_PORT);
});
}

315
api.hyungi.net/node_modules/pm2/lib/ProcessContainer.js generated vendored Normal file
View File

@@ -0,0 +1,315 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*
* This file wrap target application
* - redirect stdin, stderr to bus + log files
* - rename process
* - pid
*/
var p = require('path');
var cst = require('../constants');
var Utility = require('./Utility.js');
var ProcessUtils = require('./ProcessUtils');
var Url = require('url');
// Load all env-vars from master.
var pm2_env = JSON.parse(process.env.pm2_env);
for(var k in pm2_env) {
process.env[k] = pm2_env[k];
}
// Rename process
process.title = process.env.PROCESS_TITLE || 'node ' + pm2_env.pm_exec_path;
delete process.env.pm2_env;
/**
* Main entrance to wrap the desired code
*/
(function ProcessContainer() {
var fs = require('fs');
ProcessUtils.injectModules()
var stdFile = pm2_env.pm_log_path;
var outFile = pm2_env.pm_out_log_path;
var errFile = pm2_env.pm_err_log_path;
var pidFile = pm2_env.pm_pid_path;
var script = pm2_env.pm_exec_path;
var original_send = process.send;
if (typeof(process.env.source_map_support) != 'undefined' &&
process.env.source_map_support !== 'false') {
require('source-map-support').install();
}
process.send = function() {
if (process.connected)
original_send.apply(this, arguments);
};
//send node version
if (process.versions && process.versions.node) {
process.send({
'node_version': process.versions.node
});
}
if (cst.MODIFY_REQUIRE)
require.main.filename = pm2_env.pm_exec_path;
// Resets global paths for require()
require('module')._initPaths();
try {
var pid = process.pid
if (typeof(pid) !== 'undefined')
fs.writeFileSync(pidFile, process.pid.toString());
} catch (e) {
console.error(e.stack || e);
}
// Add args to process if args specified on start
if (process.env.args != null)
process.argv = process.argv.concat(pm2_env.args);
// stdio, including: out, err and entire (both out and err if necessary).
var stds = {
out: outFile,
err: errFile
};
stdFile && (stds.std = stdFile);
// uid/gid management
if (pm2_env.uid || pm2_env.gid) {
try {
if (process.env.gid)
process.setgid(pm2_env.gid);
if (pm2_env.uid)
process.setuid(pm2_env.uid);
} catch(e) {
setTimeout(function() {
console.error('%s on call %s', e.message, e.syscall);
console.error('%s is not accessible', pm2_env.uid);
return process.exit(1);
}, 100);
}
}
exec(script, stds);
})();
/**
* Description
* @method exec
* @param {} script
* @param {} stds
* @return
*/
function exec(script, stds) {
if (p.extname(script) == '.coffee') {
try {
require('coffee-script/register');
} catch (e) {
console.error('Failed to load CoffeeScript interpreter:', e.message || e);
}
}
if (p.extname(script) == '.ls') {
try {
require('livescript');
} catch (e) {
console.error('Failed to load LiveScript interpreter:', e.message || e);
}
}
if (p.extname(script) == '.ts' || p.extname(script) == '.tsx') {
try {
require('ts-node/register');
} catch (e) {
console.error('Failed to load Typescript interpreter:', e.message || e);
}
}
process.on('message', function (msg) {
if (msg.type === 'log:reload') {
for (var k in stds){
if (typeof stds[k] == 'object' && !isNaN(stds[k].fd)){
if (stds[k].destroy) stds[k].destroy();
else if (stds[k].end) stds[k].end();
else if (stds[k].close) stds[k].close();
stds[k] = stds[k]._file;
}
}
Utility.startLogging(stds, function (err) {
if (err)
return console.error('Failed to reload logs:', err.stack);
console.log('Reloading log...');
});
}
});
var dayjs = null;
if (pm2_env.log_date_format)
dayjs = require('dayjs');
Utility.startLogging(stds, function (err) {
if (err) {
process.send({
type : 'process:exception',
data : {
message: err.message,
syscall: 'ProcessContainer.startLogging'
}
});
throw err;
return;
}
process.stderr.write = (function(write) {
return function(string, encoding, cb) {
var log_data = null;
// Disable logs if specified
if (pm2_env.disable_logs === true) {
return cb ? cb() : false;
}
if (pm2_env.log_type && pm2_env.log_type === 'json') {
log_data = JSON.stringify({
message : string.toString(),
timestamp : pm2_env.log_date_format && dayjs ?
dayjs().format(pm2_env.log_date_format) : new Date().toISOString(),
type : 'err',
process_id : pm2_env.pm_id,
app_name : pm2_env.name
}) + '\n';
}
else if (pm2_env.log_date_format && dayjs)
log_data = `${dayjs().format(pm2_env.log_date_format)}: ${string.toString()}`;
else
log_data = string.toString();
process.send({
type : 'log:err',
topic : 'log:err',
data : log_data
});
if (Utility.checkPathIsNull(pm2_env.pm_err_log_path) &&
(!pm2_env.pm_log_path || Utility.checkPathIsNull(pm2_env.pm_log_path)))
return cb ? cb() : false;
stds.std && stds.std.write && stds.std.write(log_data, encoding);
stds.err && stds.err.write && stds.err.write(log_data, encoding, cb);
};
})(process.stderr.write);
process.stdout.write = (function(write) {
return function(string, encoding, cb) {
var log_data = null;
// Disable logs if specified
if (pm2_env.disable_logs === true) {
return cb ? cb() : false;
}
if (pm2_env.log_type && pm2_env.log_type === 'json') {
log_data = JSON.stringify({
message : string.toString(),
timestamp : pm2_env.log_date_format && dayjs ?
dayjs().format(pm2_env.log_date_format) : new Date().toISOString(),
type : 'out',
process_id : pm2_env.pm_id,
app_name : pm2_env.name
}) + '\n';
}
else if (pm2_env.log_date_format && dayjs)
log_data = `${dayjs().format(pm2_env.log_date_format)}: ${string.toString()}`;
else
log_data = string.toString();
process.send({
type : 'log:out',
data : log_data
});
if (Utility.checkPathIsNull(pm2_env.pm_out_log_path) &&
(!pm2_env.pm_log_path || Utility.checkPathIsNull(pm2_env.pm_log_path)))
return cb ? cb() : null;
stds.std && stds.std.write && stds.std.write(log_data, encoding);
stds.out && stds.out.write && stds.out.write(log_data, encoding, cb);
};
})(process.stdout.write);
function getUncaughtExceptionListener(listener) {
return function uncaughtListener(err) {
var error = err && err.stack ? err.stack : err;
if (listener === 'unhandledRejection') {
error = 'You have triggered an unhandledRejection, you may have forgotten to catch a Promise rejection:\n' + error;
}
logError(['std', 'err'], error);
// Notify master that an uncaughtException has been catched
try {
if (err) {
var errObj = {};
Object.getOwnPropertyNames(err).forEach(function(key) {
errObj[key] = err[key];
});
}
process.send({
type : 'log:err',
topic : 'log:err',
data : '\n' + error + '\n'
});
process.send({
type : 'process:exception',
data : errObj !== undefined ? errObj : {message: 'No error but ' + listener + ' was caught!'}
});
} catch(e) {
logError(['std', 'err'], 'Channel is already closed can\'t broadcast error:\n' + e.stack);
}
if (!process.listeners(listener).filter(function (listener) {
return listener !== uncaughtListener;
}).length) {
if (listener == 'uncaughtException') {
process.emit('disconnect');
process.exit(cst.CODE_UNCAUGHTEXCEPTION);
}
}
}
}
process.on('uncaughtException', getUncaughtExceptionListener('uncaughtException'));
process.on('unhandledRejection', getUncaughtExceptionListener('unhandledRejection'));
// Change dir to fix process.cwd
process.chdir(pm2_env.pm_cwd || process.env.PWD || p.dirname(script));
if (ProcessUtils.isESModule(script) === true)
import(Url.pathToFileURL(process.env.pm_exec_path));
else
require('module')._load(script, null, true);
function logError(types, error){
try {
types.forEach(function(type){
stds[type] && typeof stds[type].write == 'function' && stds[type].write(error + '\n');
});
} catch(e) { }
}
});
}

View File

@@ -0,0 +1,42 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
var url = require('url');
// Inject custom modules
var ProcessUtils = require('./ProcessUtils')
ProcessUtils.injectModules()
if (typeof(process.env.source_map_support) != "undefined" &&
process.env.source_map_support !== "false") {
require('source-map-support').install();
}
// Rename the process
process.title = process.env.PROCESS_TITLE || 'node ' + process.env.pm_exec_path;
if (process.connected &&
process.send &&
process.versions &&
process.versions.node)
process.send({
'node_version': process.versions.node
});
// Require the real application
if (process.env.pm_exec_path) {
if (ProcessUtils.isESModule(process.env.pm_exec_path) === true) {
import(url.pathToFileURL(process.env.pm_exec_path));
}
else
require('module')._load(process.env.pm_exec_path, null, true);
}
else
throw new Error('Could not _load() the script');
// Change some values to make node think that the user's application
// was started directly such as `node app.js`
process.mainModule = process.mainModule || {};
process.mainModule.loaded = false;
require.main = process.mainModule;

55
api.hyungi.net/node_modules/pm2/lib/ProcessUtils.js generated vendored Normal file
View File

@@ -0,0 +1,55 @@
'use strict'
module.exports = {
injectModules: function() {
if (process.env.pmx !== 'false') {
const pmx = require('@pm2/io')
let conf = {}
const hasSpecificConfig = typeof process.env.io === 'string' || process.env.trace === 'true'
// pmx is already init, no need to do it twice
if (hasSpecificConfig === false) return
if (process.env.io) {
const io = JSON.parse(process.env.io)
conf = io.conf ? io.conf : conf
}
pmx.init(Object.assign({
tracing: process.env.trace === 'true' || false
}, conf))
}
},
isESModule(exec_path) {
var fs = require('fs')
var path = require('path')
var semver = require('semver')
var data
var findPackageJson = function(directory) {
var file = path.join(directory, 'package.json')
if (fs.existsSync(file) && fs.statSync(file).isFile()) {
return file;
}
var parent = path.resolve(directory, '..')
if (parent === directory) {
return null;
}
return findPackageJson(parent)
}
if (semver.satisfies(process.version, '< 13.3.0'))
return false
if (path.extname(exec_path) === '.mjs')
return true
try {
data = JSON.parse(fs.readFileSync(findPackageJson(path.dirname(exec_path))))
if (data.type === 'module')
return true
else
return false
} catch(e) {
}
}
}

117
api.hyungi.net/node_modules/pm2/lib/TreeKill.js generated vendored Normal file
View File

@@ -0,0 +1,117 @@
'use strict';
// From https://raw.githubusercontent.com/pkrumins/node-tree-kill/master/index.js
var childProcess = require('child_process');
var spawn = childProcess.spawn;
var exec = childProcess.exec;
module.exports = function (pid, signal, callback) {
var tree = {};
var pidsToProcess = {};
tree[pid] = [];
pidsToProcess[pid] = 1;
switch (process.platform) {
case 'win32':
exec('taskkill /pid ' + pid + ' /T /F', { windowsHide: true }, callback);
break;
case 'darwin':
buildProcessTree(pid, tree, pidsToProcess, function (parentPid) {
return spawn('pgrep', ['-P', parentPid]);
}, function () {
killAll(tree, signal, callback);
});
break;
// case 'sunos':
// buildProcessTreeSunOS(pid, tree, pidsToProcess, function () {
// killAll(tree, signal, callback);
// });
// break;
default: // Linux
buildProcessTree(pid, tree, pidsToProcess, function (parentPid) {
return spawn('ps', ['-o', 'pid', '--no-headers', '--ppid', parentPid]);
}, function () {
killAll(tree, signal, callback);
});
break;
}
};
function killAll (tree, signal, callback) {
var killed = {};
try {
Object.keys(tree).forEach(function (pid) {
tree[pid].forEach(function (pidpid) {
if (!killed[pidpid]) {
killPid(pidpid, signal);
killed[pidpid] = 1;
}
});
if (!killed[pid]) {
killPid(pid, signal);
killed[pid] = 1;
}
});
} catch (err) {
if (callback) {
return callback(err);
} else {
console.error(err);
}
}
if (callback) {
return callback();
}
}
function killPid(pid, signal) {
try {
process.kill(parseInt(pid, 10), signal);
}
catch (err) {
if (err.code !== 'ESRCH')
console.error(err);
}
}
function buildProcessTree (parentPid, tree, pidsToProcess, spawnChildProcessesList, cb) {
var ps = spawnChildProcessesList(parentPid);
var allData = '';
ps.on('error', function(err) {
console.error(err);
});
if (ps.stdout) {
ps.stdout.on('data', function (data) {
data = data.toString('ascii');
allData += data;
});
}
var onClose = function (code) {
delete pidsToProcess[parentPid];
if (code !== 0) {
// no more parent processes
if (Object.keys(pidsToProcess).length == 0) {
cb();
}
return;
}
var pids = allData.match(/\d+/g) || [];
if (pids.length === 0)
return cb();
pids.forEach(function (pid) {
pid = parseInt(pid, 10);
tree[parentPid].push(pid);
tree[pid] = [];
pidsToProcess[pid] = 1;
buildProcessTree(pid, tree, pidsToProcess, spawnChildProcessesList, cb);
});
};
ps.on('close', onClose);
}

278
api.hyungi.net/node_modules/pm2/lib/Utility.js generated vendored Normal file
View File

@@ -0,0 +1,278 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
/**
* Common Utilities ONLY USED IN ->DAEMON<-
*/
var fclone = require('fclone');
var fs = require('fs');
var cst = require('../constants.js');
var waterfall = require('async/waterfall');
var util = require('util');
var url = require('url');
var dayjs = require('dayjs');
var findPackageJson = require('./tools/find-package-json')
var Utility = module.exports = {
findPackageVersion : function(fullpath) {
var version
try {
version = findPackageJson(fullpath).next().value.version
} catch(e) {
version = 'N/A'
}
return version
},
getDate : function() {
return Date.now();
},
extendExtraConfig : function(proc, opts) {
if (opts.env && opts.env.current_conf) {
if (opts.env.current_conf.env &&
typeof(opts.env.current_conf.env) === 'object' &&
Object.keys(opts.env.current_conf.env).length === 0)
delete opts.env.current_conf.env
Utility.extendMix(proc.pm2_env, opts.env.current_conf);
delete opts.env.current_conf;
}
},
formatCLU : function(process) {
if (!process.pm2_env) {
return process;
}
var obj = Utility.clone(process.pm2_env);
delete obj.env;
return obj;
},
extend : function(destination, source){
if (!source || typeof source != 'object') return destination;
Object.keys(source).forEach(function(new_key) {
if (source[new_key] != '[object Object]')
destination[new_key] = source[new_key];
});
return destination;
},
// Same as extend but drop value with 'null'
extendMix : function(destination, source){
if (!source || typeof source != 'object') return destination;
Object.keys(source).forEach(function(new_key) {
if (source[new_key] == 'null')
delete destination[new_key];
else
destination[new_key] = source[new_key]
});
return destination;
},
whichFileExists : function(file_arr) {
var f = null;
file_arr.some(function(file) {
try {
fs.statSync(file);
} catch(e) {
return false;
}
f = file;
return true;
});
return f;
},
clone : function(obj) {
if (obj === null || obj === undefined) return {};
return fclone(obj);
},
overrideConsole : function(bus) {
if (cst.PM2_LOG_DATE_FORMAT && typeof cst.PM2_LOG_DATE_FORMAT == 'string') {
// Generate timestamp prefix
function timestamp(){
return `${dayjs(Date.now()).format(cst.PM2_LOG_DATE_FORMAT)}:`;
}
var hacks = ['info', 'log', 'error', 'warn'], consoled = {};
// store console functions.
hacks.forEach(function(method){
consoled[method] = console[method];
});
hacks.forEach(function(k){
console[k] = function(){
if (bus) {
bus.emit('log:PM2', {
process : {
pm_id : 'PM2',
name : 'PM2',
rev : null
},
at : Utility.getDate(),
data : util.format.apply(this, arguments) + '\n'
});
}
// do not destroy variable insertion
arguments[0] && (arguments[0] = timestamp() + ' PM2 ' + k + ': ' + arguments[0]);
consoled[k].apply(console, arguments);
};
});
}
},
startLogging : function(stds, callback) {
/**
* Start log outgoing messages
* @method startLogging
* @param {} callback
* @return
*/
// Make sure directories of `logs` and `pids` exist.
// try {
// ['logs', 'pids'].forEach(function(n){
// console.log(n);
// (function(_path){
// !fs.existsSync(_path) && fs.mkdirSync(_path, '0755');
// })(path.resolve(cst.PM2_ROOT_PATH, n));
// });
// } catch(err) {
// return callback(new Error('can not create directories (logs/pids):' + err.message));
// }
// waterfall.
var flows = [];
// types of stdio, should be sorted as `std(entire log)`, `out`, `err`.
var types = Object.keys(stds).sort(function(x, y){
return -x.charCodeAt(0) + y.charCodeAt(0);
});
// Create write streams.
(function createWS(io){
if(io.length != 1){
return false;
}
io = io[0];
// If `std` is a Stream type, try next `std`.
// compatible with `pm2 reloadLogs`
if(typeof stds[io] == 'object' && !isNaN(stds[io].fd)){
return createWS(types.splice(0, 1));
}
flows.push(function(next){
var file = stds[io];
// if file contains ERR or /dev/null, dont try to create stream since he dont want logs
if (!file || file.indexOf('NULL') > -1 || file.indexOf('/dev/null') > -1)
return next();
stds[io] = fs.createWriteStream(file, {flags: 'a'})
.once('error', next)
.on('open', function(){
stds[io].removeListener('error', next);
stds[io].on('error', function(err) {
console.error(err);
});
next();
});
stds[io]._file = file;
});
return createWS(types.splice(0, 1));
})(types.splice(0, 1));
waterfall(flows, callback);
},
/**
* Function parse the module name and returns it as canonic:
* - Makes the name based on installation filename.
* - Removes the Github author, module version and git branch from original name.
*
* @param {string} module_name
* @returns {string} Canonic module name (without trimed parts).
* @example Always returns 'pm2-slack' for inputs 'ma-zal/pm2-slack', 'ma-zal/pm2-slack#own-branch',
* 'pm2-slack-1.0.0.tgz' or 'pm2-slack@1.0.0'.
*/
getCanonicModuleName: function(module_name) {
if (typeof module_name !== 'string') return null;
var canonic_module_name = module_name;
// Returns the module name from a .tgz package name (or the original name if it is not a valid pkg).
// Input: The package name (e.g. "foo.tgz", "foo-1.0.0.tgz", "folder/foo.tgz")
// Output: The module name
if (canonic_module_name.match(/\.tgz($|\?)/)) {
if (canonic_module_name.match(/^(.+\/)?([^\/]+)\.tgz($|\?)/)) {
canonic_module_name = canonic_module_name.match(/^(.+\/)?([^\/]+)\.tgz($|\?)/)[2];
if (canonic_module_name.match(/^(.+)-[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9_]+\.[0-9]+)?$/)) {
canonic_module_name = canonic_module_name.match(/^(.+)-[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9_]+\.[0-9]+)?$/)[1];
}
}
}
//pm2 install git+https://github.com/user/module
if(canonic_module_name.indexOf('git+') !== -1) {
canonic_module_name = canonic_module_name.split('/').pop();
}
//pm2 install https://github.com/user/module
if(canonic_module_name.indexOf('http') !== -1) {
var uri = url.parse(canonic_module_name);
canonic_module_name = uri.pathname.split('/').pop();
}
//pm2 install file:///home/user/module
else if(canonic_module_name.indexOf('file://') === 0) {
canonic_module_name = canonic_module_name.replace(/\/$/, '').split('/').pop();
}
//pm2 install username/module
else if(canonic_module_name.indexOf('/') !== -1) {
if (canonic_module_name.charAt(0) !== "@"){
canonic_module_name = canonic_module_name.split('/')[1];
}
}
//pm2 install @somescope/module@2.1.0-beta
if(canonic_module_name.lastIndexOf('@') > 0) {
canonic_module_name = canonic_module_name.substr(0,canonic_module_name.lastIndexOf("@"));
}
//pm2 install module#some-branch
if(canonic_module_name.indexOf('#') !== -1) {
canonic_module_name = canonic_module_name.split('#')[0];
}
if (canonic_module_name.indexOf('.git') !== -1) {
canonic_module_name = canonic_module_name.replace('.git', '');
}
return canonic_module_name;
},
checkPathIsNull: function(path) {
return path === 'NULL' || path === '/dev/null' || path === '\\\\.\\NUL';
},
generateUUID: function () {
var s = [];
var hexDigits = "0123456789abcdef";
for (var i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
s[14] = "4";
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
s[8] = s[13] = s[18] = s[23] = "-";
return s.join("");
}
};

46
api.hyungi.net/node_modules/pm2/lib/VersionCheck.js generated vendored Normal file
View File

@@ -0,0 +1,46 @@
var vCheck = require('@pm2/pm2-version-check')
var semver = require('semver')
var fs = require('fs')
var os = require('os')
function hasDockerEnv() {
try {
fs.statSync('/.dockerenv');
return true;
} catch (_) {
return false;
}
}
function hasDockerCGroup() {
try {
return fs.readFileSync('/proc/self/cgroup', 'utf8').includes('docker');
} catch (_) {
return false;
}
}
module.exports = function (opts) {
var params = {
state: opts.state,
version: opts.version
}
try {
params.os = os.type()
params.uptime = Math.floor(process.uptime())
params.nodev = process.versions.node
params.docker = hasDockerEnv() || hasDockerCGroup()
} catch(e) {
}
vCheck.runCheck(params, (err, pkg) => {
if (err) return false
if (!pkg.current_version) return false
if (opts.version && semver.lt(opts.version, pkg.current_version)) {
console.log('[PM2] This PM2 is not UP TO DATE')
console.log('[PM2] Upgrade to version %s', pkg.current_version)
}
})
}

117
api.hyungi.net/node_modules/pm2/lib/Watcher.js generated vendored Normal file
View File

@@ -0,0 +1,117 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
var chokidar = require('chokidar');
var util = require('util');
var log = require('debug')('pm2:watch');
module.exports = function ClusterMode(God) {
/**
* Watch folder for changes and restart
* @method watch
* @param {Object} pm2_env pm2 app environnement
* @return MemberExpression
*/
God.watch = {};
God.watch._watchers = {};
God.watch.enable = function(pm2_env) {
if (God.watch._watchers[pm2_env.pm_id]) {
God.watch._watchers[pm2_env.pm_id].close();
God.watch._watchers[pm2_env.pm_id] = null;
delete God.watch._watchers[pm2_env.pm_id];
}
log('Initial watch ', pm2_env.watch)
var watch = pm2_env.watch
if(typeof watch == 'boolean' || Array.isArray(watch) && watch.length === 0)
watch = pm2_env.pm_cwd;
log('Watching %s', watch);
var watch_options = {
ignored : pm2_env.ignore_watch || /[\/\\]\.|node_modules/,
persistent : true,
ignoreInitial : true,
cwd: pm2_env.pm_cwd
};
if (pm2_env.watch_options) {
watch_options = Object.assign(watch_options, pm2_env.watch_options);
}
log('Watch opts', watch_options);
var watcher = chokidar.watch(watch, watch_options);
console.log('[Watch] Start watching', pm2_env.name);
watcher.on('all', function(event, path) {
var self = this;
if (self.restarting === true) {
log('Already restarting, skipping');
return false;
}
self.restarting = true;
console.log('Change detected on path %s for app %s - restarting', path, pm2_env.name);
setTimeout(function() {
God.restartProcessName(pm2_env.name, function(err, list) {
self.restarting = false;
if (err) {
log('Error while restarting', err);
return false;
}
return log('Process restarted');
});
}, (pm2_env.watch_delay || 0));
return false;
});
watcher.on('error', function(e) {
console.error(e.stack || e);
});
God.watch._watchers[pm2_env.pm_id] = watcher;
//return God.watch._watchers[pm2_env.name];
},
/**
* Description
* @method close
* @param {} id
* @return
*/
God.watch.disableAll = function() {
var watchers = God.watch._watchers;
console.log('[Watch] PM2 is being killed. Watch is disabled to avoid conflicts');
for (var i in watchers) {
watchers[i].close && watchers[i].close();
watchers.splice(i, 1);
}
},
God.watch.disable = function(pm2_env) {
var watcher = God.watch._watchers[pm2_env.pm_id]
if (watcher) {
console.log('[Watch] Stop watching', pm2_env.name);
watcher.close();
delete God.watch._watchers[pm2_env.pm_id];
return true;
} else {
return false;
}
}
};

213
api.hyungi.net/node_modules/pm2/lib/Worker.js generated vendored Normal file
View File

@@ -0,0 +1,213 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
const vizion = require('vizion');
const eachLimit = require('async/eachLimit');
const debug = require('debug')('pm2:worker');
const domain = require('domain');
const Cron = require('croner');
const pkg = require('../package.json');
var cst = require('../constants.js');
var vCheck = require('./VersionCheck.js')
module.exports = function(God) {
var timer = null;
God.CronJobs = new Map();
God.Worker = {};
God.Worker.is_running = false;
God.getCronID = function(pm_id) {
return `cron-${pm_id}`
}
God.registerCron = function(pm2_env) {
if (!pm2_env ||
pm2_env.pm_id === undefined ||
!pm2_env.cron_restart ||
pm2_env.cron_restart == '0' ||
God.CronJobs.has(God.getCronID(pm2_env.pm_id)))
return;
var pm_id = pm2_env.pm_id
console.log('[PM2][WORKER] Registering a cron job on:', pm_id);
var job = Cron(pm2_env.cron_restart, function() {
God.restartProcessId({id: pm_id}, function(err, data) {
if (err)
console.error(err.stack || err);
return;
});
});
God.CronJobs.set(God.getCronID(pm_id), job);
}
/**
* Deletes the cron job on deletion of process
*/
God.deleteCron = function(id) {
if (typeof(id) !== 'undefined' && God.CronJobs.has(God.getCronID(id)) === false)
return;
console.log('[PM2] Deregistering a cron job on:', id);
var job = God.CronJobs.get(God.getCronID(id));
if (job)
job.stop();
God.CronJobs.delete(God.getCronID(id));
};
var _getProcessById = function(pm_id) {
var proc = God.clusters_db[pm_id];
return proc ? proc : null;
};
var maxMemoryRestart = function(proc_key, cb) {
var proc = _getProcessById(proc_key.pm2_env.pm_id);
if (!(proc &&
proc.pm2_env &&
proc_key.monit))
return cb();
if (proc_key.monit.memory !== undefined &&
proc.pm2_env.max_memory_restart !== undefined &&
proc.pm2_env.max_memory_restart < proc_key.monit.memory &&
proc.pm2_env.axm_options &&
proc.pm2_env.axm_options.pid === undefined) {
console.log('[PM2][WORKER] Process %s restarted because it exceeds --max-memory-restart value (current_memory=%s max_memory_limit=%s [octets])', proc.pm2_env.pm_id, proc_key.monit.memory, proc.pm2_env.max_memory_restart);
God.reloadProcessId({
id : proc.pm2_env.pm_id
}, function(err, data) {
if (err)
console.error(err.stack || err);
return cb();
});
}
else {
return cb();
}
};
// Deprecated
var versioningRefresh = function(proc_key, cb) {
var proc = _getProcessById(proc_key.pm2_env.pm_id);
if (!(proc &&
proc.pm2_env &&
(proc.pm2_env.vizion !== false && proc.pm2_env.vizion != "false") &&
proc.pm2_env.versioning &&
proc.pm2_env.versioning.repo_path)) {
return cb();
}
if (proc.pm2_env.vizion_running === true)
{
debug('Vizion is already running for proc id: %d, skipping this round', proc.pm2_env.pm_id);
return cb();
}
proc.pm2_env.vizion_running = true;
var repo_path = proc.pm2_env.versioning.repo_path;
vizion.analyze({
folder: proc.pm2_env.versioning.repo_path
},
function(err, meta) {
if (err != null)
return cb();
proc = _getProcessById(proc_key.pm2_env.pm_id);
if (!(proc &&
proc.pm2_env &&
proc.pm2_env.versioning &&
proc.pm2_env.versioning.repo_path)) {
console.error('Proc not defined anymore or versioning unknown');
return cb();
}
proc.pm2_env.vizion_running = false;
meta.repo_path = repo_path;
proc.pm2_env.versioning = meta;
debug('[PM2][WORKER] %s parsed for versioning', proc.pm2_env.name);
return cb();
});
};
var tasks = function() {
if (God.Worker.is_running === true) {
debug('[PM2][WORKER] Worker is already running, skipping this round');
return false;
}
God.Worker.is_running = true;
God.getMonitorData(null, function(err, data) {
if (err || !data || typeof(data) !== 'object') {
God.Worker.is_running = false;
return console.error(err);
}
eachLimit(data, 1, function(proc, next) {
if (!proc || !proc.pm2_env || proc.pm2_env.pm_id === undefined)
return next();
debug('[PM2][WORKER] Processing proc id:', proc.pm2_env.pm_id);
// Reset restart delay if application has an uptime of more > 30secs
if (proc.pm2_env.exp_backoff_restart_delay !== undefined &&
proc.pm2_env.prev_restart_delay && proc.pm2_env.prev_restart_delay > 0) {
var app_uptime = Date.now() - proc.pm2_env.pm_uptime
if (app_uptime > cst.EXP_BACKOFF_RESET_TIMER) {
var ref_proc = _getProcessById(proc.pm2_env.pm_id);
ref_proc.pm2_env.prev_restart_delay = 0
console.log(`[PM2][WORKER] Reset the restart delay, as app ${proc.name} has been up for more than ${cst.EXP_BACKOFF_RESET_TIMER}ms`)
}
}
// Check if application has reached memory threshold
maxMemoryRestart(proc, function() {
return next();
});
}, function(err) {
God.Worker.is_running = false;
debug('[PM2][WORKER] My job here is done, next job in %d seconds', parseInt(cst.WORKER_INTERVAL / 1000));
});
});
};
var wrappedTasks = function() {
var d = domain.create();
d.once('error', function(err) {
console.error('[PM2][WORKER] Error caught by domain:\n' + (err.stack || err));
God.Worker.is_running = false;
});
d.run(function() {
tasks();
});
};
God.Worker.start = function() {
timer = setInterval(wrappedTasks, cst.WORKER_INTERVAL);
setInterval(() => {
vCheck({
state: 'check',
version: pkg.version
})
}, 1000 * 60 * 60 * 24)
};
God.Worker.stop = function() {
if (timer !== null)
clearInterval(timer);
};
};

1033
api.hyungi.net/node_modules/pm2/lib/binaries/CLI.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

183
api.hyungi.net/node_modules/pm2/lib/binaries/DevCLI.js generated vendored Normal file
View File

@@ -0,0 +1,183 @@
'use strict';
process.env.PM2_NO_INTERACTION = 'true';
// Do not print banner
process.env.PM2_DISCRETE_MODE = true;
var commander = require('commander');
var PM2 = require('../..');
var Log = require('../API/Log');
var cst = require('../../constants.js');
var pkg = require('../../package.json');
var chalk = require('chalk');
var path = require('path');
var fmt = require('../tools/fmt.js');
var exec = require('child_process').exec;
var os = require('os');
commander.version(pkg.version)
.description('pm2-dev monitor for any file changes and automatically restart it')
.option('--raw', 'raw log output')
.option('--timestamp', 'print timestamp')
.option('--node-args <node_args>', 'space delimited arguments to pass to node in cluster mode - e.g. --node-args="--debug=7001 --trace-deprecation"')
.option('--ignore [files]', 'files to ignore while watching')
.option('--post-exec [cmd]', 'execute extra command after change detected')
.option('--silent-exec', 'do not output result of post command', false)
.option('--test-mode', 'debug mode for test suit')
.option('--interpreter <interpreter>', 'the interpreter pm2 should use for executing app (bash, python...)')
.option('--env [name]', 'select env_[name] env variables in process config file')
.option('--auto-exit', 'exit if all processes are errored/stopped or 0 apps launched')
.usage('pm2-dev app.js');
var pm2 = new PM2.custom({
pm2_home : path.join(os.homedir ? os.homedir() : (process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE), '.pm2-dev')
});
pm2.connect(function() {
commander.parse(process.argv);
});
function postExecCmd(command, cb) {
var exec_cmd = exec(command);
if (commander.silentExec !== true) {
exec_cmd.stdout.on('data', function(data) {
process.stdout.write(data);
});
exec_cmd.stderr.on('data', function(data) {
process.stderr.write(data);
});
}
exec_cmd.on('close', function done() {
if (cb) cb(null);
});
exec_cmd.on('error', function (err) {
console.error(err.stack || err);
});
};
function run(cmd, opts) {
var timestamp = opts.timestamp;
opts.watch = true;
opts.autostart = true;
opts.autorestart = true;
opts.restart_delay = 1000
if (opts.autoExit)
autoExit();
if (opts.ignore) {
opts.ignore_watch = opts.ignore.split(',')
opts.ignore_watch.push('node_modules');
}
if (timestamp === true)
timestamp = 'YYYY-MM-DD-HH:mm:ss';
pm2.start(cmd, opts, function(err, procs) {
if (err) {
console.error(err);
pm2.destroy(function() {
process.exit(0);
});
return false;
}
if (opts.testMode) {
return pm2.disconnect(function() {
});
}
fmt.sep();
fmt.title('PM2 development mode');
fmt.field('Apps started', procs.map(function(p) { return p.pm2_env.name } ));
fmt.field('Processes started', chalk.bold(procs.length));
fmt.field('Watch and Restart', chalk.green('Enabled'));
fmt.field('Ignored folder', opts.ignore_watch || 'node_modules');
if (opts.postExec)
fmt.field('Post restart cmd', opts.postExec);
fmt.sep();
setTimeout(function() {
pm2.Client.launchBus(function(err, bus) {
bus.on('process:event', function(packet) {
if (packet.event == 'online') {
if (opts.postExec)
postExecCmd(opts.postExec);
}
});
});
}, 1000);
Log.devStream(pm2.Client, 'all', opts.raw, timestamp, false);
process.on('SIGINT', function() {
console.log('>>>>> [PM2 DEV] Stopping current development session');
pm2.delete('all', function() {
pm2.destroy(function() {
process.exit(0);
});
});
});
});
}
commander.command('*')
.action(function(cmd, opts){
run(cmd, commander);
});
commander.command('start <file|json_file>')
.description('start target config file/script in development mode')
.action(function(cmd, opts) {
run(cmd, commander);
});
function exitPM2() {
if (pm2 && pm2.connected == true) {
console.log(chalk.green.bold('>>> Exiting PM2'));
pm2.kill(function() {
process.exit(0);
});
}
else
process.exit(0);
}
function autoExit(final) {
setTimeout(function() {
pm2.list(function(err, apps) {
if (err) console.error(err.stack || err);
var online_count = 0;
apps.forEach(function(app) {
if (app.pm2_env.status == cst.ONLINE_STATUS ||
app.pm2_env.status == cst.LAUNCHING_STATUS)
online_count++;
});
if (online_count == 0) {
console.log('0 application online, exiting');
if (final == true)
process.exit(1);
else
autoExit(true);
return false;
}
autoExit(false);
});
}, 3000);
}
if (process.argv.length == 2) {
commander.outputHelp();
exitPM2();
}

101
api.hyungi.net/node_modules/pm2/lib/binaries/Runtime.js generated vendored Normal file
View File

@@ -0,0 +1,101 @@
'use strict';
var commander = require('commander');
var PM2 = require('../..');
var Log = require('../../lib/API/Log');
var cst = require('../../constants.js');
var pkg = require('../../package.json');
var path = require('path');
var pm2;
// Do not print banner
process.env.PM2_DISCRETE_MODE = true;
commander.version(pkg.version)
.description('pm2-runtime is an automatic pmx injection that runs in simulated no-daemon environment')
.option('--auto-manage', 'keep application online after command exit')
.option('--fast-boot', 'boot app faster by keeping pm2 runtime online in background (effective at second exit/start)')
.option('--web [port]', 'launch process web api on [port] default to 9615')
.option('--secret [key]', 'PM2 plus secret key')
.option('--public [key]', 'PM2 plus public key')
.option('--machine-name [name]', 'PM2 plus machine name')
.option('--env [name]', 'select env_[name] env variables in process config file')
.option('--watch', 'Watch and Restart')
.option('-i --instances <number>', 'launch [number] instances with load-balancer')
.usage('pm2-runtime app.js');
commander.command('*')
.action(function(cmd){
pm2 = new PM2.custom({
pm2_home : path.join(process.env.HOME, '.pm3'),
secret_key : cst.SECRET_KEY || commander.secret,
public_key : cst.PUBLIC_KEY || commander.public,
machine_name : cst.MACHINE_NAME || commander.machineName
});
pm2.connect(function() {
if (commander.web) {
var port = commander.web === true ? cst.WEB_PORT : commander.web;
pm2.web(port);
}
pm2.start(cmd, commander, function(err, obj) {
if (process.env.PM2_RUNTIME_DEBUG) {
return pm2.disconnect(function() {});
}
if (err) {
console.error(err);
return process.exit(1);
}
var pm_id = obj[0].pm2_env.pm_id;
if (commander.instances == undefined) {
return pm2.attach(pm_id, function() {
exitPM2();
});
}
if (commander.json === true)
Log.jsonStream(pm2.Client, pm_id);
else if (commander.format === true)
Log.formatStream(pm2.Client, pm_id, false, 'YYYY-MM-DD-HH:mm:ssZZ');
else
Log.stream(pm2.Client, 'all', true);
});
});
});
if (process.argv.length == 2) {
commander.outputHelp();
process.exit(1);
}
process.on('SIGINT', function() {
exitPM2();
});
process.on('SIGTERM', function() {
exitPM2();
});
commander.parse(process.argv);
function exitPM2() {
console.log('Exited at %s', new Date());
if (commander.autoManage)
return process.exit(0);
if (commander.fastBoot) {
return pm2.delete('all', function() {
process.exit(0);
});
}
pm2.kill(function() {
process.exit(0);
});
}

View File

@@ -0,0 +1,192 @@
'use strict';
/**
* Specialized PM2 CLI for Containers
*/
var commander = require('commander');
var PM2 = require('../..');
var Log = require('../../lib/API/Log');
var cst = require('../../constants.js');
var pkg = require('../../package.json');
var path = require('path');
var DEFAULT_FAIL_COUNT = 3;
process.env.PM2_DISCRETE_MODE = true;
commander.version(pkg.version)
.description('pm2-runtime is a drop-in replacement Node.js binary for containers')
.option('-i --instances <number>', 'launch [number] of processes automatically load-balanced. Increase overall performances and performance stability.')
.option('--secret [key]', '[MONITORING] PM2 plus secret key')
.option('--no-autostart', 'add an app without automatic start')
.option('--no-autorestart', 'start an app without automatic restart')
.option('--stop-exit-codes <exit_codes...>', 'specify a list of exit codes that should skip automatic restart')
.option('--node-args <node_args>', 'space delimited arguments to pass to node in cluster mode - e.g. --node-args="--debug=7001 --trace-deprecation"')
.option('-n --name <name>', 'set a <name> for script')
.option('--max-memory-restart <memory>', 'specify max memory amount used to autorestart (in octet or use syntax like 100M)')
.option('-c --cron <cron_pattern>', 'restart a running process based on a cron pattern')
.option('--interpreter <interpreter>', 'the interpreter pm2 should use for executing app (bash, python...)')
.option('--public [key]', '[MONITORING] PM2 plus public key')
.option('--machine-name [name]', '[MONITORING] PM2 plus machine name')
.option('--trace', 'enable transaction tracing with km')
.option('--v8', 'enable v8 data collecting')
.option('--format', 'output logs formated like key=val')
.option('--raw', 'raw output (default mode)')
.option('--formatted', 'formatted log output |id|app|log')
.option('--json', 'output logs in json format')
.option('--delay <seconds>', 'delay start of configuration file by <seconds>', 0)
.option('--web [port]', 'launch process web api on [port] (default to 9615)')
.option('--only <application-name>', 'only act on one application of configuration')
.option('--no-auto-exit', 'do not exit if all processes are errored/stopped or 0 apps launched')
.option('--env [name]', 'inject env_[name] env variables in process config file')
.option('--watch', 'watch and restart application on file change')
.option('--error <path>', 'error log file destination (default disabled)', '/dev/null')
.option('--output <path>', 'output log file destination (default disabled)', '/dev/null')
.option('--deep-monitoring', 'enable all monitoring tools (equivalent to --v8 --event-loop-inspector --trace)')
.allowUnknownOption()
.usage('app.js');
commander.command('*')
.action(function(cmd){
Runtime.instanciate(cmd);
});
commander.command('start <app.js|json_file>')
.description('start an application or json ecosystem file')
.action(function(cmd) {
Runtime.instanciate(cmd);
});
if (process.argv.length == 2) {
commander.outputHelp();
process.exit(1);
}
var Runtime = {
pm2 : null,
instanciate : function(cmd) {
this.pm2 = new PM2.custom({
pm2_home : process.env.PM2_HOME || path.join(process.env.HOME, '.pm2'),
secret_key : cst.SECRET_KEY || commander.secret,
public_key : cst.PUBLIC_KEY || commander.public,
machine_name : cst.MACHINE_NAME || commander.machineName,
daemon_mode : process.env.PM2_RUNTIME_DEBUG || false
});
this.pm2.connect(function(err, pm2_meta) {
process.on('SIGINT', function() {
Runtime.exit();
});
process.on('SIGTERM', function() {
Runtime.exit();
});
Runtime.startLogStreaming();
Runtime.startApp(cmd, function(err) {
if (err) {
console.error(err.message || err);
return Runtime.exit();
}
});
});
},
/**
* Log Streaming Management
*/
startLogStreaming : function() {
if (commander.json === true)
Log.jsonStream(this.pm2.Client, 'all');
else if (commander.format === true)
Log.formatStream(this.pm2.Client, 'all', false, 'YYYY-MM-DD-HH:mm:ssZZ');
else
Log.stream(this.pm2.Client, 'all', !commander.formatted, commander.timestamp, true);
},
/**
* Application Startup
*/
startApp : function(cmd, cb) {
function exec() {
this.pm2.start(cmd, commander, function(err, obj) {
if (err)
return cb(err);
if (obj && obj.length == 0)
return cb(new Error(`0 application started (no apps to run on ${cmd})`))
if (commander.web) {
var port = commander.web === true ? cst.WEB_PORT : commander.web;
Runtime.pm2.web(port);
}
if (commander.autoExit) {
setTimeout(function() {
Runtime.autoExitWorker();
}, 4000);
}
// For Testing purpose (allow to auto exit CLI)
if (process.env.PM2_RUNTIME_DEBUG)
Runtime.pm2.disconnect(function() {});
return cb(null, obj);
});
}
// via --delay <seconds> option
setTimeout(exec.bind(this), commander.delay * 1000);
},
/**
* Exit runtime mgmt
*/
exit : function(code) {
if (!this.pm2) return process.exit(1);
this.pm2.kill(function() {
process.exit(code || 0);
});
},
/**
* Exit current PM2 instance if 0 app is online
* function activated via --auto-exit
*/
autoExitWorker : function(fail_count) {
var interval = 2000;
if (typeof(fail_count) =='undefined')
fail_count = DEFAULT_FAIL_COUNT;
var timer = setTimeout(function () {
Runtime.pm2.list(function (err, apps) {
if (err) {
console.error('Could not run pm2 list');
return Runtime.autoExitWorker();
}
var appOnline = 0;
apps.forEach(function (app) {
if (!app.pm2_env.pmx_module &&
(app.pm2_env.status === cst.ONLINE_STATUS ||
app.pm2_env.status === cst.LAUNCHING_STATUS)) {
appOnline++;
}
});
if (appOnline === 0) {
console.log('0 application online, retry =', fail_count);
if (fail_count <= 0)
return Runtime.exit(2);
return Runtime.autoExitWorker(--fail_count);
}
Runtime.autoExitWorker();
});
}, interval);
timer.unref();
}
}
commander.parse(process.argv);

229
api.hyungi.net/node_modules/pm2/lib/completion.js generated vendored Normal file
View File

@@ -0,0 +1,229 @@
/**
* Copyright 2013-2022 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
var fs = require('fs'),
pth = require('path');
// hacked from node-tabtab 0.0.4 https://github.com/mklabs/node-tabtab.git
// Itself based on npm completion by @isaac
exports.complete = function complete(name, completer, cb) {
// cb not there, assume callback is completer and
// the completer is the executable itself
if(!cb) {
cb = completer;
completer = name;
}
var env = parseEnv();
// if not a complete command, return here.
if(!env.complete) return cb();
// if install cmd, add complete script to either ~/.bashrc or ~/.zshrc
if(env.install) return install(name, completer, function(err, state) {
console.log(state || err.message);
if(err) return cb(err);
cb(null, null, state);
});
// if install cmd, add complete script to either ~/.bashrc or ~/.zshrc
if(env.uninstall) return uninstall(name, completer, function(err, state) {
console.log(state || err.message);
if(err) return cb(err);
cb(null, null, state);
});
// if the COMP_* are not in the env, then dump the install script.
if(!env.words || !env.point || !env.line) return script(name, completer, function(err, content) {
if(err) return cb(err);
process.stdout.write(content, function (n) { cb(null, null, content); });
process.stdout.on("error", function (er) {
// Darwin is a real dick sometimes.
//
// This is necessary because the "source" or "." program in
// bash on OS X closes its file argument before reading
// from it, meaning that you get exactly 1 write, which will
// work most of the time, and will always raise an EPIPE.
//
// Really, one should not be tossing away EPIPE errors, or any
// errors, so casually. But, without this, `. <(npm completion)`
// can never ever work on OS X.
// -- isaacs
// https://github.com/isaacs/npm/blob/master/lib/completion.js#L162
if (er.errno === "EPIPE") er = null
cb(er, null, content);
});
cb(null, null, content);
});
var partial = env.line.substr(0, env.point),
last = env.line.split(' ').slice(-1).join(''),
lastPartial = partial.split(' ').slice(-1).join(''),
prev = env.line.split(' ').slice(0, -1).slice(-1)[0];
cb(null, {
line: env.line,
words: env.words,
point: env.point,
partial: partial,
last: last,
prev: prev,
lastPartial: lastPartial
});
};
// simple helper function to know if the script is run
// in the context of a completion command. Also mapping the
// special `<pkgname> completion` cmd.
exports.isComplete = function isComplete() {
var env = parseEnv();
return env.complete || (env.words && env.point && env.line);
};
exports.parseOut = function parseOut(str) {
var shorts = str.match(/\s-\w+/g);
var longs = str.match(/\s--\w+/g);
return {
shorts: shorts.map(trim).map(cleanPrefix),
longs: longs.map(trim).map(cleanPrefix)
};
};
// specific to cake case
exports.parseTasks = function(str, prefix, reg) {
var tasks = str.match(reg || new RegExp('^' + prefix + '\\s[^#]+', 'gm')) || [];
return tasks.map(trim).map(function(s) {
return s.replace(prefix + ' ', '');
});
};
exports.log = function log(arr, o, prefix) {
prefix = prefix || '';
arr = Array.isArray(arr) ? arr : [arr];
arr.filter(abbrev(o)).forEach(function(v) {
console.log(prefix + v);
});
}
function trim (s) {
return s.trim();
}
function cleanPrefix(s) {
return s.replace(/-/g, '');
}
function abbrev(o) { return function(it) {
return new RegExp('^' + o.last.replace(/^--?/g, '')).test(it);
}}
// output the completion.sh script to the console for install instructions.
// This is actually a 'template' where the package name is used to setup
// the completion on the right command, and properly name the bash/zsh functions.
function script(name, completer, cb) {
var p = pth.join(__dirname, 'completion.sh');
fs.readFile(p, 'utf8', function (er, d) {
if (er) return cb(er);
cb(null, d);
});
}
function install(name, completer, cb) {
var markerIn = '###-begin-' + name + '-completion-###',
markerOut = '###-end-' + name + '-completion-###';
var rc, scriptOutput;
readRc(completer, function(err, file) {
if(err) return cb(err);
var part = file.split(markerIn)[1];
if(part) {
return cb(null, ' ✗ ' + completer + ' tab-completion has been already installed. Do nothing.');
}
rc = file;
next();
});
script(name, completer, function(err, file) {
scriptOutput = file;
next();
});
function next() {
if(!rc || !scriptOutput) return;
writeRc(rc + scriptOutput, function(err) {
if(err) return cb(err);
return cb(null, ' ✓ ' + completer + ' tab-completion installed.');
});
}
}
function uninstall(name, completer, cb) {
var markerIn = '\n\n###-begin-' + name + '-completion-###',
markerOut = '###-end-' + name + '-completion-###\n';
readRc(completer, function(err, file) {
if(err) return cb(err);
var part = file.split(markerIn)[1];
if(!part) {
return cb(null, ' ✗ ' + completer + ' tab-completion has been already uninstalled. Do nothing.');
}
part = markerIn + part.split(markerOut)[0] + markerOut;
writeRc(file.replace(part, ''), function(err) {
if(err) return cb(err);
return cb(null, ' ✓ ' + completer + ' tab-completion uninstalled.');
});
});
}
function readRc(completer, cb) {
var file = '.' + process.env.SHELL.match(/\/bin\/(\w+)/)[1] + 'rc',
filepath = pth.join(process.env.HOME, file);
fs.lstat(filepath, function (err, stats) {
if(err) return cb(new Error("No " + file + " file. You'll have to run instead: " + completer + " completion >> ~/" + file));
fs.readFile(filepath, 'utf8', cb);
});
}
function writeRc(content, cb) {
var file = '.' + process.env.SHELL.match(/\/bin\/(\w+)/)[1] + 'rc',
filepath = pth.join(process.env.HOME, file);
fs.lstat(filepath, function (err, stats) {
if(err) return cb(new Error("No " + file + " file. You'll have to run instead: " + completer + " completion >> ~/" + file));
fs.writeFile(filepath, content, cb);
});
}
function installed (marker, completer, cb) {
readRc(completer, function(err, file) {
if(err) return cb(err);
var installed = file.match(marker);
return cb(!!installed);
});
}
function parseEnv() {
var args = process.argv.slice(2),
complete = args[0] === 'completion';
return {
args: args,
complete: complete,
install: complete && args[1] === 'install',
uninstall: complete && args[1] === 'uninstall',
words: +process.env.COMP_CWORD,
point: +process.env.COMP_POINT,
line: process.env.COMP_LINE
}
};

40
api.hyungi.net/node_modules/pm2/lib/completion.sh generated vendored Normal file
View File

@@ -0,0 +1,40 @@
###-begin-pm2-completion-###
### credits to npm for the completion file model
#
# Installation: pm2 completion >> ~/.bashrc (or ~/.zshrc)
#
COMP_WORDBREAKS=${COMP_WORDBREAKS/=/}
COMP_WORDBREAKS=${COMP_WORDBREAKS/@/}
export COMP_WORDBREAKS
if type complete &>/dev/null; then
_pm2_completion () {
local si="$IFS"
IFS=$'\n' COMPREPLY=($(COMP_CWORD="$COMP_CWORD" \
COMP_LINE="$COMP_LINE" \
COMP_POINT="$COMP_POINT" \
pm2 completion -- "${COMP_WORDS[@]}" \
2>/dev/null)) || return $?
IFS="$si"
}
complete -o default -F _pm2_completion pm2
elif type compctl &>/dev/null; then
_pm2_completion () {
local cword line point words si
read -Ac words
read -cn cword
let cword-=1
read -l line
read -ln point
si="$IFS"
IFS=$'\n' reply=($(COMP_CWORD="$cword" \
COMP_LINE="$line" \
COMP_POINT="$point" \
pm2 completion -- "${words[@]}" \
2>/dev/null)) || return $?
IFS="$si"
}
compctl -K _pm2_completion + -f + pm2
fi
###-end-pm2-completion-###

36
api.hyungi.net/node_modules/pm2/lib/motd generated vendored Normal file
View File

@@ -0,0 +1,36 @@
-------------
__/\\\\\\\\\\\\\____/\\\\____________/\\\\____/\\\\\\\\\_____
_\/\\\/////////\\\_\/\\\\\\________/\\\\\\__/\\\///////\\\___
_\/\\\_______\/\\\_\/\\\//\\\____/\\\//\\\_\///______\//\\\__
_\/\\\\\\\\\\\\\/__\/\\\\///\\\/\\\/_\/\\\___________/\\\/___
_\/\\\/////////____\/\\\__\///\\\/___\/\\\________/\\\//_____
_\/\\\_____________\/\\\____\///_____\/\\\_____/\\\//________
_\/\\\_____________\/\\\_____________\/\\\___/\\\/___________
_\/\\\_____________\/\\\_____________\/\\\__/\\\\\\\\\\\\\\\_
_\///______________\///______________\///__\///////////////__
Runtime Edition
PM2 is a Production Process Manager for Node.js applications
with a built-in Load Balancer.
Start and Daemonize any application:
$ pm2 start app.js
Load Balance 4 instances of api.js:
$ pm2 start api.js -i 4
Monitor in production:
$ pm2 monitor
Make pm2 auto-boot at server restart:
$ pm2 startup
To go further checkout:
http://pm2.io/
-------------

View File

@@ -0,0 +1,7 @@
FROM anapsix/alpine-java:latest
RUN apk update && apk add git && rm -rf /var/cache/apk/*
RUN npm install pm2@next -g
RUN mkdir -p /var/app
WORKDIR /var/app

View File

@@ -0,0 +1,8 @@
FROM keymetrics/pm2:latest
RUN mkdir -p /var/app
WORKDIR /var/app
COPY ./package.json /var/app
RUN npm install

View File

@@ -0,0 +1,7 @@
FROM anapsix/alpine-ruby:latest
RUN apk update && apk add git && rm -rf /var/cache/apk/*
RUN npm install pm2@next -g
RUN mkdir -p /var/app
WORKDIR /var/app

View File

@@ -0,0 +1,24 @@
const config = {
apps : [{
script: 'index.js',
watch: '.'
}, {
script: './service-worker/',
watch: ['./service-worker']
}],
deploy : {
production : {
user : 'SSH_USERNAME',
host : 'SSH_HOSTMACHINE',
ref : 'origin/master',
repo : 'GIT_REPOSITORY',
path : 'DESTINATION_PATH',
'pre-deploy-local': '',
'post-deploy' : 'npm install && pm2 reload ecosystem.config.js --env production',
'pre-setup': ''
}
}
};
export default config;

View File

@@ -0,0 +1,8 @@
const config = {
apps : [{
name : "app1",
script : "./app.js"
}]
}
export default config;

View File

@@ -0,0 +1,6 @@
module.exports = {
apps : [{
name : "app1",
script : "./app.js"
}]
}

View File

@@ -0,0 +1,22 @@
module.exports = {
apps : [{
script: 'index.js',
watch: '.'
}, {
script: './service-worker/',
watch: ['./service-worker']
}],
deploy : {
production : {
user : 'SSH_USERNAME',
host : 'SSH_HOSTMACHINE',
ref : 'origin/master',
repo : 'GIT_REPOSITORY',
path : 'DESTINATION_PATH',
'pre-deploy-local': '',
'post-deploy' : 'npm install && pm2 reload ecosystem.config.js --env production',
'pre-setup': ''
}
}
};

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.PM2</string>
<key>UserName</key>
<string>%USER%</string>
<key>KeepAlive</key>
<true/>
<key>ProgramArguments</key>
<array>
<string>/bin/sh</string>
<string>-c</string>
<string>%PM2_PATH% resurrect</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>OnDemand</key>
<false/>
<key>LaunchOnlyOnce</key>
<true/>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>%NODE_PATH%</string>
<key>PM2_HOME</key>
<string>%HOME_PATH%</string>
</dict>
<key>StandardErrorPath</key>
<string>/tmp/com.PM2.err</string>
<key>StandardOutPath</key>
<string>/tmp/com.PM2.out</string>
</dict>
</plist>

View File

@@ -0,0 +1,52 @@
#!/sbin/openrc-run
# Copyright 2013-2022 the PM2 project authors. All rights reserved.
# Init script automatically generated by pm2 startup
description="Production process manager for Node.js apps with a built-in load balancer."
extra_started_commands="reload"
PM2="%PM2_PATH%"
user=${PM2_USER:-%USER%}
export PM2_HOME=$(eval echo ~${user})"/.pm2/"
# Options for start-stop-daemon (default start function)
command=${PM2}
command_user=${user}
command_args="resurrect"
pidfile=${PM2_HOME}/pm2.pid
run_pm2_as_user() {
einfo "${PM2} $@"
eval su -l ${user} -c \'${PM2} $@\'
}
depend() {
need net
need localmount
after bootmisc
}
start_post() {
if [ "${user}" == "root" ]; then
ewarn "PM2: Better run this daemon as a non root user. To set this user create"
ewarn "PM2: /etc/conf.d/pm2 file and define 'PM2_USER=user' there."
ewarn "PM2: Note user MUST have home directory for PM2 logs/state/etc..."
fi
einfo "PM2: Process Manager started. To start services run:"
einfo "PM2: # su -l ${user} -c '$PM2 start /path/to/app'"
}
stop() {
ebegin "Stopping PM2 process manager..."
run_pm2_as_user dump
run_pm2_as_user kill
eend $?
}
reload() {
ebegin "Reloading pm2"
run_pm2_as_user reload all
eend $?
}
# vim: ts=4

View File

@@ -0,0 +1,86 @@
#!/bin/bash
#
# pm2 Process manager for NodeJS
#
# chkconfig: 345 80 20
#
# description: PM2 next gen process manager for Node.js
# processname: pm2
#
### BEGIN INIT INFO
# Provides: pm2
# Required-Start: $local_fs $remote_fs
# Required-Stop: $local_fs $remote_fs
# Should-Start: $network
# Should-Stop: $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: PM2 init script
# Description: PM2 is the next gen process manager for Node.js
### END INIT INFO
NAME=pm2
PM2=%PM2_PATH%
USER=%USER%
export PATH=%NODE_PATH%:$PATH
export PM2_HOME="%HOME_PATH%"
lockfile="/var/lock/subsys/pm2-init.sh"
super() {
su - $USER -c "PATH=$PATH; PM2_HOME=$PM2_HOME $*"
}
start() {
echo "Starting $NAME"
super $PM2 resurrect
retval=$?
[ $retval -eq 0 ] && touch $lockfile
}
stop() {
echo "Stopping $NAME"
super $PM2 kill
rm -f $lockfile
}
restart() {
echo "Restarting $NAME"
stop
start
}
reload() {
echo "Reloading $NAME"
super $PM2 reload all
}
status() {
echo "Status for $NAME:"
super $PM2 list
RETVAL=$?
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status
;;
restart)
restart
;;
reload)
reload
;;
*)
echo "Usage: {start|stop|status|restart|reload}"
exit 1
;;
esac
exit $RETVAL

View File

@@ -0,0 +1,41 @@
#!/bin/sh
#
# from /usr/ports/infrastructure/templates/rc.template
daemon="/usr/local/bin/pm2"
#daemon_flags=
#daemon_rtable=0
#daemon_timeout="30"
daemon_user="%USER%"
. /etc/rc.d/rc.subr
pexp="node: PM2.*God Daemon.*"
#rc_bg= # (undefined)
#rc_reload= # (undefined)
#rc_usercheck=YES
#rc_pre() {
#}
rc_start() {
${rcexec} "${daemon} ${daemon_flags} resurrect"
}
#rc_check() {
# pgrep -T "${daemon_rtable}" -q -xf "${pexp}"
#}
rc_reload() {
${rcexec} "${daemon} reload all"
#pkill -HUP -T "${daemon_rtable}" -xf "${pexp}"
}
#rc_stop() {
# pkill -T "${daemon_rtable}" -xf "${pexp}"
#}
#rc_post() {
#}
rc_cmd $1

View File

@@ -0,0 +1,44 @@
#!/bin/sh
# PROVIDE: pm2
# REQUIRE: LOGIN
# KEYWORD: shutdown
. /etc/rc.subr
name="%SERVICE_NAME%"
rcvar="%SERVICE_NAME%_enable"
start_cmd="pm2_start"
stop_cmd="pm2_stop"
reload_cmd="pm2_reload"
status_cmd="pm2_status"
extra_commands="reload status"
pm2()
{
env PATH="$PATH:%NODE_PATH%" PM2_HOME="%HOME_PATH%" su -m "%USER%" -c "%PM2_PATH% $*"
}
pm2_start()
{
pm2 resurrect
}
pm2_stop()
{
pm2 kill
}
pm2_reload()
{
pm2 reload all
}
pm2_status()
{
pm2 list
}
load_rc_config $name
run_rc_command "$1"

View File

@@ -0,0 +1,43 @@
<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
<service_bundle type="manifest" name="%SERVICE_NAME%">
<service name="application/%SERVICE_NAME%" type="service" version="1">
<create_default_instance enabled="false"/>
<single_instance/>
<dependency name="network" grouping="require_all" restart_on="error" type="service">
<service_fmri value="svc:/milestone/network:default"/>
</dependency>
<dependency name="filesystem" grouping="require_all" restart_on="error" type="service">
<service_fmri value="svc:/system/filesystem/local"/>
</dependency>
<method_context>
<method_environment>
<envvar name='PATH' value="%NODE_PATH%:/usr/local/sbin:/usr/local/bin:/opt/local/sbin:/opt/local/bin:/usr/sbin:/usr/bin:/sbin"/>
<envvar name='PM2_HOME' value="%HOME_PATH%"/>
</method_environment>
</method_context>
<exec_method type="method" name="start" exec="%PM2_PATH% resurrect" timeout_seconds="60"/>
<exec_method type="method" name="refresh" exec="%PM2_PATH% reload all" timeout_seconds="60"/>
<exec_method type="method" name="stop" exec="%PM2_PATH% kill" timeout_seconds="60"/>
<property_group name="startd" type="framework">
<propval name="duration" type="astring" value="contract"/>
<propval name="ignore_error" type="astring" value="core,signal"/>
</property_group>
<property_group name="application" type="application"></property_group>
<stability value="Evolving"/>
<template>
<common_name>
<loctext xml:lang="C">
PM2 process manager
</loctext>
</common_name>
</template>
</service>
</service_bundle>

View File

@@ -0,0 +1,22 @@
[Unit]
Description=PM2 process manager
Documentation=https://pm2.keymetrics.io/
After=network-online.target
Restart=on-failure
[Service]
Type=forking
User=%USER%
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
Environment=PATH=%NODE_PATH%:/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
Environment=PM2_HOME=%HOME_PATH%
PIDFile=%HOME_PATH%/pm2.pid
ExecStart=%PM2_PATH% resurrect
ExecReload=%PM2_PATH% reload all
ExecStop=%PM2_PATH% kill
[Install]
WantedBy=network-online.target

View File

@@ -0,0 +1,22 @@
[Unit]
Description=PM2 process manager
Documentation=https://pm2.keymetrics.io/
After=network.target
[Service]
Type=forking
User=%USER%
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
Environment=PATH=%NODE_PATH%:/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
Environment=PM2_HOME=%HOME_PATH%
PIDFile=%HOME_PATH%/pm2.pid
Restart=on-failure
ExecStart=%PM2_PATH% resurrect
ExecReload=%PM2_PATH% reload all
ExecStop=%PM2_PATH% kill
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,103 @@
#!/bin/bash
### BEGIN INIT INFO
# Provides: pm2
# Required-Start: $local_fs $remote_fs $network
# Required-Stop: $local_fs $remote_fs $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: PM2 Init script
# Description: PM2 process manager
### END INIT INFO
NAME=pm2
PM2=%PM2_PATH%
USER=%USER%
DEFAULT=/etc/default/$NAME
export PATH=%NODE_PATH%:$PATH
export PM2_HOME="%HOME_PATH%"
# The following variables can be overwritten in $DEFAULT
# maximum number of open files
MAX_OPEN_FILES=
# overwrite settings from default file
if [ -f "$DEFAULT" ]; then
. "$DEFAULT"
fi
# set maximum open files if set
if [ -n "$MAX_OPEN_FILES" ]; then
ulimit -n $MAX_OPEN_FILES
fi
get_user_shell() {
local shell
shell=$(getent passwd "${1:-$(whoami)}" | cut -d: -f7 | sed -e 's/[[:space:]]*$//')
if [[ $shell == *"/sbin/nologin" ]] || [[ $shell == "/bin/false" ]] || [[ -z "$shell" ]];
then
shell="/bin/bash"
fi
echo "$shell"
}
super() {
local shell
shell=$(get_user_shell $USER)
su - "$USER" -s "$shell" -c "PATH=$PATH; PM2_HOME=$PM2_HOME $*"
}
start() {
echo "Starting $NAME"
super $PM2 resurrect
}
stop() {
super $PM2 kill
}
restart() {
echo "Restarting $NAME"
stop
start
}
reload() {
echo "Reloading $NAME"
super $PM2 reload all
}
status() {
echo "Status for $NAME:"
super $PM2 list
RETVAL=$?
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status
;;
restart)
restart
;;
reload)
reload
;;
force-reload)
reload
;;
*)
echo "Usage: {start|stop|status|restart|reload|force-reload}"
exit 1
;;
esac
exit $RETVAL

View File

@@ -0,0 +1,10 @@
%HOME_PATH%/pm2.log %HOME_PATH%/logs/*.log {
rotate 12
weekly
missingok
notifempty
compress
delaycompress
copytruncate
create 0640 %USER% %USER%
}

View File

@@ -0,0 +1,14 @@
# Basic HTTP Server and Cluster mode
In this boilerplate it will start an http server in cluster mode.
You can check the content of the ecosystem.config.js on how to start mutliple instances of the same HTTP application in order to get the most from your working system.
## Via CLI
Via CLI you can start any HTTP/TCP application in cluster mode with:
```bash
$ pm2 start api.js -i max
```

View File

@@ -0,0 +1,9 @@
var http = require('http');
var server = http.createServer(function(req, res) {
res.writeHead(200);
res.end('hey');
}).listen(process.env.PORT || 8000, function() {
console.log('App listening on port %d', server.address().port);
});

View File

@@ -0,0 +1,14 @@
module.exports = {
apps : [{
name: 'API',
script: 'api.js',
instances: 4,
max_memory_restart: '1G',
env: {
NODE_ENV: 'development'
},
env_production: {
NODE_ENV: 'production'
}
}]
};

View File

@@ -0,0 +1,11 @@
{
"name": "simple-http-server",
"version": "1.0.0",
"description": "Simple HTTP server that can be used in cluster mode",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}

View File

@@ -0,0 +1,45 @@
# pm2 custom metrics boilerplate
In this boilerplate you will discover a working example of custom metrics feature.
Metrics covered are:
- io.metric
- io.counter
- io.meter
- io.histogram
## What is Custom Metrics?
Custom metrics is a powerfull way to get more visibility from a running application. It will allow you to monitor in realtime the current value of variables, know the number of actions being processed, measure latency and much more.
Once you have plugged in some custom metrics you will be able to monitor their value in realtime with
`pm2 monit`
Or
`pm2 describe`
Or on the PM2+ Web interface
`pm2 open`
## Example
```javascript
const io = require('@pm2/io')
const currentReq = io.counter({
name: 'CM: Current Processing',
type: 'counter'
})
setInterval(() => {
currentReq.inc()
}, 1000)
```
## Documentation
https://doc.pm2.io/en/plus/guide/custom-metrics/

View File

@@ -0,0 +1,66 @@
const io = require('@pm2/io')
// Straight Metric
var user_count = 10
const users = io.metric({
name: 'CM: Realtime user',
value: () => {
return user_count
}
})
// or users.set(user_count)
// Counter (.inc() .dec())
const currentReq = io.counter({
name: 'CM: Current Processing',
type: 'counter'
})
setInterval(() => {
currentReq.inc()
}, 1000)
// Meter
const reqsec = io.meter({
name: 'CM: req/sec'
})
setInterval(() => {
reqsec.mark()
}, 100)
// Histogram
const latency = io.histogram({
name: 'CM: latency'
});
var latencyValue = 0;
setInterval(() => {
latencyValue = Math.round(Math.random() * 100);
latency.update(latencyValue);
}, 100)
////////////////////
// Custom Actions //
////////////////////
io.action('add user', (done) => {
user_count++
done({success:true})
})
io.action('remove user', (done) => {
user_count++
done({success:true})
})
io.action('with params', (arg, done) => {
console.log(arg)
done({success:arg})
})

View File

@@ -0,0 +1,12 @@
module.exports = {
apps : [{
name: 'Custom Metrics',
script: 'custom-metrics.js',
env: {
NODE_ENV: 'development'
},
env_production: {
NODE_ENV: 'production'
}
}]
};

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