@ -1,4 +1,3 @@
|
||||
## Contribution guidelines |
||||
- Try to keep it close to original design. Key idea here being zero configuration. So automate by going out of the way. |
||||
- Tests to cover new functionality. |
||||
- If the issue is a major or large change. Please create a PR issue before working on it. |
||||
``` |
||||
See ./contribute folder |
||||
``` |
@ -1,25 +0,0 @@
|
||||
|
||||
|
||||
Please provide dev environment versions of your system |
||||
|
||||
node -v |
||||
( xmysql requires node >= 7.6.0 ) |
||||
|
||||
npm -v |
||||
mysql --version |
||||
xmysql --version |
||||
|
||||
- - - |
||||
|
||||
Provide steps to reproduce this issue |
||||
or |
||||
Provide minimum code to reproduce this issue |
||||
or |
||||
HTTP request,response,error snapshot of this issue |
||||
|
||||
- - - |
||||
|
||||
|
||||
Mark issue with suitable label if it is clear what this issue is. |
||||
|
||||
|
@ -1,21 +1,661 @@
|
||||
MIT License |
||||
|
||||
Copyright (c) 2017 o1lab |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
of this software and associated documentation files (the "Software"), to deal |
||||
in the Software without restriction, including without limitation the rights |
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
copies of the Software, and to permit persons to whom the Software is |
||||
furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included in all |
||||
copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
SOFTWARE. |
||||
GNU AFFERO GENERAL PUBLIC LICENSE |
||||
Version 3, 19 November 2007 |
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> |
||||
Everyone is permitted to copy and distribute verbatim copies |
||||
of this license document, but changing it is not allowed. |
||||
|
||||
Preamble |
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for |
||||
software and other kinds of works, specifically designed to ensure |
||||
cooperation with the community in the case of network server software. |
||||
|
||||
The licenses for most software and other practical works are designed |
||||
to take away your freedom to share and change the works. By contrast, |
||||
our General Public Licenses are intended to guarantee your freedom to |
||||
share and change all versions of a program--to make sure it remains free |
||||
software for all its users. |
||||
|
||||
When we speak of free software, we are referring to freedom, not |
||||
price. Our General Public Licenses are designed to make sure that you |
||||
have the freedom to distribute copies of free software (and charge for |
||||
them if you wish), that you receive source code or can get it if you |
||||
want it, that you can change the software or use pieces of it in new |
||||
free programs, and that you know you can do these things. |
||||
|
||||
Developers that use our General Public Licenses protect your rights |
||||
with two steps: (1) assert copyright on the software, and (2) offer |
||||
you this License which gives you legal permission to copy, distribute |
||||
and/or modify the software. |
||||
|
||||
A secondary benefit of defending all users' freedom is that |
||||
improvements made in alternate versions of the program, if they |
||||
receive widespread use, become available for other developers to |
||||
incorporate. Many developers of free software are heartened and |
||||
encouraged by the resulting cooperation. However, in the case of |
||||
software used on network servers, this result may fail to come about. |
||||
The GNU General Public License permits making a modified version and |
||||
letting the public access it on a server without ever releasing its |
||||
source code to the public. |
||||
|
||||
The GNU Affero General Public License is designed specifically to |
||||
ensure that, in such cases, the modified source code becomes available |
||||
to the community. It requires the operator of a network server to |
||||
provide the source code of the modified version running there to the |
||||
users of that server. Therefore, public use of a modified version, on |
||||
a publicly accessible server, gives the public access to the source |
||||
code of the modified version. |
||||
|
||||
An older license, called the Affero General Public License and |
||||
published by Affero, was designed to accomplish similar goals. This is |
||||
a different license, not a version of the Affero GPL, but Affero has |
||||
released a new version of the Affero GPL which permits relicensing under |
||||
this license. |
||||
|
||||
The precise terms and conditions for copying, distribution and |
||||
modification follow. |
||||
|
||||
TERMS AND CONDITIONS |
||||
|
||||
0. Definitions. |
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License. |
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of |
||||
works, such as semiconductor masks. |
||||
|
||||
"The Program" refers to any copyrightable work licensed under this |
||||
License. Each licensee is addressed as "you". "Licensees" and |
||||
"recipients" may be individuals or organizations. |
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work |
||||
in a fashion requiring copyright permission, other than the making of an |
||||
exact copy. The resulting work is called a "modified version" of the |
||||
earlier work or a work "based on" the earlier work. |
||||
|
||||
A "covered work" means either the unmodified Program or a work based |
||||
on the Program. |
||||
|
||||
To "propagate" a work means to do anything with it that, without |
||||
permission, would make you directly or secondarily liable for |
||||
infringement under applicable copyright law, except executing it on a |
||||
computer or modifying a private copy. Propagation includes copying, |
||||
distribution (with or without modification), making available to the |
||||
public, and in some countries other activities as well. |
||||
|
||||
To "convey" a work means any kind of propagation that enables other |
||||
parties to make or receive copies. Mere interaction with a user through |
||||
a computer network, with no transfer of a copy, is not conveying. |
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices" |
||||
to the extent that it includes a convenient and prominently visible |
||||
feature that (1) displays an appropriate copyright notice, and (2) |
||||
tells the user that there is no warranty for the work (except to the |
||||
extent that warranties are provided), that licensees may convey the |
||||
work under this License, and how to view a copy of this License. If |
||||
the interface presents a list of user commands or options, such as a |
||||
menu, a prominent item in the list meets this criterion. |
||||
|
||||
1. Source Code. |
||||
|
||||
The "source code" for a work means the preferred form of the work |
||||
for making modifications to it. "Object code" means any non-source |
||||
form of a work. |
||||
|
||||
A "Standard Interface" means an interface that either is an official |
||||
standard defined by a recognized standards body, or, in the case of |
||||
interfaces specified for a particular programming language, one that |
||||
is widely used among developers working in that language. |
||||
|
||||
The "System Libraries" of an executable work include anything, other |
||||
than the work as a whole, that (a) is included in the normal form of |
||||
packaging a Major Component, but which is not part of that Major |
||||
Component, and (b) serves only to enable use of the work with that |
||||
Major Component, or to implement a Standard Interface for which an |
||||
implementation is available to the public in source code form. A |
||||
"Major Component", in this context, means a major essential component |
||||
(kernel, window system, and so on) of the specific operating system |
||||
(if any) on which the executable work runs, or a compiler used to |
||||
produce the work, or an object code interpreter used to run it. |
||||
|
||||
The "Corresponding Source" for a work in object code form means all |
||||
the source code needed to generate, install, and (for an executable |
||||
work) run the object code and to modify the work, including scripts to |
||||
control those activities. However, it does not include the work's |
||||
System Libraries, or general-purpose tools or generally available free |
||||
programs which are used unmodified in performing those activities but |
||||
which are not part of the work. For example, Corresponding Source |
||||
includes interface definition files associated with source files for |
||||
the work, and the source code for shared libraries and dynamically |
||||
linked subprograms that the work is specifically designed to require, |
||||
such as by intimate data communication or control flow between those |
||||
subprograms and other parts of the work. |
||||
|
||||
The Corresponding Source need not include anything that users |
||||
can regenerate automatically from other parts of the Corresponding |
||||
Source. |
||||
|
||||
The Corresponding Source for a work in source code form is that |
||||
same work. |
||||
|
||||
2. Basic Permissions. |
||||
|
||||
All rights granted under this License are granted for the term of |
||||
copyright on the Program, and are irrevocable provided the stated |
||||
conditions are met. This License explicitly affirms your unlimited |
||||
permission to run the unmodified Program. The output from running a |
||||
covered work is covered by this License only if the output, given its |
||||
content, constitutes a covered work. This License acknowledges your |
||||
rights of fair use or other equivalent, as provided by copyright law. |
||||
|
||||
You may make, run and propagate covered works that you do not |
||||
convey, without conditions so long as your license otherwise remains |
||||
in force. You may convey covered works to others for the sole purpose |
||||
of having them make modifications exclusively for you, or provide you |
||||
with facilities for running those works, provided that you comply with |
||||
the terms of this License in conveying all material for which you do |
||||
not control copyright. Those thus making or running the covered works |
||||
for you must do so exclusively on your behalf, under your direction |
||||
and control, on terms that prohibit them from making any copies of |
||||
your copyrighted material outside their relationship with you. |
||||
|
||||
Conveying under any other circumstances is permitted solely under |
||||
the conditions stated below. Sublicensing is not allowed; section 10 |
||||
makes it unnecessary. |
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law. |
||||
|
||||
No covered work shall be deemed part of an effective technological |
||||
measure under any applicable law fulfilling obligations under article |
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or |
||||
similar laws prohibiting or restricting circumvention of such |
||||
measures. |
||||
|
||||
When you convey a covered work, you waive any legal power to forbid |
||||
circumvention of technological measures to the extent such circumvention |
||||
is effected by exercising rights under this License with respect to |
||||
the covered work, and you disclaim any intention to limit operation or |
||||
modification of the work as a means of enforcing, against the work's |
||||
users, your or third parties' legal rights to forbid circumvention of |
||||
technological measures. |
||||
|
||||
4. Conveying Verbatim Copies. |
||||
|
||||
You may convey verbatim copies of the Program's source code as you |
||||
receive it, in any medium, provided that you conspicuously and |
||||
appropriately publish on each copy an appropriate copyright notice; |
||||
keep intact all notices stating that this License and any |
||||
non-permissive terms added in accord with section 7 apply to the code; |
||||
keep intact all notices of the absence of any warranty; and give all |
||||
recipients a copy of this License along with the Program. |
||||
|
||||
You may charge any price or no price for each copy that you convey, |
||||
and you may offer support or warranty protection for a fee. |
||||
|
||||
5. Conveying Modified Source Versions. |
||||
|
||||
You may convey a work based on the Program, or the modifications to |
||||
produce it from the Program, in the form of source code under the |
||||
terms of section 4, provided that you also meet all of these conditions: |
||||
|
||||
a) The work must carry prominent notices stating that you modified |
||||
it, and giving a relevant date. |
||||
|
||||
b) The work must carry prominent notices stating that it is |
||||
released under this License and any conditions added under section |
||||
7. This requirement modifies the requirement in section 4 to |
||||
"keep intact all notices". |
||||
|
||||
c) You must license the entire work, as a whole, under this |
||||
License to anyone who comes into possession of a copy. This |
||||
License will therefore apply, along with any applicable section 7 |
||||
additional terms, to the whole of the work, and all its parts, |
||||
regardless of how they are packaged. This License gives no |
||||
permission to license the work in any other way, but it does not |
||||
invalidate such permission if you have separately received it. |
||||
|
||||
d) If the work has interactive user interfaces, each must display |
||||
Appropriate Legal Notices; however, if the Program has interactive |
||||
interfaces that do not display Appropriate Legal Notices, your |
||||
work need not make them do so. |
||||
|
||||
A compilation of a covered work with other separate and independent |
||||
works, which are not by their nature extensions of the covered work, |
||||
and which are not combined with it such as to form a larger program, |
||||
in or on a volume of a storage or distribution medium, is called an |
||||
"aggregate" if the compilation and its resulting copyright are not |
||||
used to limit the access or legal rights of the compilation's users |
||||
beyond what the individual works permit. Inclusion of a covered work |
||||
in an aggregate does not cause this License to apply to the other |
||||
parts of the aggregate. |
||||
|
||||
6. Conveying Non-Source Forms. |
||||
|
||||
You may convey a covered work in object code form under the terms |
||||
of sections 4 and 5, provided that you also convey the |
||||
machine-readable Corresponding Source under the terms of this License, |
||||
in one of these ways: |
||||
|
||||
a) Convey the object code in, or embodied in, a physical product |
||||
(including a physical distribution medium), accompanied by the |
||||
Corresponding Source fixed on a durable physical medium |
||||
customarily used for software interchange. |
||||
|
||||
b) Convey the object code in, or embodied in, a physical product |
||||
(including a physical distribution medium), accompanied by a |
||||
written offer, valid for at least three years and valid for as |
||||
long as you offer spare parts or customer support for that product |
||||
model, to give anyone who possesses the object code either (1) a |
||||
copy of the Corresponding Source for all the software in the |
||||
product that is covered by this License, on a durable physical |
||||
medium customarily used for software interchange, for a price no |
||||
more than your reasonable cost of physically performing this |
||||
conveying of source, or (2) access to copy the |
||||
Corresponding Source from a network server at no charge. |
||||
|
||||
c) Convey individual copies of the object code with a copy of the |
||||
written offer to provide the Corresponding Source. This |
||||
alternative is allowed only occasionally and noncommercially, and |
||||
only if you received the object code with such an offer, in accord |
||||
with subsection 6b. |
||||
|
||||
d) Convey the object code by offering access from a designated |
||||
place (gratis or for a charge), and offer equivalent access to the |
||||
Corresponding Source in the same way through the same place at no |
||||
further charge. You need not require recipients to copy the |
||||
Corresponding Source along with the object code. If the place to |
||||
copy the object code is a network server, the Corresponding Source |
||||
may be on a different server (operated by you or a third party) |
||||
that supports equivalent copying facilities, provided you maintain |
||||
clear directions next to the object code saying where to find the |
||||
Corresponding Source. Regardless of what server hosts the |
||||
Corresponding Source, you remain obligated to ensure that it is |
||||
available for as long as needed to satisfy these requirements. |
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided |
||||
you inform other peers where the object code and Corresponding |
||||
Source of the work are being offered to the general public at no |
||||
charge under subsection 6d. |
||||
|
||||
A separable portion of the object code, whose source code is excluded |
||||
from the Corresponding Source as a System Library, need not be |
||||
included in conveying the object code work. |
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any |
||||
tangible personal property which is normally used for personal, family, |
||||
or household purposes, or (2) anything designed or sold for incorporation |
||||
into a dwelling. In determining whether a product is a consumer product, |
||||
doubtful cases shall be resolved in favor of coverage. For a particular |
||||
product received by a particular user, "normally used" refers to a |
||||
typical or common use of that class of product, regardless of the status |
||||
of the particular user or of the way in which the particular user |
||||
actually uses, or expects or is expected to use, the product. A product |
||||
is a consumer product regardless of whether the product has substantial |
||||
commercial, industrial or non-consumer uses, unless such uses represent |
||||
the only significant mode of use of the product. |
||||
|
||||
"Installation Information" for a User Product means any methods, |
||||
procedures, authorization keys, or other information required to install |
||||
and execute modified versions of a covered work in that User Product from |
||||
a modified version of its Corresponding Source. The information must |
||||
suffice to ensure that the continued functioning of the modified object |
||||
code is in no case prevented or interfered with solely because |
||||
modification has been made. |
||||
|
||||
If you convey an object code work under this section in, or with, or |
||||
specifically for use in, a User Product, and the conveying occurs as |
||||
part of a transaction in which the right of possession and use of the |
||||
User Product is transferred to the recipient in perpetuity or for a |
||||
fixed term (regardless of how the transaction is characterized), the |
||||
Corresponding Source conveyed under this section must be accompanied |
||||
by the Installation Information. But this requirement does not apply |
||||
if neither you nor any third party retains the ability to install |
||||
modified object code on the User Product (for example, the work has |
||||
been installed in ROM). |
||||
|
||||
The requirement to provide Installation Information does not include a |
||||
requirement to continue to provide support service, warranty, or updates |
||||
for a work that has been modified or installed by the recipient, or for |
||||
the User Product in which it has been modified or installed. Access to a |
||||
network may be denied when the modification itself materially and |
||||
adversely affects the operation of the network or violates the rules and |
||||
protocols for communication across the network. |
||||
|
||||
Corresponding Source conveyed, and Installation Information provided, |
||||
in accord with this section must be in a format that is publicly |
||||
documented (and with an implementation available to the public in |
||||
source code form), and must require no special password or key for |
||||
unpacking, reading or copying. |
||||
|
||||
7. Additional Terms. |
||||
|
||||
"Additional permissions" are terms that supplement the terms of this |
||||
License by making exceptions from one or more of its conditions. |
||||
Additional permissions that are applicable to the entire Program shall |
||||
be treated as though they were included in this License, to the extent |
||||
that they are valid under applicable law. If additional permissions |
||||
apply only to part of the Program, that part may be used separately |
||||
under those permissions, but the entire Program remains governed by |
||||
this License without regard to the additional permissions. |
||||
|
||||
When you convey a copy of a covered work, you may at your option |
||||
remove any additional permissions from that copy, or from any part of |
||||
it. (Additional permissions may be written to require their own |
||||
removal in certain cases when you modify the work.) You may place |
||||
additional permissions on material, added by you to a covered work, |
||||
for which you have or can give appropriate copyright permission. |
||||
|
||||
Notwithstanding any other provision of this License, for material you |
||||
add to a covered work, you may (if authorized by the copyright holders of |
||||
that material) supplement the terms of this License with terms: |
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the |
||||
terms of sections 15 and 16 of this License; or |
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or |
||||
author attributions in that material or in the Appropriate Legal |
||||
Notices displayed by works containing it; or |
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or |
||||
requiring that modified versions of such material be marked in |
||||
reasonable ways as different from the original version; or |
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or |
||||
authors of the material; or |
||||
|
||||
e) Declining to grant rights under trademark law for use of some |
||||
trade names, trademarks, or service marks; or |
||||
|
||||
f) Requiring indemnification of licensors and authors of that |
||||
material by anyone who conveys the material (or modified versions of |
||||
it) with contractual assumptions of liability to the recipient, for |
||||
any liability that these contractual assumptions directly impose on |
||||
those licensors and authors. |
||||
|
||||
All other non-permissive additional terms are considered "further |
||||
restrictions" within the meaning of section 10. If the Program as you |
||||
received it, or any part of it, contains a notice stating that it is |
||||
governed by this License along with a term that is a further |
||||
restriction, you may remove that term. If a license document contains |
||||
a further restriction but permits relicensing or conveying under this |
||||
License, you may add to a covered work material governed by the terms |
||||
of that license document, provided that the further restriction does |
||||
not survive such relicensing or conveying. |
||||
|
||||
If you add terms to a covered work in accord with this section, you |
||||
must place, in the relevant source files, a statement of the |
||||
additional terms that apply to those files, or a notice indicating |
||||
where to find the applicable terms. |
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the |
||||
form of a separately written license, or stated as exceptions; |
||||
the above requirements apply either way. |
||||
|
||||
8. Termination. |
||||
|
||||
You may not propagate or modify a covered work except as expressly |
||||
provided under this License. Any attempt otherwise to propagate or |
||||
modify it is void, and will automatically terminate your rights under |
||||
this License (including any patent licenses granted under the third |
||||
paragraph of section 11). |
||||
|
||||
However, if you cease all violation of this License, then your |
||||
license from a particular copyright holder is reinstated (a) |
||||
provisionally, unless and until the copyright holder explicitly and |
||||
finally terminates your license, and (b) permanently, if the copyright |
||||
holder fails to notify you of the violation by some reasonable means |
||||
prior to 60 days after the cessation. |
||||
|
||||
Moreover, your license from a particular copyright holder is |
||||
reinstated permanently if the copyright holder notifies you of the |
||||
violation by some reasonable means, this is the first time you have |
||||
received notice of violation of this License (for any work) from that |
||||
copyright holder, and you cure the violation prior to 30 days after |
||||
your receipt of the notice. |
||||
|
||||
Termination of your rights under this section does not terminate the |
||||
licenses of parties who have received copies or rights from you under |
||||
this License. If your rights have been terminated and not permanently |
||||
reinstated, you do not qualify to receive new licenses for the same |
||||
material under section 10. |
||||
|
||||
9. Acceptance Not Required for Having Copies. |
||||
|
||||
You are not required to accept this License in order to receive or |
||||
run a copy of the Program. Ancillary propagation of a covered work |
||||
occurring solely as a consequence of using peer-to-peer transmission |
||||
to receive a copy likewise does not require acceptance. However, |
||||
nothing other than this License grants you permission to propagate or |
||||
modify any covered work. These actions infringe copyright if you do |
||||
not accept this License. Therefore, by modifying or propagating a |
||||
covered work, you indicate your acceptance of this License to do so. |
||||
|
||||
10. Automatic Licensing of Downstream Recipients. |
||||
|
||||
Each time you convey a covered work, the recipient automatically |
||||
receives a license from the original licensors, to run, modify and |
||||
propagate that work, subject to this License. You are not responsible |
||||
for enforcing compliance by third parties with this License. |
||||
|
||||
An "entity transaction" is a transaction transferring control of an |
||||
organization, or substantially all assets of one, or subdividing an |
||||
organization, or merging organizations. If propagation of a covered |
||||
work results from an entity transaction, each party to that |
||||
transaction who receives a copy of the work also receives whatever |
||||
licenses to the work the party's predecessor in interest had or could |
||||
give under the previous paragraph, plus a right to possession of the |
||||
Corresponding Source of the work from the predecessor in interest, if |
||||
the predecessor has it or can get it with reasonable efforts. |
||||
|
||||
You may not impose any further restrictions on the exercise of the |
||||
rights granted or affirmed under this License. For example, you may |
||||
not impose a license fee, royalty, or other charge for exercise of |
||||
rights granted under this License, and you may not initiate litigation |
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that |
||||
any patent claim is infringed by making, using, selling, offering for |
||||
sale, or importing the Program or any portion of it. |
||||
|
||||
11. Patents. |
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this |
||||
License of the Program or a work on which the Program is based. The |
||||
work thus licensed is called the contributor's "contributor version". |
||||
|
||||
A contributor's "essential patent claims" are all patent claims |
||||
owned or controlled by the contributor, whether already acquired or |
||||
hereafter acquired, that would be infringed by some manner, permitted |
||||
by this License, of making, using, or selling its contributor version, |
||||
but do not include claims that would be infringed only as a |
||||
consequence of further modification of the contributor version. For |
||||
purposes of this definition, "control" includes the right to grant |
||||
patent sublicenses in a manner consistent with the requirements of |
||||
this License. |
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free |
||||
patent license under the contributor's essential patent claims, to |
||||
make, use, sell, offer for sale, import and otherwise run, modify and |
||||
propagate the contents of its contributor version. |
||||
|
||||
In the following three paragraphs, a "patent license" is any express |
||||
agreement or commitment, however denominated, not to enforce a patent |
||||
(such as an express permission to practice a patent or covenant not to |
||||
sue for patent infringement). To "grant" such a patent license to a |
||||
party means to make such an agreement or commitment not to enforce a |
||||
patent against the party. |
||||
|
||||
If you convey a covered work, knowingly relying on a patent license, |
||||
and the Corresponding Source of the work is not available for anyone |
||||
to copy, free of charge and under the terms of this License, through a |
||||
publicly available network server or other readily accessible means, |
||||
then you must either (1) cause the Corresponding Source to be so |
||||
available, or (2) arrange to deprive yourself of the benefit of the |
||||
patent license for this particular work, or (3) arrange, in a manner |
||||
consistent with the requirements of this License, to extend the patent |
||||
license to downstream recipients. "Knowingly relying" means you have |
||||
actual knowledge that, but for the patent license, your conveying the |
||||
covered work in a country, or your recipient's use of the covered work |
||||
in a country, would infringe one or more identifiable patents in that |
||||
country that you have reason to believe are valid. |
||||
|
||||
If, pursuant to or in connection with a single transaction or |
||||
arrangement, you convey, or propagate by procuring conveyance of, a |
||||
covered work, and grant a patent license to some of the parties |
||||
receiving the covered work authorizing them to use, propagate, modify |
||||
or convey a specific copy of the covered work, then the patent license |
||||
you grant is automatically extended to all recipients of the covered |
||||
work and works based on it. |
||||
|
||||
A patent license is "discriminatory" if it does not include within |
||||
the scope of its coverage, prohibits the exercise of, or is |
||||
conditioned on the non-exercise of one or more of the rights that are |
||||
specifically granted under this License. You may not convey a covered |
||||
work if you are a party to an arrangement with a third party that is |
||||
in the business of distributing software, under which you make payment |
||||
to the third party based on the extent of your activity of conveying |
||||
the work, and under which the third party grants, to any of the |
||||
parties who would receive the covered work from you, a discriminatory |
||||
patent license (a) in connection with copies of the covered work |
||||
conveyed by you (or copies made from those copies), or (b) primarily |
||||
for and in connection with specific products or compilations that |
||||
contain the covered work, unless you entered into that arrangement, |
||||
or that patent license was granted, prior to 28 March 2007. |
||||
|
||||
Nothing in this License shall be construed as excluding or limiting |
||||
any implied license or other defenses to infringement that may |
||||
otherwise be available to you under applicable patent law. |
||||
|
||||
12. No Surrender of Others' Freedom. |
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or |
||||
otherwise) that contradict the conditions of this License, they do not |
||||
excuse you from the conditions of this License. If you cannot convey a |
||||
covered work so as to satisfy simultaneously your obligations under this |
||||
License and any other pertinent obligations, then as a consequence you may |
||||
not convey it at all. For example, if you agree to terms that obligate you |
||||
to collect a royalty for further conveying from those to whom you convey |
||||
the Program, the only way you could satisfy both those terms and this |
||||
License would be to refrain entirely from conveying the Program. |
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License. |
||||
|
||||
Notwithstanding any other provision of this License, if you modify the |
||||
Program, your modified version must prominently offer all users |
||||
interacting with it remotely through a computer network (if your version |
||||
supports such interaction) an opportunity to receive the Corresponding |
||||
Source of your version by providing access to the Corresponding Source |
||||
from a network server at no charge, through some standard or customary |
||||
means of facilitating copying of software. This Corresponding Source |
||||
shall include the Corresponding Source for any work covered by version 3 |
||||
of the GNU General Public License that is incorporated pursuant to the |
||||
following paragraph. |
||||
|
||||
Notwithstanding any other provision of this License, you have |
||||
permission to link or combine any covered work with a work licensed |
||||
under version 3 of the GNU General Public License into a single |
||||
combined work, and to convey the resulting work. The terms of this |
||||
License will continue to apply to the part which is the covered work, |
||||
but the work with which it is combined will remain governed by version |
||||
3 of the GNU General Public License. |
||||
|
||||
14. Revised Versions of this License. |
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of |
||||
the GNU Affero General Public License from time to time. Such new versions |
||||
will be similar in spirit to the present version, but may differ in detail to |
||||
address new problems or concerns. |
||||
|
||||
Each version is given a distinguishing version number. If the |
||||
Program specifies that a certain numbered version of the GNU Affero General |
||||
Public License "or any later version" applies to it, you have the |
||||
option of following the terms and conditions either of that numbered |
||||
version or of any later version published by the Free Software |
||||
Foundation. If the Program does not specify a version number of the |
||||
GNU Affero General Public License, you may choose any version ever published |
||||
by the Free Software Foundation. |
||||
|
||||
If the Program specifies that a proxy can decide which future |
||||
versions of the GNU Affero General Public License can be used, that proxy's |
||||
public statement of acceptance of a version permanently authorizes you |
||||
to choose that version for the Program. |
||||
|
||||
Later license versions may give you additional or different |
||||
permissions. However, no additional obligations are imposed on any |
||||
author or copyright holder as a result of your choosing to follow a |
||||
later version. |
||||
|
||||
15. Disclaimer of Warranty. |
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY |
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT |
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY |
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, |
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM |
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF |
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION. |
||||
|
||||
16. Limitation of Liability. |
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING |
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS |
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY |
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE |
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF |
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD |
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), |
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF |
||||
SUCH DAMAGES. |
||||
|
||||
17. Interpretation of Sections 15 and 16. |
||||
|
||||
If the disclaimer of warranty and limitation of liability provided |
||||
above cannot be given local legal effect according to their terms, |
||||
reviewing courts shall apply local law that most closely approximates |
||||
an absolute waiver of all civil liability in connection with the |
||||
Program, unless a warranty or assumption of liability accompanies a |
||||
copy of the Program in return for a fee. |
||||
|
||||
END OF TERMS AND CONDITIONS |
||||
|
||||
How to Apply These Terms to Your New Programs |
||||
|
||||
If you develop a new program, and you want it to be of the greatest |
||||
possible use to the public, the best way to achieve this is to make it |
||||
free software which everyone can redistribute and change under these terms. |
||||
|
||||
To do so, attach the following notices to the program. It is safest |
||||
to attach them to the start of each source file to most effectively |
||||
state the exclusion of warranty; and each file should have at least |
||||
the "copyright" line and a pointer to where the full notice is found. |
||||
|
||||
<one line to give the program's name and a brief idea of what it does.> |
||||
Copyright (C) <year> <name of author> |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU Affero General Public License as published |
||||
by the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU Affero General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU Affero General Public License |
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
|
||||
Also add information on how to contact you by electronic and paper mail. |
||||
|
||||
If your software can interact with users remotely through a computer |
||||
network, you should also make sure that it provides a way for users to |
||||
get its source. For example, if your program is a web application, its |
||||
interface could display a "Source" link that leads users to an archive |
||||
of the code. There are many ways you could offer source, and different |
||||
solutions will be better for different programs; see section 13 for the |
||||
specific requirements. |
||||
|
||||
You should also get your employer (if you work as a programmer) or school, |
||||
if any, to sign a "copyright disclaimer" for the program, if necessary. |
||||
For more information on this, and how to apply and follow the GNU AGPL, see |
||||
<http://www.gnu.org/licenses/>. |
@ -0,0 +1,49 @@
|
||||
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node |
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions |
||||
|
||||
name: Node.js CI |
||||
|
||||
on: |
||||
push: |
||||
branches: [ main ] |
||||
pull_request: |
||||
branches: [ main ] |
||||
|
||||
jobs: |
||||
cypress-run: |
||||
runs-on: ubuntu-20.04 |
||||
steps: |
||||
- name: Checkout |
||||
uses: actions/checkout@v2 |
||||
- name: Cypress run |
||||
uses: cypress-io/github-action@v2 |
||||
with: |
||||
start: docker run -p 8080:8080 -p 8081:8081 -p 8082:8082 -d --name xc-instant --env nocodb/nocodb:latest ; sleep 10 |
||||
wait-on: 'http://localhost:8080/dashboard' |
||||
docker: |
||||
|
||||
runs-on: ubuntu-latest |
||||
|
||||
|
||||
steps: |
||||
- uses: actions/checkout@v2 |
||||
|
||||
- name: Test Mysql REST APIs |
||||
run: cd ./packages/nocodb/ && docker-compose run xc-test-mysql |
||||
- name: Test Mysql GraphQL APIs |
||||
run: cd ./packages/nocodb/ && docker-compose run xc-test-gql-mysql |
||||
|
||||
# - name: Test MSSQL REST APIs |
||||
# run: cd ./packages/nocodb/ && docker-compose run xc-test-mssql |
||||
# - name: Test MSSQL GraphQL APIs |
||||
# run: cd ./packages/nocodb/ && docker-compose run xc-test-gql-mssql |
||||
# |
||||
# - name: Test PostgreSQL REST APIs |
||||
# run: cd ./packages/nocodb/ && docker-compose run xc-test-pg |
||||
# - name: Test PostgreSQL GraphQL APIs |
||||
# run: cd ./packages/nocodb/ && docker-compose run xc-test-gql-pg |
||||
# |
||||
# - name: Test SQLite3 REST APIs |
||||
# run: cd ./packages/nocodb/ && docker-compose run xc-test-sqlite |
||||
# - name: Test SQLite3 GraphQL APIs |
||||
# run: cd ./packages/nocodb/ && docker-compose run xc-test-gql-sqlite |
Before Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 7.6 KiB |
Before Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 424 KiB |
Before Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 11 KiB |
@ -1,93 +0,0 @@
|
||||
#! /usr/bin/env node
|
||||
|
||||
const morgan = require("morgan"); |
||||
const bodyParser = require("body-parser"); |
||||
const express = require("express"); |
||||
const sqlConfig = require("commander"); |
||||
const mysql = require("mysql"); |
||||
const cors = require("cors"); |
||||
const dataHelp = require("../lib/util/data.helper.js"); |
||||
const Xapi = require("../lib/xapi.js"); |
||||
const cmdargs = require("../lib/util/cmd.helper.js"); |
||||
const cluster = require("cluster"); |
||||
const numCPUs = require("os").cpus().length; |
||||
|
||||
function startXmysql(sqlConfig) { |
||||
/**************** START : setup express ****************/ |
||||
let app = express(); |
||||
app.use(morgan("tiny")); |
||||
app.use(cors()); |
||||
app.use(bodyParser.json()); |
||||
app.use( |
||||
bodyParser.urlencoded({ |
||||
extended: true |
||||
}) |
||||
); |
||||
/**************** END : setup express ****************/ |
||||
|
||||
/**************** START : setup mysql ****************/ |
||||
let mysqlPool = mysql.createPool(sqlConfig); |
||||
/**************** END : setup mysql ****************/ |
||||
|
||||
/**************** START : setup Xapi ****************/ |
||||
console.log(""); |
||||
console.log(""); |
||||
console.log(""); |
||||
console.log(" Generating REST APIs at the speed of your thought.. "); |
||||
console.log(""); |
||||
|
||||
let t = process.hrtime(); |
||||
let moreApis = new Xapi(sqlConfig, mysqlPool, app); |
||||
|
||||
moreApis.init((err, results) => { |
||||
app.listen(sqlConfig.portNumber, sqlConfig.ipAddress); |
||||
var t1 = process.hrtime(t); |
||||
var t2 = t1[0] + t1[1] / 1000000000; |
||||
|
||||
console.log( |
||||
" Xmysql took : %d seconds", |
||||
dataHelp.round(t2, 1) |
||||
); |
||||
console.log( |
||||
" API's base URL : " + |
||||
"localhost:" + |
||||
sqlConfig.portNumber |
||||
); |
||||
console.log(" "); |
||||
console.log( |
||||
" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - " |
||||
); |
||||
}); |
||||
/**************** END : setup Xapi ****************/ |
||||
} |
||||
|
||||
function start(sqlConfig) { |
||||
//handle cmd line arguments
|
||||
cmdargs.handle(sqlConfig); |
||||
|
||||
if (cluster.isMaster && sqlConfig.useCpuCores > 1) { |
||||
console.log(`Master ${process.pid} is running`); |
||||
|
||||
for (let i = 0; i < numCPUs && i < sqlConfig.useCpuCores; i++) { |
||||
console.log(`Forking process number ${i}...`); |
||||
cluster.fork(); |
||||
} |
||||
|
||||
cluster.on("exit", function(worker, code, signal) { |
||||
console.log( |
||||
"Worker " + |
||||
worker.process.pid + |
||||
" died with code: " + |
||||
code + |
||||
", and signal: " + |
||||
signal |
||||
); |
||||
console.log("Starting a new worker"); |
||||
cluster.fork(); |
||||
}); |
||||
} else { |
||||
startXmysql(sqlConfig); |
||||
} |
||||
} |
||||
|
||||
start(sqlConfig); |
@ -0,0 +1,52 @@
|
||||
# How to apply a license |
||||
NocoDB doesn't require a CLA (Contributor License Agreement). |
||||
We require Developer Certificate of Origin (DCO) as an additional safeguard |
||||
for the NocoDB project. This is a well established and widely used |
||||
mechanism to assure contributors have confirmed their right to license |
||||
their contribution under the project's license. |
||||
|
||||
## Modifying existing file |
||||
If you modify an existing file, please keep the existing license header as |
||||
it is and just add your copyright notice and author: |
||||
|
||||
```` |
||||
@author <your name> <your email address> |
||||
```` |
||||
|
||||
## Creating new file |
||||
```` |
||||
/** |
||||
* @copyright Copyright (c) <year>, <your name> (<your email address>) |
||||
* |
||||
* @author <your name> <your email address> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* 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/>. |
||||
* |
||||
*/ |
||||
```` |
||||
|
||||
|
||||
## How to sign your work |
||||
```` |
||||
Signed-off-by: FirstName Initials/Lastname <email@provider.com> |
||||
```` |
||||
|
||||
Use your real name (sorry, no pseudonyms or anonymous contributions). |
||||
If you set your `user.name` and `user.email` git configs, you can sign your |
||||
commit automatically with `git commit -s`. |
||||
You can also use git [aliases](https://git-scm.com/book/tr/v2/Git-Basics-Git-Aliases) |
||||
like `git config --global alias.ci 'commit -s'`. Now you can commit with |
||||
`git ci` and the commit will be signed. |
@ -0,0 +1,35 @@
|
||||
Developer Certificate of Origin |
||||
Version 1.1 |
||||
|
||||
Copyright (C) 2004, 2006 The Linux Foundation and its contributors. |
||||
660 York Street, Suite 102, |
||||
San Francisco, CA 94110 USA |
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this |
||||
license document, but changing it is not allowed. |
||||
|
||||
Developer's Certificate of Origin 1.1 |
||||
|
||||
By making a contribution to this project, I certify that: |
||||
|
||||
(a) The contribution was created in whole or in part by me and I |
||||
have the right to submit it under the open source license |
||||
indicated in the file; or |
||||
|
||||
(b) The contribution is based upon previous work that, to the best |
||||
of my knowledge, is covered under an appropriate open source |
||||
license and I have the right under that license to submit that |
||||
work with modifications, whether created in whole or in part |
||||
by me, under the same open source license (unless I am |
||||
permitted to submit under a different license), as indicated |
||||
in the file; or |
||||
|
||||
(c) The contribution was provided directly to me by some other |
||||
person who certified (a), (b) or (c) and I have not modified |
||||
it. |
||||
|
||||
(d) I understand and agree that this project and the contribution |
||||
are public and that a record of the contribution (including all |
||||
personal information I submit with it, including my sign-off) is |
||||
maintained indefinitely and may be redistributed consistent with |
||||
this project or the open source license(s) involved. |
@ -0,0 +1,5 @@
|
||||
{ |
||||
"name": "Using fixtures to represent data", |
||||
"email": "hello@cypress.io", |
||||
"body": "Fixtures are a great way to mock data for responses to routes" |
||||
} |
@ -0,0 +1,38 @@
|
||||
describe('My First Test', () => { |
||||
|
||||
it('Sign Up / Sign In', (done) => { |
||||
cy.visit('http://localhost:8080/dashboard') |
||||
cy.get('body').then($body => { |
||||
|
||||
if ($body.find('.welcome-msg').length > 0) { |
||||
cy.wait(12000); |
||||
cy.get('body').trigger('mousemove'); |
||||
cy.wait(1000); |
||||
cy.contains('Let\'s Begin').click(); |
||||
cy.wait(1000); |
||||
cy.get('input[type="text"]').type('pranavc@gmail.com'); |
||||
cy.get('input[type="password"]').type('Password123.'); |
||||
cy.contains('Sign Up').click() |
||||
} else { |
||||
cy.get('input[type="text"]').type('pranavc@gmail.com'); |
||||
cy.get('input[type="password"]').type('Password123.'); |
||||
cy.contains('Sign In').click() |
||||
} |
||||
done() |
||||
}); |
||||
|
||||
}); |
||||
|
||||
it('Create Project', async () => { |
||||
cy.wait(1000) |
||||
cy.contains('New Project').trigger('onmouseover').trigger('mouseenter'); |
||||
cy.wait(1500) |
||||
cy.get('.create-external-db-project').click() |
||||
cy.wait(5500) |
||||
cy.get('.database-field input').click().clear().type('sakila') |
||||
cy.contains('Test Database Connection').click() |
||||
|
||||
cy.wait(1500); |
||||
cy.contains('Ok & Save Project').click() |
||||
}); |
||||
}) |
@ -0,0 +1,22 @@
|
||||
/// <reference types="cypress" />
|
||||
// ***********************************************************
|
||||
// This example plugins/index.js can be used to load plugins
|
||||
//
|
||||
// You can change the location of this file or turn off loading
|
||||
// the plugins file with the 'pluginsFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/plugins-guide
|
||||
// ***********************************************************
|
||||
|
||||
// This function is called when a project is opened or re-opened (e.g. due to
|
||||
// the project's config changing)
|
||||
|
||||
/** |
||||
* @type {Cypress.PluginConfig} |
||||
*/ |
||||
// eslint-disable-next-line no-unused-vars
|
||||
module.exports = (on, config) => { |
||||
// `on` is used to hook into various events Cypress emits
|
||||
// `config` is the resolved Cypress config
|
||||
} |
@ -0,0 +1,25 @@
|
||||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add('login', (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
@ -0,0 +1,20 @@
|
||||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands' |
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
@ -1,20 +0,0 @@
|
||||
FROM frolvlad/alpine-python2 |
||||
|
||||
RUN apk --update --no-cache add \ |
||||
g++ \ |
||||
make \ |
||||
nodejs \ |
||||
nodejs-npm \ |
||||
paxctl \ |
||||
&& paxctl -cm $(which node) |
||||
|
||||
WORKDIR /usr/src/app |
||||
|
||||
COPY package.json . |
||||
RUN npm install |
||||
|
||||
COPY . . |
||||
|
||||
ENTRYPOINT ["./docker-entrypoint.sh"] |
||||
|
||||
CMD ["sh", "-c", "node index.js -h $DATABASE_HOST -p $DATABASE_PASSWORD -d $DATABASE_NAME -u $DATABASE_USER -n 80 -r 0.0.0.0"] |
@ -1,41 +0,0 @@
|
||||
version: '3.5' |
||||
|
||||
services: |
||||
db: |
||||
image: mysql:5.7 |
||||
restart: always |
||||
command: --init-file /docker-entrypoint-initdb.d/sample.sql |
||||
environment: |
||||
MYSQL_ROOT_PASSWORD: 'pass' |
||||
MYSQL_USER: 'root' |
||||
MYSQL_PASSWORD: 'pass' |
||||
MYSQL_DATABASE: 'classicmodels' |
||||
volumes: |
||||
- ./tests/docker-sample.sql:/docker-entrypoint-initdb.d/sample.sql |
||||
- db-data:/var/lib/mysql |
||||
ports: |
||||
- "3306:3306" |
||||
|
||||
db-api: &db-api |
||||
build: |
||||
context: ./ |
||||
dockerfile: ./dev.Dockerfile |
||||
environment: |
||||
DATABASE_HOST: 'db' |
||||
DATABASE_USER: 'root' |
||||
DATABASE_PASSWORD: 'pass' |
||||
DATABASE_NAME: 'classicmodels' |
||||
ports: |
||||
- "3002:80" |
||||
depends_on: |
||||
- db |
||||
|
||||
test: |
||||
<<: *db-api |
||||
command: sh -c "npm test" |
||||
volumes: |
||||
- ./tests:/usr/src/app/tests |
||||
- ./lib:/usr/src/app/lib |
||||
|
||||
volumes: |
||||
db-data: |
@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env bash |
||||
|
||||
|
||||
read -p "Enter your domain name: " domain |
||||
read -p "Enter your email id: " email |
||||
|
||||
# Docker installation |
||||
if [ -x "$(command -v docker)" ]; then |
||||
echo "Docker already available" |
||||
else |
||||
sudo apt-get update |
||||
sudo apt-get install -y apt-transport-https ca-certificates curl gnupg2 software-properties-common |
||||
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -- |
||||
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian buster stable" |
||||
sudo apt-get update |
||||
sudo apt-get install -y docker-ce docker-ce-cli containerd.io |
||||
sudo usermod -a -G docker $USER |
||||
echo "Docker installed successfully" |
||||
fi |
||||
|
||||
# Docker compose installation |
||||
if [ -x "$(command -v docker-compose)" ]; then |
||||
echo "Docker-compose already available" |
||||
else |
||||
sudo apt-get -y install wget |
||||
sudo wget https://github.com/docker/compose/releases/download/1.26.2/docker-compose-$(uname -s)-$(uname -m) -O /usr/local/bin/docker-compose |
||||
sudo chmod +x /usr/local/bin/docker-compose |
||||
docker-compose --version |
||||
echo "Docker-compose installed successfully" |
||||
fi |
||||
|
||||
|
||||
#wget https://github.com/evertramos/docker-compose-letsencrypt-nginx-proxy-companion/archive/master.zip -O master.zip |
||||
# |
||||
#unzip -n master.zip |
||||
# |
||||
#cd docker-compose-letsencrypt-nginx-proxy-companion-master |
||||
|
||||
git clone https://github.com/evertramos/docker-compose-letsencrypt-nginx-proxy-companion.git |
||||
|
||||
cd docker-compose-letsencrypt-nginx-proxy-companion |
||||
|
||||
OUTPUT1=$(./start.sh) |
||||
|
||||
|
||||
|
||||
|
||||
docker run -p 8080:8080 -p 8081:8081 -p 8082:8082 -d --name xc-instant \ |
||||
-e VIRTUAL_HOST="$domain" \ |
||||
-e LETSENCRYPT_HOST="$domain" \ |
||||
-e LETSENCRYPT_EMAIL="$email" \ |
||||
-e VIRTUAL_PORT=8080 \ |
||||
--network=webproxy nocodb/nocodb:latest |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,33 @@
|
||||
version: '3.3' |
||||
|
||||
services: |
||||
xcdb: |
||||
image: mcr.microsoft.com/mssql/server:2017-latest |
||||
restart: always |
||||
volumes: |
||||
- db_data:/var/opt/mssql |
||||
environment: |
||||
SA_PASSWORD: Password123. |
||||
ACCEPT_EULA: Y |
||||
healthcheck: |
||||
test: /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P "$$SA_PASSWORD" -Q "SELECT 1" || exit 1 |
||||
interval: 10s |
||||
timeout: 3s |
||||
retries: 10 |
||||
start_period: 10s |
||||
|
||||
xc: |
||||
depends_on: |
||||
xcdb: |
||||
condition: service_healthy |
||||
image: nocodb/nocodb:latest |
||||
ports: |
||||
- "8080:8080" |
||||
- "8081:8081" |
||||
- "8082:8082" |
||||
- "8083:8083" |
||||
restart: always |
||||
environment: |
||||
NC_DB: "mssql://xcdb:1433?u=sa&p=Password123.&d=xcdb" |
||||
volumes: |
||||
db_data: {} |
@ -0,0 +1,33 @@
|
||||
version: '3.3' |
||||
|
||||
services: |
||||
xcdb: |
||||
image: mysql:5.7 |
||||
volumes: |
||||
- db_data:/var/lib/mysql |
||||
restart: always |
||||
environment: |
||||
MYSQL_ROOT_PASSWORD: password |
||||
MYSQL_DATABASE: xcdb |
||||
MYSQL_USER: xc |
||||
MYSQL_PASSWORD: password |
||||
healthcheck: |
||||
test: [ "CMD", "mysqladmin" ,"ping", "-h", "localhost" ] |
||||
timeout: 20s |
||||
retries: 10 |
||||
|
||||
xc: |
||||
depends_on: |
||||
xcdb: |
||||
condition: service_healthy |
||||
image: nocodb/nocodb:latest |
||||
ports: |
||||
- "8080:8080" |
||||
- "8081:8081" |
||||
- "8082:8082" |
||||
- "8083:8083" |
||||
restart: always |
||||
environment: |
||||
NC_DB: "mysql2://xcdb:3306?u=xc&p=password&d=xcdb" |
||||
volumes: |
||||
db_data: {} |
@ -0,0 +1,32 @@
|
||||
version: '3.3' |
||||
|
||||
services: |
||||
xcdb: |
||||
image: postgres |
||||
restart: always |
||||
volumes: |
||||
- db_data:/var/lib/postgresql/data |
||||
environment: |
||||
POSTGRES_PASSWORD: password |
||||
POSTGRES_USER: postgres |
||||
POSTGRES_DB: xcdb |
||||
healthcheck: |
||||
test: pg_isready -U "$$POSTGRES_USER" -d "$$POSTGRES_DB" |
||||
interval: 10s |
||||
timeout: 2s |
||||
retries: 10 |
||||
xc: |
||||
depends_on: |
||||
xcdb: |
||||
condition: service_healthy |
||||
image: nocodb/nocodb:latest |
||||
ports: |
||||
- "8080:8080" |
||||
- "8081:8081" |
||||
- "8082:8082" |
||||
- "8083:8083" |
||||
restart: always |
||||
environment: |
||||
NC_DB: "pg://xcdb:5432?u=postgres&p=password&d=xcdb" |
||||
volumes: |
||||
db_data: {} |
@ -1,15 +0,0 @@
|
||||
#!/bin/sh |
||||
# https://stackoverflow.com/questions/25503412/how-do-i-know-when-my-docker-mysql-container-is-up-and-mysql-is-ready-for-taking |
||||
|
||||
set -e |
||||
|
||||
until nc -z -v -w30 $DATABASE_HOST 3306 |
||||
do |
||||
echo "Waiting for database connection..." |
||||
# wait for 5 seconds before check again |
||||
sleep 5 |
||||
done |
||||
|
||||
echo "Mysql is up - executing command" |
||||
|
||||
exec "$@" |
@ -1,32 +0,0 @@
|
||||
FROM alpine:3.7 |
||||
|
||||
RUN apk --update --no-cache add \ |
||||
nodejs \ |
||||
nodejs-npm |
||||
|
||||
# Bug fix for segfault ( Convert PT_GNU_STACK program header into PT_PAX_FLAGS ) |
||||
RUN apk --update --no-cache add paxctl \ |
||||
&& paxctl -cm $(which node) |
||||
|
||||
RUN mkdir -p /usr/src/{app,bin,lib} |
||||
WORKDIR /usr/src/app |
||||
|
||||
# only install production deps to keep image small |
||||
COPY package.json /usr/src/app |
||||
RUN npm install --production |
||||
|
||||
RUN apk del nodejs-npm |
||||
|
||||
COPY index.js /usr/src/app |
||||
COPY bin/ /usr/src/app/bin |
||||
COPY lib/ /usr/src/app/lib |
||||
COPY docker-entrypoint.sh /docker-entrypoint.sh |
||||
|
||||
# env |
||||
ENV DATABASE_HOST 127.0.0.1 |
||||
ENV DATABASE_USER root |
||||
ENV DATABASE_PASSWORD password |
||||
ENV DATABASE_NAME sakila |
||||
|
||||
EXPOSE 80 |
||||
ENTRYPOINT ["/docker-entrypoint.sh"] |
@ -1,82 +0,0 @@
|
||||
/** |
||||
* I was able to get xmysql to work on AWS lambda. This is just some docs on the process to help others. |
||||
* AWS setup notes: |
||||
* * The deploy uses serverless: https://serverless.com/framework/docs/getting-started/
|
||||
* * The lambda function is accessed through API Gateway and access the RDS (mysql) database |
||||
* ** Make sure you deploy your API Gateway setup otherwise it won't be accessible. |
||||
* * The security setup: |
||||
* ** You need to put your lambda in a VPC |
||||
* ** The lambda role needs access to the VPC |
||||
* ** The RDS server has to be inside the VPC as well |
||||
* |
||||
* Performance: |
||||
* * Requests to the API Gateway resolve in ~ 90ms |
||||
* |
||||
* Extra packages needed: |
||||
* * serverless-http |
||||
* |
||||
* More info on deploying this to lambda: |
||||
* * https://serverless.com/blog/serverless-express-rest-api/
|
||||
*/ |
||||
|
||||
"use strict"; |
||||
Object.defineProperty(exports, "__esModule", { value: true }); |
||||
var bodyParser = require("body-parser"); |
||||
var express = require("express"); |
||||
var serverless = require("serverless-http"); |
||||
var cors = require("cors"); |
||||
var mysql = require("mysql"); |
||||
var Xapi = require("./node_modules/xmysql/lib/xapi.js"); |
||||
var morgan = require("morgan"); |
||||
var app = express(); |
||||
|
||||
var onXapiInitialized = new Promise(function(resolve, reject) { |
||||
try { |
||||
// /**************** START : setup express ****************/
|
||||
app.use(morgan("tiny")); |
||||
app.use(cors()); |
||||
app.use(bodyParser.json()); |
||||
app.use( |
||||
bodyParser.urlencoded({ |
||||
extended: true |
||||
}) |
||||
); |
||||
// /**************** END : setup express ****************/
|
||||
|
||||
app.use(function(req, res, next) { |
||||
// You can add authentication here
|
||||
console.log("Received request for: " + req.url, req); |
||||
next(); |
||||
}); |
||||
|
||||
var mysqlConfig = { |
||||
host: config.mysql.host, |
||||
port: 3306, |
||||
database: config.mysql.database, |
||||
user: config.mysql.user, |
||||
password: config.mysql.password, |
||||
apiPrefix: "/", |
||||
ipAddress: "localhost", |
||||
portNumber: 3000, |
||||
ignoreTables: [], |
||||
storageFolder: __dirname |
||||
}; |
||||
|
||||
var mysqlPool = mysql.createPool(mysqlConfig); |
||||
var xapi = new Xapi(mysqlConfig, mysqlPool, app); |
||||
xapi.init(function(err, results) { |
||||
app.listen(3000); |
||||
resolve(); |
||||
}); |
||||
} catch (err) { |
||||
reject(err); |
||||
} |
||||
}); |
||||
|
||||
function handler(event, context, callback) { |
||||
onXapiInitialized.then(function() { |
||||
serverless(app)(event, context, callback); |
||||
}); |
||||
} |
||||
|
||||
exports.handler = handler; |
@ -0,0 +1,22 @@
|
||||
{ |
||||
"tags": { |
||||
"allowUnknownTags": false |
||||
}, |
||||
"source": { |
||||
"include": "./lib/SqlMigrator/lib", |
||||
"includePattern": ".js$", |
||||
"excludePattern": "(node_modules/|docs)" |
||||
}, |
||||
"plugins": ["plugins/markdown"], |
||||
"opts": { |
||||
"template": "node_modules/minami", |
||||
"encoding": "utf8", |
||||
"destination": "docs/", |
||||
"recurse": true, |
||||
"verbose": true |
||||
}, |
||||
"templates": { |
||||
"cleverLinks": false, |
||||
"monospaceLinks": false |
||||
} |
||||
} |
@ -0,0 +1,6 @@
|
||||
{ |
||||
"packages": [ |
||||
"packages/*" |
||||
], |
||||
"version": "independent" |
||||
} |
@ -1,107 +0,0 @@
|
||||
"use strict"; |
||||
const program = require("commander"); |
||||
const colors = require("colors"); |
||||
const maxCpus = require("os").cpus().length; |
||||
|
||||
program.on("--help", () => { |
||||
console.log(""); |
||||
console.log(" Examples:".blue); |
||||
console.log(""); |
||||
console.log(" $ xmysql -u username -p password -d databaseSchema".blue); |
||||
console.log(""); |
||||
}); |
||||
|
||||
program |
||||
.version(module.exports.version) |
||||
.option("-h, --host <n>", "hostname of database / localhost by default") |
||||
.option("-u, --user <n>", "username of database / root by default") |
||||
.option("-p, --password <n>", "password of database / empty by default") |
||||
.option("-d, --database <n>", "database schema name") |
||||
.option( |
||||
"-r, --ipAddress <n>", |
||||
"IP interface of your server / localhost by default" |
||||
) |
||||
.option("-n, --portNumber <n>", "port number for app / 3000 by default") |
||||
.option("-o, --port <n>", "port number for mysql / 3306 by default") |
||||
.option("-S, --socketPath <n>", "unix socket path / not used by default") |
||||
.option( |
||||
"-s, --storageFolder <n>", |
||||
"storage folder / current working dir by default / available only with local" |
||||
) |
||||
.option("-i, --ignoreTables <n>", "comma separated table names to ignore") |
||||
.option("-a, --apiPrefix <n>", 'api url prefix / "/api/" by default') |
||||
.option("-y, --readOnly", "readonly apis / false by default") |
||||
.option( |
||||
"-c, --useCpuCores <n>", |
||||
"use number of CPU cores (using cluster) / 1 by default" |
||||
) |
||||
.parse(process.argv); |
||||
|
||||
function paintHelp(txt) { |
||||
return colors.magenta(txt); //display the help text in a color
|
||||
} |
||||
|
||||
function processInvalidArguments(program) { |
||||
let err = ""; |
||||
|
||||
if (!program.password) { |
||||
err += "Error: password for database is missing\n"; |
||||
} |
||||
|
||||
if (!program.database) { |
||||
err += "Error: database name is missing\n"; |
||||
} |
||||
|
||||
if (err !== "") { |
||||
program.outputHelp(paintHelp); |
||||
console.log(err.red); |
||||
} |
||||
} |
||||
|
||||
exports.handle = program => { |
||||
/**************** START : default values ****************/ |
||||
program.ipAddress = program.ipAddress || "localhost"; |
||||
program.portNumber = program.portNumber || 3000; |
||||
program.port = program.port || 3306; |
||||
program.user = program.user || "root"; |
||||
program.password = program.password || ""; |
||||
program.host = program.host || "localhost"; |
||||
program.socketPath = program.socketPath || ""; |
||||
program.storageFolder = program.storageFolder || process.cwd(); |
||||
program.apiPrefix = program.apiPrefix || "/api/"; |
||||
program.readOnly = program.readOnly || false; |
||||
program.useCpuCores = program.useCpuCores || 1; |
||||
|
||||
if (program.useCpuCores === "0") { |
||||
program.useCpuCores = maxCpus; |
||||
} |
||||
|
||||
if (program.ignoreTables) { |
||||
let ignoreTables = program.ignoreTables.split(","); |
||||
program.ignoreTables = {}; |
||||
for (var i = 0; i < ignoreTables.length; ++i) { |
||||
program.ignoreTables[ignoreTables[i]] = ignoreTables[i]; |
||||
} |
||||
} else { |
||||
program.ignoreTables = {}; |
||||
} |
||||
|
||||
program.connectionLimit = 10; |
||||
|
||||
if ( |
||||
program.host === "localhost" || |
||||
program.host === "127.0.0.1" || |
||||
program.host === "::1" |
||||
) { |
||||
program.dynamic = 1; |
||||
} |
||||
// console.log(program);
|
||||
/**************** END : default values ****************/ |
||||
|
||||
if (program.database && program.host && program.user) { |
||||
//console.log('Starting server at:', 'http://' + program.host + ':' + program.portNumber)
|
||||
} else { |
||||
processInvalidArguments(program); |
||||
process.exit(1); |
||||
} |
||||
}; |
@ -1,211 +0,0 @@
|
||||
"use strict"; |
||||
|
||||
exports.findOrInsertObjectArrayByKey = (obj, key, array) => { |
||||
let found = 0; |
||||
let i = 0; |
||||
|
||||
for (i = 0; i < array.length; ++i) { |
||||
if (key in array[i]) { |
||||
if (obj[key] === array[i][key]) { |
||||
found = 1; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
if (!found) { |
||||
array.push(obj); |
||||
} |
||||
|
||||
return array[i]; |
||||
}; |
||||
|
||||
exports.findObjectInArrayByKey = (key, value, objArray) => { |
||||
for (let i = 0; i < objArray.length; ++i) { |
||||
if (objArray[i][key] === value) { |
||||
return objArray[i]; |
||||
} |
||||
} |
||||
|
||||
return null; |
||||
}; |
||||
|
||||
exports.round = function(number, precision) { |
||||
var factor = Math.pow(10, precision); |
||||
var tempNumber = number * factor; |
||||
var roundedTempNumber = Math.round(tempNumber); |
||||
return roundedTempNumber / factor; |
||||
}; |
||||
|
||||
exports.numberRound = (number, precision) => { |
||||
var factor = Math.pow(10, precision); |
||||
var tempNumber = number * factor; |
||||
var roundedTempNumber = Math.round(tempNumber); |
||||
return roundedTempNumber / factor; |
||||
}; |
||||
|
||||
exports.numberGetLength = number => { |
||||
var n = number; |
||||
|
||||
if (number < 0) { |
||||
n = n * -1; |
||||
} |
||||
|
||||
return n.toString().length; |
||||
}; |
||||
|
||||
exports.numberGetFixed = number => { |
||||
//console.log(number, typeof number);
|
||||
return parseInt(number.toFixed()); |
||||
}; |
||||
|
||||
exports.getStepArraySimple = function(min, max, step) { |
||||
var arr = []; |
||||
for (var i = min; i <= max; i = i + step) { |
||||
arr.push(i); |
||||
} |
||||
|
||||
return arr; |
||||
}; |
||||
|
||||
exports.getStepArray = (min, max, stddev) => { |
||||
// console.log(' = = = = = = = ');
|
||||
//console.log('original numbers', min, max, stddev);
|
||||
|
||||
min = this.numberGetFixed(min); |
||||
max = this.numberGetFixed(max); |
||||
stddev = this.numberGetFixed(stddev); |
||||
|
||||
// console.log('fixed numbers', min, max, stddev);
|
||||
|
||||
let minMinusHalf = min - stddev / 2; |
||||
let maxMinusHalf = max + stddev / 2; |
||||
|
||||
minMinusHalf = this.numberGetFixed(minMinusHalf); |
||||
maxMinusHalf = this.numberGetFixed(maxMinusHalf); |
||||
|
||||
// console.log('fixed numbers + (min,max)', min, max, stddev, '(', minMinusHalf, ',', maxMinusHalf, ')');
|
||||
// console.log('numbers length', 'min', numberGetLength(min), 'max', numberGetLength(max), 'stddev', numberGetLength(stddev));
|
||||
|
||||
let minLen = this.numberGetLength(minMinusHalf); |
||||
let maxLen = this.numberGetLength(maxMinusHalf); |
||||
let stddevLen = this.numberGetLength(stddev); |
||||
//
|
||||
// console.log('- - - -');
|
||||
// console.log('Range', 'min', numberRound(minMinusHalf, -1));
|
||||
// console.log('Range', 'max', numberRound(maxMinusHalf, -1));
|
||||
// console.log('Range', 'stddev', numberRound(stddev, -1));
|
||||
|
||||
if (minLen > 1) minMinusHalf = this.numberRound(minMinusHalf, -1); |
||||
|
||||
if (maxLen > 2) maxMinusHalf = this.numberRound(maxMinusHalf, -1); |
||||
|
||||
if (stddevLen !== 1) stddev = this.numberRound(stddev, -1); |
||||
|
||||
var arr = []; |
||||
for (var step = minMinusHalf; step < maxMinusHalf; step = step + stddev) { |
||||
arr.push(step); |
||||
} |
||||
arr.push(maxMinusHalf); |
||||
|
||||
// console.log(arr);
|
||||
|
||||
return arr; |
||||
}; |
||||
|
||||
exports.getSchemaQuery = function() { |
||||
return ( |
||||
"select c.table_name, c.column_name, c.ordinal_position,c.column_key,c.is_nullable, c.data_type, c.column_type,c.extra,c.privileges, " + |
||||
"c.column_comment,c.column_default,c.data_type,c.character_maximum_length, " + |
||||
"k.constraint_name, k.referenced_table_name, k.referenced_column_name, " + |
||||
"s.index_name,s.seq_in_index, " + |
||||
"v.table_name as isView " + |
||||
"from " + |
||||
"information_schema.columns as c " + |
||||
"left join " + |
||||
"information_schema.key_column_usage as k " + |
||||
"on " + |
||||
"c.column_name=k.column_name and " + |
||||
"c.table_schema = k.referenced_table_schema and " + |
||||
"c.table_name = k.table_name " + |
||||
"left join " + |
||||
"information_schema.statistics as s " + |
||||
"on " + |
||||
"c.column_name = s.column_name and " + |
||||
"c.table_schema = s.index_schema and " + |
||||
"c.table_name = s.table_name " + |
||||
"LEFT JOIN " + |
||||
"information_schema.VIEWS as v " + |
||||
"ON " + |
||||
"c.table_schema = v.table_schema and " + |
||||
"c.table_name = v.table_name " + |
||||
"where " + |
||||
"c.table_schema=? " + |
||||
"order by " + |
||||
"c.table_name, c.ordinal_position" |
||||
); |
||||
}; |
||||
|
||||
exports.getRoutines = function () { |
||||
return 'select routine_name from information_schema.routines' |
||||
} |
||||
|
||||
exports.getChartQuery = function() { |
||||
return "select ? as ??, count(*) as _count from ?? where ?? between ? and ? "; |
||||
}; |
||||
|
||||
exports.getDataType = function(colType, typesArr) { |
||||
// console.log(colType,typesArr);
|
||||
for (let i = 0; i < typesArr.length; ++i) { |
||||
if (colType.indexOf(typesArr[i]) !== -1) { |
||||
return 1; |
||||
} |
||||
} |
||||
return 0; |
||||
}; |
||||
|
||||
exports.getColumnType = function(column) { |
||||
let strTypes = [ |
||||
"varchar", |
||||
"text", |
||||
"char", |
||||
"tinytext", |
||||
"mediumtext", |
||||
"longtext", |
||||
"blob", |
||||
"mediumblob", |
||||
"longblob", |
||||
"tinyblob", |
||||
"binary", |
||||
"varbinary" |
||||
]; |
||||
let intTypes = ["int", "long", "smallint", "mediumint", "bigint", "tinyint"]; |
||||
let flatTypes = ["float", "double", "decimal"]; |
||||
let dateTypes = ["date", "datetime", "timestamp", "time", "year"]; |
||||
|
||||
//console.log(column);
|
||||
if (this.getDataType(column["data_type"], strTypes)) { |
||||
return "string"; |
||||
} else if (this.getDataType(column["data_type"], intTypes)) { |
||||
return "int"; |
||||
} else if (this.getDataType(column["data_type"], flatTypes)) { |
||||
return "float"; |
||||
} else if (this.getDataType(column["data_type"], dateTypes)) { |
||||
return "date"; |
||||
} else { |
||||
return "string"; |
||||
} |
||||
}; |
||||
|
||||
exports.getType = function(colType, typesArr) { |
||||
for (let i = 0; i < typesArr.length; ++i) { |
||||
// if (typesArr[i].indexOf(colType) !== -1) {
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
if (colType.indexOf(typesArr[i]) !== -1) { |
||||
return 1; |
||||
} |
||||
} |
||||
return 0; |
||||
}; |
@ -1,350 +0,0 @@
|
||||
"use strict"; |
||||
|
||||
/** |
||||
* |
||||
* @param input : 1,2,3,4 |
||||
* @returns obj.query = (?,?,?,?) obj.params = [1,2,3,4] |
||||
*/ |
||||
function prepareInClauseParams(input) { |
||||
let inElems = input.split(","); |
||||
let obj = {}; |
||||
obj.whereQuery = ""; |
||||
obj.whereParams = []; |
||||
|
||||
for (var j = 0; j < inElems.length; ++j) { |
||||
if (j === 0) { |
||||
//enclose with open parenthesis
|
||||
obj.whereQuery += "("; |
||||
} |
||||
if (j) { |
||||
obj.whereQuery += ","; |
||||
} |
||||
// add q mark and push the variable
|
||||
obj.whereQuery += "?"; |
||||
obj.whereParams.push(inElems[j]); |
||||
|
||||
if (j === inElems.length - 1) { |
||||
//enclose with closing parenthesis
|
||||
obj.whereQuery += ")"; |
||||
} |
||||
} |
||||
|
||||
//console.log(obj);
|
||||
|
||||
return obj; |
||||
} |
||||
|
||||
function prepareLikeValue(value) { |
||||
//return value.replace("~", "%");
|
||||
return value.replace(/~/g, "%"); |
||||
} |
||||
|
||||
function prepareIsValue(value) { |
||||
//return value.replace("~", "%");
|
||||
if (value === "null") { |
||||
return null; |
||||
} else if (value === "true") { |
||||
return true; |
||||
} else if (value === "false") { |
||||
return false; |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
function prepareBetweenValue(value) { |
||||
let values = value.split(","); |
||||
let obj = {}; |
||||
obj.whereQuery = ""; |
||||
obj.whereParams = []; |
||||
|
||||
if (values.length === 2) { |
||||
obj.whereQuery = " ? and ? "; |
||||
obj.whereParams.push(values[0]); |
||||
obj.whereParams.push(values[1]); |
||||
} |
||||
|
||||
//console.log('prepareBetweenValue', obj);
|
||||
|
||||
return obj; |
||||
} |
||||
|
||||
function replaceWhereParamsToQMarks( |
||||
openParentheses, |
||||
str, |
||||
comparisonOperator, |
||||
condType |
||||
) { |
||||
let converted = ""; |
||||
|
||||
if (openParentheses) { |
||||
for (var i = 0; i < str.length; ++i) { |
||||
if (str[i] === "(") { |
||||
converted += "("; |
||||
} else { |
||||
converted += "??"; |
||||
break; |
||||
} |
||||
} |
||||
} else { |
||||
if ( |
||||
comparisonOperator !== " in " && |
||||
comparisonOperator !== " between " && |
||||
condType !== " on " |
||||
) { |
||||
converted = "?"; |
||||
} else if (condType === " on ") { |
||||
converted = "??"; |
||||
} |
||||
|
||||
for (var i = str.length - 1; i >= 0; --i) { |
||||
if (str[i] === ")") { |
||||
converted += ")"; |
||||
} else { |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
return converted; |
||||
} |
||||
|
||||
function getComparisonOperator(operator) { |
||||
switch (operator) { |
||||
case "eq": |
||||
return "="; |
||||
break; |
||||
|
||||
case "ne": |
||||
return "!="; |
||||
break; |
||||
|
||||
case "lt": |
||||
return "<"; |
||||
break; |
||||
|
||||
case "lte": |
||||
return "<="; |
||||
break; |
||||
|
||||
case "gt": |
||||
return ">"; |
||||
break; |
||||
|
||||
case "gte": |
||||
return ">="; |
||||
break; |
||||
|
||||
case "is": |
||||
return " is "; |
||||
break; |
||||
|
||||
case "isnot": |
||||
return " is not "; |
||||
break; |
||||
|
||||
// case 'isnull':
|
||||
// return ' is NULL '
|
||||
// break;
|
||||
|
||||
// case 'isnnull':
|
||||
// return ' is not NULL '
|
||||
// break;
|
||||
|
||||
case "like": |
||||
return " like "; |
||||
break; |
||||
|
||||
case "nlike": |
||||
return " not like "; |
||||
break; |
||||
|
||||
case "in": |
||||
return " in "; |
||||
break; |
||||
|
||||
case "bw": |
||||
return " between "; |
||||
break; |
||||
|
||||
default: |
||||
return null; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
function getLogicalOperator(operator) { |
||||
switch (operator) { |
||||
case "~or": |
||||
return "or"; |
||||
break; |
||||
|
||||
case "~and": |
||||
return "and"; |
||||
break; |
||||
|
||||
// case '~not':
|
||||
// return 'not'
|
||||
// break;
|
||||
|
||||
case "~xor": |
||||
return "xor"; |
||||
break; |
||||
|
||||
default: |
||||
return null; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
exports.getConditionClause = function(whereInQueryParams, condType = "where") { |
||||
let whereQuery = ""; |
||||
let whereParams = []; |
||||
let grammarErr = 0; |
||||
let numOfConditions = whereInQueryParams.split(/~or|~and|~not|~xor/); |
||||
let logicalOperatorsInClause = whereInQueryParams.match( |
||||
/(~or|~and|~not|~xor)/g |
||||
); |
||||
|
||||
if ( |
||||
numOfConditions && |
||||
logicalOperatorsInClause && |
||||
numOfConditions.length !== logicalOperatorsInClause.length + 1 |
||||
) { |
||||
console.log( |
||||
"conditions and logical operators mismatch", |
||||
numOfConditions.length, |
||||
logicalOperatorsInClause.length |
||||
); |
||||
} else { |
||||
//console.log('numOfConditions',numOfConditions,whereInQueryParams);
|
||||
//console.log('logicalOperatorsInClause',logicalOperatorsInClause);
|
||||
|
||||
for (var i = 0; i < numOfConditions.length; ++i) { |
||||
let variable = ""; |
||||
let comparisonOperator = ""; |
||||
let logicalOperator = ""; |
||||
let variableValue = ""; |
||||
let temp = ""; |
||||
|
||||
// split on commas
|
||||
var arr = numOfConditions[i].split(","); |
||||
|
||||
// consider first two splits only
|
||||
var result = arr.splice(0, 2); |
||||
|
||||
// join to make only 3 array elements
|
||||
result.push(arr.join(",")); |
||||
|
||||
// variable, operator, variablevalue
|
||||
if (result.length !== 3) { |
||||
grammarErr = 1; |
||||
break; |
||||
} |
||||
|
||||
/**************** START : variable ****************/ |
||||
//console.log('variable, operator, variablevalue',result);
|
||||
variable = result[0].match(/\(+(.*)/); |
||||
|
||||
//console.log('variable',variable);
|
||||
|
||||
if (!variable || variable.length !== 2) { |
||||
grammarErr = 1; |
||||
break; |
||||
} |
||||
|
||||
// get the variableName and push
|
||||
whereParams.push(variable[1]); |
||||
|
||||
// then replace the variable name with ??
|
||||
temp = replaceWhereParamsToQMarks(true, result[0], " ignore ", condType); |
||||
if (!temp) { |
||||
grammarErr = 1; |
||||
break; |
||||
} |
||||
whereQuery += temp; |
||||
|
||||
/**************** END : variable ****************/ |
||||
|
||||
/**************** START : operator and value ****************/ |
||||
comparisonOperator = getComparisonOperator(result[1]); |
||||
if (!comparisonOperator) { |
||||
grammarErr = 1; |
||||
break; |
||||
} |
||||
whereQuery += comparisonOperator; |
||||
|
||||
// get the variableValue and push to params
|
||||
variableValue = result[2].match(/(.*?)\)/); |
||||
if (!variableValue || variableValue.length !== 2) { |
||||
grammarErr = 1; |
||||
break; |
||||
} |
||||
|
||||
if (comparisonOperator === " in ") { |
||||
let obj = prepareInClauseParams(variableValue[1]); |
||||
whereQuery += obj.whereQuery; |
||||
whereParams = whereParams.concat(obj.whereParams); |
||||
} else if ( |
||||
comparisonOperator === " like " || |
||||
comparisonOperator === " not like " |
||||
) { |
||||
whereParams.push(prepareLikeValue(variableValue[1])); |
||||
} else if (comparisonOperator === " is ") { |
||||
whereParams.push(prepareIsValue(variableValue[1])); |
||||
} else if (comparisonOperator === " between ") { |
||||
let obj = prepareBetweenValue(variableValue[1]); |
||||
whereQuery += obj.whereQuery; |
||||
whereParams = whereParams.concat(obj.whereParams); |
||||
//console.log(whereQuery, whereParams);
|
||||
} else { |
||||
whereParams.push(variableValue[1]); |
||||
} |
||||
|
||||
// then replace the variableValue with ?
|
||||
temp = replaceWhereParamsToQMarks( |
||||
false, |
||||
result[2], |
||||
comparisonOperator, |
||||
condType |
||||
); |
||||
if (!temp) { |
||||
grammarErr = 1; |
||||
break; |
||||
} |
||||
whereQuery += temp; |
||||
|
||||
if (numOfConditions.length !== -1 && i !== numOfConditions.length - 1) { |
||||
//console.log('finding logical operator for',logicalOperatorsInClause[i]);
|
||||
logicalOperator = getLogicalOperator(logicalOperatorsInClause[i]); |
||||
|
||||
if (!logicalOperator) { |
||||
grammarErr = 1; |
||||
break; |
||||
} |
||||
|
||||
whereQuery += getLogicalOperator(logicalOperatorsInClause[i]); |
||||
} |
||||
/**************** END : operator ****************/ |
||||
} |
||||
} |
||||
|
||||
let obj = {}; |
||||
|
||||
obj["query"] = ""; |
||||
obj["params"] = []; |
||||
obj["err"] = grammarErr; |
||||
|
||||
// console.log(whereInQueryParams);
|
||||
// console.log(whereQuery);
|
||||
// console.log(whereParams);
|
||||
// console.log(grammarErr);
|
||||
// console.log('= = = = = = = = =');
|
||||
|
||||
if (!grammarErr) { |
||||
obj["query"] = whereQuery; |
||||
obj["params"] = whereParams; |
||||
} |
||||
|
||||
return obj; |
||||
}; |
@ -1,508 +0,0 @@
|
||||
"use strict"; |
||||
|
||||
var Xsql = require("./xsql.js"); |
||||
var Xctrl = require("./xctrl.js"); |
||||
var multer = require("multer"); |
||||
const path = require("path"); |
||||
|
||||
const v8 = require("v8"), |
||||
os = require("os"); |
||||
|
||||
//define class
|
||||
class Xapi { |
||||
constructor(args, mysqlPool, app) { |
||||
this.config = args; |
||||
this.mysql = new Xsql(args, mysqlPool); |
||||
this.app = app; |
||||
this.ctrls = []; |
||||
|
||||
/**************** START : multer ****************/ |
||||
this.storage = multer.diskStorage({ |
||||
destination: function(req, file, cb) { |
||||
cb(null, process.cwd()); |
||||
}, |
||||
filename: function(req, file, cb) { |
||||
console.log(file); |
||||
cb(null, Date.now() + "-" + file.originalname); |
||||
} |
||||
}); |
||||
|
||||
this.upload = multer({ storage: this.storage }); |
||||
/**************** END : multer ****************/ |
||||
} |
||||
|
||||
init(cbk) { |
||||
this.mysql.init((err, results) => { |
||||
this.app.use(this.urlMiddleware.bind(this)); |
||||
let stat = this.setupRoutes(); |
||||
this.app.use(this.errorMiddleware.bind(this)); |
||||
cbk(err, stat); |
||||
}); |
||||
} |
||||
|
||||
urlMiddleware(req, res, next) { |
||||
// get only request url from originalUrl
|
||||
let justUrl = req.originalUrl.split("?")[0]; |
||||
let pathSplit = []; |
||||
|
||||
// split by apiPrefix
|
||||
let apiSuffix = justUrl.split(this.config.apiPrefix); |
||||
|
||||
if (apiSuffix.length === 2) { |
||||
// split by /
|
||||
pathSplit = apiSuffix[1].split("/"); |
||||
if (pathSplit.length) { |
||||
if (pathSplit.length >= 3) { |
||||
// handle for relational routes
|
||||
req.app.locals._parentTable = pathSplit[0]; |
||||
req.app.locals._childTable = pathSplit[2]; |
||||
} else { |
||||
// handles rest of routes
|
||||
req.app.locals._tableName = pathSplit[0]; |
||||
} |
||||
} |
||||
} |
||||
|
||||
next(); |
||||
} |
||||
|
||||
errorMiddleware(err, req, res, next) { |
||||
if (err && err.code) res.status(400).json({ error: err }); |
||||
else if (err && err.message) |
||||
res.status(500).json({ error: "Internal server error : " + err.message }); |
||||
else res.status(500).json({ error: "Internal server error : " + err }); |
||||
|
||||
next(err); |
||||
} |
||||
|
||||
asyncMiddleware(fn) { |
||||
return (req, res, next) => { |
||||
Promise.resolve(fn(req, res, next)).catch(err => { |
||||
next(err); |
||||
}); |
||||
}; |
||||
} |
||||
|
||||
root(req, res) { |
||||
let routes = []; |
||||
routes = this.mysql.getSchemaRoutes( |
||||
false, |
||||
req.protocol + "://" + req.get("host") + this.config.apiPrefix |
||||
); |
||||
routes = routes.concat( |
||||
this.mysql.globalRoutesPrint( |
||||
req.protocol + "://" + req.get("host") + this.config.apiPrefix |
||||
) |
||||
); |
||||
res.json(routes); |
||||
} |
||||
|
||||
setupRoutes() { |
||||
let stat = {} |
||||
stat.tables = 0 |
||||
stat.apis = 0 |
||||
stat.routines = 0 |
||||
|
||||
// console.log('this.config while setting up routes', this.config);
|
||||
|
||||
// show routes for database schema
|
||||
this.app.get("/", this.asyncMiddleware(this.root.bind(this))); |
||||
|
||||
// show all resouces
|
||||
this.app |
||||
.route(this.config.apiPrefix + "tables") |
||||
.get(this.asyncMiddleware(this.tables.bind(this))); |
||||
|
||||
this.app |
||||
.route(this.config.apiPrefix + "xjoin") |
||||
.get(this.asyncMiddleware(this.xjoin.bind(this))); |
||||
|
||||
stat.apis += 3; |
||||
|
||||
/**************** START : setup routes for each table ****************/ |
||||
let resources = []; |
||||
resources = this.mysql.getSchemaRoutes(true, this.config.apiPrefix); |
||||
|
||||
stat.tables += resources.length; |
||||
|
||||
// iterate over each resource
|
||||
for (var j = 0; j < resources.length; ++j) { |
||||
let resourceCtrl = new Xctrl(this.app, this.mysql); |
||||
this.ctrls.push(resourceCtrl); |
||||
|
||||
let routes = resources[j]["routes"]; |
||||
|
||||
stat.apis += resources[j]["routes"].length; |
||||
|
||||
// iterate over each routes in resource and map function
|
||||
for (var i = 0; i < routes.length; ++i) { |
||||
switch (routes[i]["routeType"]) { |
||||
case "list": |
||||
this.app |
||||
.route(routes[i]["routeUrl"]) |
||||
.get(this.asyncMiddleware(resourceCtrl.list.bind(resourceCtrl))); |
||||
break; |
||||
|
||||
case "findOne": |
||||
this.app |
||||
.route(routes[i]["routeUrl"]) |
||||
.get( |
||||
this.asyncMiddleware(resourceCtrl.findOne.bind(resourceCtrl)) |
||||
); |
||||
break; |
||||
|
||||
case "create": |
||||
if (!this.config.readOnly) |
||||
this.app |
||||
.route(routes[i]["routeUrl"]) |
||||
.post( |
||||
this.asyncMiddleware(resourceCtrl.create.bind(resourceCtrl)) |
||||
); |
||||
break; |
||||
|
||||
case "read": |
||||
this.app |
||||
.route(routes[i]["routeUrl"]) |
||||
.get(this.asyncMiddleware(resourceCtrl.read.bind(resourceCtrl))); |
||||
break; |
||||
|
||||
case "bulkInsert": |
||||
if (!this.config.readOnly) { |
||||
this.app |
||||
.route(routes[i]["routeUrl"]) |
||||
.post( |
||||
this.asyncMiddleware( |
||||
resourceCtrl.bulkInsert.bind(resourceCtrl) |
||||
) |
||||
); |
||||
} |
||||
break; |
||||
|
||||
case "bulkRead": |
||||
if (!this.config.readOnly) { |
||||
this.app |
||||
.route(routes[i]["routeUrl"]) |
||||
.get( |
||||
this.asyncMiddleware(resourceCtrl.bulkRead.bind(resourceCtrl)) |
||||
); |
||||
} else { |
||||
stat.apis--; |
||||
} |
||||
break; |
||||
|
||||
case "bulkDelete": |
||||
if (!this.config.readOnly) { |
||||
this.app |
||||
.route(routes[i]["routeUrl"]) |
||||
.delete( |
||||
this.asyncMiddleware( |
||||
resourceCtrl.bulkDelete.bind(resourceCtrl) |
||||
) |
||||
); |
||||
} else { |
||||
stat.apis--; |
||||
} |
||||
break; |
||||
|
||||
case "patch": |
||||
if (!this.config.readOnly) { |
||||
this.app |
||||
.route(routes[i]["routeUrl"]) |
||||
.patch( |
||||
this.asyncMiddleware(resourceCtrl.patch.bind(resourceCtrl)) |
||||
); |
||||
} else { |
||||
stat.apis--; |
||||
} |
||||
break; |
||||
|
||||
case "update": |
||||
if (!this.config.readOnly) { |
||||
this.app |
||||
.route(routes[i]["routeUrl"]) |
||||
.put( |
||||
this.asyncMiddleware(resourceCtrl.update.bind(resourceCtrl)) |
||||
); |
||||
} else { |
||||
stat.apis--; |
||||
} |
||||
break; |
||||
|
||||
case "delete": |
||||
if (!this.config.readOnly) { |
||||
this.app |
||||
.route(routes[i]["routeUrl"]) |
||||
.delete( |
||||
this.asyncMiddleware(resourceCtrl.delete.bind(resourceCtrl)) |
||||
); |
||||
} else { |
||||
stat.apis--; |
||||
} |
||||
break; |
||||
|
||||
case "exists": |
||||
this.app |
||||
.route(routes[i]["routeUrl"]) |
||||
.get( |
||||
this.asyncMiddleware(resourceCtrl.exists.bind(resourceCtrl)) |
||||
); |
||||
break; |
||||
|
||||
case "count": |
||||
this.app |
||||
.route(routes[i]["routeUrl"]) |
||||
.get(this.asyncMiddleware(resourceCtrl.count.bind(resourceCtrl))); |
||||
break; |
||||
|
||||
case "distinct": |
||||
this.app |
||||
.route(routes[i]["routeUrl"]) |
||||
.get( |
||||
this.asyncMiddleware(resourceCtrl.distinct.bind(resourceCtrl)) |
||||
); |
||||
break; |
||||
|
||||
case "describe": |
||||
this.app |
||||
.route(routes[i]["routeUrl"]) |
||||
.get(this.asyncMiddleware(this.tableDescribe.bind(this))); |
||||
break; |
||||
|
||||
case "relational": |
||||
this.app |
||||
.route(routes[i]["routeUrl"]) |
||||
.get( |
||||
this.asyncMiddleware(resourceCtrl.nestedList.bind(resourceCtrl)) |
||||
); |
||||
break; |
||||
|
||||
case "groupby": |
||||
this.app |
||||
.route(routes[i]["routeUrl"]) |
||||
.get( |
||||
this.asyncMiddleware(resourceCtrl.groupBy.bind(resourceCtrl)) |
||||
); |
||||
break; |
||||
|
||||
case "ugroupby": |
||||
this.app |
||||
.route(routes[i]["routeUrl"]) |
||||
.get( |
||||
this.asyncMiddleware(resourceCtrl.ugroupby.bind(resourceCtrl)) |
||||
); |
||||
break; |
||||
|
||||
case "chart": |
||||
this.app |
||||
.route(routes[i]["routeUrl"]) |
||||
.get(this.asyncMiddleware(resourceCtrl.chart.bind(resourceCtrl))); |
||||
break; |
||||
|
||||
case "autoChart": |
||||
this.app |
||||
.route(routes[i]["routeUrl"]) |
||||
.get( |
||||
this.asyncMiddleware(resourceCtrl.autoChart.bind(resourceCtrl)) |
||||
); |
||||
break; |
||||
|
||||
case "aggregate": |
||||
this.app |
||||
.route(routes[i]["routeUrl"]) |
||||
.get( |
||||
this.asyncMiddleware(resourceCtrl.aggregate.bind(resourceCtrl)) |
||||
); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
/**************** END : setup routes for each table ****************/ |
||||
|
||||
if (this.config.dynamic === 1 && !this.config.readOnly) { |
||||
this.app |
||||
.route("/dynamic*") |
||||
.post(this.asyncMiddleware(this.runQuery.bind(this))); |
||||
|
||||
/**************** START : multer routes ****************/ |
||||
this.app.post( |
||||
"/upload", |
||||
this.upload.single("file"), |
||||
this.uploadFile.bind(this) |
||||
); |
||||
this.app.post( |
||||
"/uploads", |
||||
this.upload.array("files", 10), |
||||
this.uploadFiles.bind(this) |
||||
); |
||||
this.app.get("/download", this.downloadFile.bind(this)); |
||||
/**************** END : multer routes ****************/ |
||||
|
||||
stat.apis += 4; |
||||
} |
||||
|
||||
/**************** START : health and version ****************/ |
||||
this.app.get("/_health", this.asyncMiddleware(this.health.bind(this))); |
||||
this.app.get("/_version", this.asyncMiddleware(this.version.bind(this))); |
||||
stat.apis += 2; |
||||
/**************** END : health and version ****************/ |
||||
|
||||
/**************** START : call stored procedures ****************/ |
||||
this.app.get('/_proc', this.asyncMiddleware(this.proc.bind(this))) |
||||
stat.apis += 1 |
||||
const procResources = this.mysql.getProcList(true, this.config.apiPrefix) |
||||
this.app.post('/_proc/:proc', this.asyncMiddleware(this.callProc.bind(this))) |
||||
stat.routines += procResources.length |
||||
stat.apis += procResources.length |
||||
/**************** END : call stored procedures ****************/ |
||||
|
||||
|
||||
let statStr = ' Generated: ' + stat.apis + ' REST APIs for ' + stat.tables + ' tables ' |
||||
|
||||
console.log(' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - '); |
||||
console.log(' '); |
||||
console.log(' Database : %s', this.config.database); |
||||
console.log(' Number of Tables : %s', stat.tables); |
||||
console.log(' Number of Routines : %s', stat.routines); |
||||
console.log(' '); |
||||
console.log(' REST APIs Generated : %s'.green.bold, stat.apis); |
||||
console.log(' '); |
||||
|
||||
return stat |
||||
|
||||
} |
||||
|
||||
async xjoin(req, res) { |
||||
let obj = {}; |
||||
|
||||
obj.query = ""; |
||||
obj.params = []; |
||||
|
||||
this.mysql.prepareJoinQuery(req, res, obj); |
||||
|
||||
//console.log(obj);
|
||||
if (obj.query.length) { |
||||
let results = await this.mysql.exec(obj.query, obj.params); |
||||
res.status(200).json(results); |
||||
} else { |
||||
res.status(400).json({ err: "Invalid Xjoin request" }); |
||||
} |
||||
} |
||||
|
||||
async tableDescribe(req, res) { |
||||
let query = "describe ??"; |
||||
let params = [req.app.locals._tableName]; |
||||
|
||||
let results = await this.mysql.exec(query, params); |
||||
res.status(200).json(results); |
||||
} |
||||
|
||||
async tables(req, res) { |
||||
let query = |
||||
"SELECT table_name AS resource FROM information_schema.tables WHERE table_schema = ? "; |
||||
let params = [this.config.database]; |
||||
|
||||
if (Object.keys(this.config.ignoreTables).length > 0) { |
||||
query += "and table_name not in (?)"; |
||||
params.push(Object.keys(this.config.ignoreTables)); |
||||
} |
||||
|
||||
let results = await this.mysql.exec(query, params); |
||||
|
||||
res.status(200).json(results); |
||||
} |
||||
|
||||
async runQuery(req, res) { |
||||
let query = req.body.query; |
||||
let params = req.body.params; |
||||
|
||||
let results = await this.mysql.exec(query, params); |
||||
res.status(200).json(results); |
||||
} |
||||
|
||||
/**************** START : files related ****************/ |
||||
downloadFile(req, res) { |
||||
let file = path.join(process.cwd(), req.query.name); |
||||
res.download(file); |
||||
} |
||||
|
||||
uploadFile(req, res) { |
||||
if (req.file) { |
||||
console.log(req.file.path); |
||||
res.end(req.file.path); |
||||
} else { |
||||
res.end("upload failed"); |
||||
} |
||||
} |
||||
|
||||
uploadFiles(req, res) { |
||||
if (!req.files || req.files.length === 0) { |
||||
res.end("upload failed"); |
||||
} else { |
||||
let files = []; |
||||
for (let i = 0; i < req.files.length; ++i) { |
||||
files.push(req.files[i].path); |
||||
} |
||||
|
||||
res.end(files.toString()); |
||||
} |
||||
} |
||||
|
||||
/**************** END : files related ****************/ |
||||
|
||||
/**************** START : health and version ****************/ |
||||
|
||||
async getMysqlUptime() { |
||||
let v = await this.mysql.exec("SHOW GLOBAL STATUS LIKE 'Uptime';", []); |
||||
return v[0]["Value"]; |
||||
} |
||||
|
||||
async getMysqlHealth() { |
||||
let v = await this.mysql.exec("select version() as version", []); |
||||
return v[0]["version"]; |
||||
} |
||||
|
||||
async health(req, res) { |
||||
let status = {}; |
||||
status["process_uptime"] = process.uptime(); |
||||
status["mysql_uptime"] = await this.getMysqlUptime(); |
||||
|
||||
if (Object.keys(req.query).length) { |
||||
status["process_memory_usage"] = process.memoryUsage(); |
||||
status["os_total_memory"] = os.totalmem(); |
||||
status["os_free_memory"] = os.freemem(); |
||||
status["os_load_average"] = os.loadavg(); |
||||
status["v8_heap_statistics"] = v8.getHeapStatistics(); |
||||
} |
||||
|
||||
res.json(status); |
||||
} |
||||
|
||||
async version(req, res) { |
||||
let version = {}; |
||||
|
||||
version["Xmysql"] = this.app.get("version"); |
||||
version["mysql"] = await this.getMysqlHealth(); |
||||
version["node"] = process.versions.node; |
||||
res.json(version); |
||||
} |
||||
|
||||
/**************** END : health and version ****************/ |
||||
|
||||
async proc(req, res) { |
||||
let query = 'SELECT routine_name AS resource FROM information_schema.routines WHERE routine_schema = ? '; |
||||
let params = [this.config.database]; |
||||
let results = await this.mysql.exec(query, params) |
||||
res.status(200).json(results) |
||||
} |
||||
|
||||
async callProc(req, res) { |
||||
let query = 'CALL ??(?)' |
||||
let params = [req.params.proc, Object.values(req.body)] |
||||
let results = await this.mysql.exec(query, params) |
||||
res.status(200).json(results) |
||||
} |
||||
|
||||
} |
||||
|
||||
//expose class
|
||||
module.exports = Xapi; |
@ -1,589 +0,0 @@
|
||||
//define class
|
||||
class xctrl { |
||||
constructor(app, mysql) { |
||||
this.app = app; |
||||
this.mysql = mysql; |
||||
} |
||||
|
||||
async create(req, res) { |
||||
let query = "INSERT INTO ?? SET ?"; |
||||
let params = []; |
||||
|
||||
params.push(req.app.locals._tableName); |
||||
params.push(req.body); |
||||
|
||||
var results = await this.mysql.exec(query, params); |
||||
res.status(200).json(results); |
||||
} |
||||
|
||||
async list(req, res) { |
||||
let queryParamsObj = {}; |
||||
queryParamsObj.query = ""; |
||||
queryParamsObj.params = []; |
||||
|
||||
this.mysql.prepareListQuery(req, res, queryParamsObj, 0); |
||||
|
||||
let results = await this.mysql.exec( |
||||
queryParamsObj.query, |
||||
queryParamsObj.params |
||||
); |
||||
res.status(200).json(results); |
||||
} |
||||
|
||||
async nestedList(req, res) { |
||||
let queryParamsObj = {}; |
||||
queryParamsObj.query = ""; |
||||
queryParamsObj.params = []; |
||||
|
||||
this.mysql.prepareListQuery(req, res, queryParamsObj, 1); |
||||
|
||||
let results = await this.mysql.exec( |
||||
queryParamsObj.query, |
||||
queryParamsObj.params |
||||
); |
||||
res.status(200).json(results); |
||||
} |
||||
|
||||
async findOne(req, res) { |
||||
let queryParamsObj = {}; |
||||
queryParamsObj.query = ""; |
||||
queryParamsObj.params = []; |
||||
|
||||
this.mysql.prepareListQuery(req, res, queryParamsObj, 2); |
||||
|
||||
let results = await this.mysql.exec( |
||||
queryParamsObj.query, |
||||
queryParamsObj.params |
||||
); |
||||
res.status(200).json(results); |
||||
} |
||||
|
||||
async read(req, res) { |
||||
let query = "select * from ?? where "; |
||||
let params = []; |
||||
|
||||
params.push(req.app.locals._tableName); |
||||
|
||||
let clause = this.mysql.getPrimaryKeyWhereClause( |
||||
req.app.locals._tableName, |
||||
req.params.id.split("___") |
||||
); |
||||
|
||||
if (!clause) { |
||||
return res.status(400).send({ |
||||
error: |
||||
"Table is made of composite primary keys - all keys were not in input" |
||||
}); |
||||
} |
||||
|
||||
query += clause; |
||||
query += " LIMIT 1"; |
||||
|
||||
let results = await this.mysql.exec(query, params); |
||||
res.status(200).json(results); |
||||
} |
||||
|
||||
async exists(req, res) { |
||||
let query = "select * from ?? where "; |
||||
let params = []; |
||||
|
||||
params.push(req.app.locals._tableName); |
||||
|
||||
let clause = this.mysql.getPrimaryKeyWhereClause( |
||||
req.app.locals._tableName, |
||||
req.params.id.split("___") |
||||
); |
||||
|
||||
if (!clause) { |
||||
return res.status(400).send({ |
||||
error: |
||||
"Table is made of composite primary keys - all keys were not in input" |
||||
}); |
||||
} |
||||
|
||||
query += clause; |
||||
query += " LIMIT 1"; |
||||
|
||||
let results = await this.mysql.exec(query, params); |
||||
res.status(200).json(results); |
||||
} |
||||
|
||||
async update(req, res) { |
||||
let query = "REPLACE INTO ?? SET ?"; |
||||
let params = []; |
||||
|
||||
params.push(req.app.locals._tableName); |
||||
params.push(req.body); |
||||
|
||||
var results = await this.mysql.exec(query, params); |
||||
res.status(200).json(results); |
||||
} |
||||
|
||||
async patch(req, res) { |
||||
let query = "UPDATE ?? SET "; |
||||
let keys = Object.keys(req.body); |
||||
|
||||
// SET clause
|
||||
let updateKeys = ""; |
||||
for (let i = 0; i < keys.length; ++i) { |
||||
updateKeys += keys[i] + " = ? "; |
||||
if (i !== keys.length - 1) updateKeys += ", "; |
||||
} |
||||
|
||||
// where clause
|
||||
query += updateKeys + " where "; |
||||
let clause = this.mysql.getPrimaryKeyWhereClause( |
||||
req.app.locals._tableName, |
||||
req.params.id.split("___") |
||||
); |
||||
|
||||
if (!clause) { |
||||
return res.status(400).send({ |
||||
error: |
||||
"Table is made of composite primary keys - all keys were not in input" |
||||
}); |
||||
} |
||||
|
||||
query += clause; |
||||
|
||||
// params
|
||||
let params = []; |
||||
params.push(req.app.locals._tableName); |
||||
params = params.concat(Object.values(req.body)); |
||||
|
||||
let results = await this.mysql.exec(query, params); |
||||
res.status(200).json(results); |
||||
} |
||||
|
||||
async delete(req, res) { |
||||
let query = "DELETE FROM ?? WHERE "; |
||||
let params = []; |
||||
|
||||
params.push(req.app.locals._tableName); |
||||
|
||||
let clause = this.mysql.getPrimaryKeyWhereClause( |
||||
req.app.locals._tableName, |
||||
req.params.id.split("___") |
||||
); |
||||
|
||||
if (!clause) { |
||||
return res.status(400).send({ |
||||
error: |
||||
"Table is made of composite primary keys - all keys were not in input" |
||||
}); |
||||
} |
||||
|
||||
query += clause; |
||||
|
||||
let results = await this.mysql.exec(query, params); |
||||
res.status(200).json(results); |
||||
} |
||||
|
||||
async bulkInsert(req, res) { |
||||
let queryParamsObj = {}; |
||||
queryParamsObj.query = ""; |
||||
queryParamsObj.params = []; |
||||
let results = []; |
||||
|
||||
//console.log(req.app.locals._tableName, req.body);
|
||||
|
||||
this.mysql.prepareBulkInsert( |
||||
req.app.locals._tableName, |
||||
req.body, |
||||
queryParamsObj |
||||
); |
||||
|
||||
results = await this.mysql.exec( |
||||
queryParamsObj.query, |
||||
queryParamsObj.params |
||||
); |
||||
res.status(200).json(results); |
||||
} |
||||
|
||||
async bulkDelete(req, res) { |
||||
let query = "delete from ?? where ?? in "; |
||||
let params = []; |
||||
|
||||
params.push(req.app.locals._tableName); |
||||
params.push(this.mysql.getPrimaryKeyName(req.app.locals._tableName)); |
||||
|
||||
query += "("; |
||||
|
||||
if (req.query && req.query._ids) { |
||||
let ids = req.query._ids.split(","); |
||||
for (var i = 0; i < ids.length; ++i) { |
||||
if (i) { |
||||
query += ","; |
||||
} |
||||
query += "?"; |
||||
params.push(ids[i]); |
||||
} |
||||
} |
||||
|
||||
query += ")"; |
||||
|
||||
//console.log(query, params);
|
||||
|
||||
var results = await this.mysql.exec(query, params); |
||||
res.status(200).json(results); |
||||
} |
||||
|
||||
async bulkRead(req, res) { |
||||
let queryParamsObj = {}; |
||||
queryParamsObj.query = ""; |
||||
queryParamsObj.params = []; |
||||
|
||||
this.mysql.prepareListQuery(req, res, queryParamsObj, 3); |
||||
|
||||
//console.log(queryParamsObj.query, queryParamsObj.params);
|
||||
|
||||
let results = await this.mysql.exec( |
||||
queryParamsObj.query, |
||||
queryParamsObj.params |
||||
); |
||||
res.status(200).json(results); |
||||
} |
||||
|
||||
async count(req, res) { |
||||
let queryParams = {}; |
||||
|
||||
queryParams.query = "select count(1) as no_of_rows from ?? "; |
||||
queryParams.params = []; |
||||
|
||||
queryParams.params.push(req.app.locals._tableName); |
||||
|
||||
this.mysql.getWhereClause( |
||||
req.query._where, |
||||
req.app.locals._tableName, |
||||
queryParams, |
||||
" where " |
||||
); |
||||
|
||||
let results = await this.mysql.exec(queryParams.query, queryParams.params); |
||||
res.status(200).json(results); |
||||
} |
||||
|
||||
async distinct(req, res) { |
||||
let queryParamsObj = {}; |
||||
queryParamsObj.query = ""; |
||||
queryParamsObj.params = []; |
||||
|
||||
this.mysql.prepareListQuery(req, res, queryParamsObj, 4); |
||||
|
||||
let results = await this.mysql.exec( |
||||
queryParamsObj.query, |
||||
queryParamsObj.params |
||||
); |
||||
res.status(200).json(results); |
||||
} |
||||
|
||||
async groupBy(req, res) { |
||||
if (req.query && req.query._fields) { |
||||
let queryParamsObj = {}; |
||||
queryParamsObj.query = "select "; |
||||
queryParamsObj.params = []; |
||||
|
||||
/**************** add columns and group by columns ****************/ |
||||
this.mysql.getColumnsForSelectStmt( |
||||
req.app.locals._tableName, |
||||
req.query, |
||||
queryParamsObj |
||||
); |
||||
|
||||
queryParamsObj.query += ",count(*) as _count from ?? group by "; |
||||
let tableName = req.app.locals._tableName; |
||||
queryParamsObj.params.push(tableName); |
||||
|
||||
this.mysql.getColumnsForGroupBy( |
||||
req.app.locals._tableName, |
||||
req.query, |
||||
queryParamsObj |
||||
); |
||||
|
||||
if (!req.query._sort) { |
||||
req.query._sort = {}; |
||||
req.query._sort = "-_count"; |
||||
} |
||||
|
||||
/**************** add having clause ****************/ |
||||
this.mysql.getHavingClause( |
||||
req.query._having, |
||||
req.app.locals._tableName, |
||||
queryParamsObj, |
||||
" having " |
||||
); |
||||
|
||||
/**************** add orderby clause ****************/ |
||||
this.mysql.getOrderByClause(req.query, tableName, queryParamsObj); |
||||
|
||||
//console.log(queryParamsObj.query, queryParamsObj.params);
|
||||
var results = await this.mysql.exec( |
||||
queryParamsObj.query, |
||||
queryParamsObj.params |
||||
); |
||||
|
||||
res.status(200).json(results); |
||||
} else { |
||||
res |
||||
.status(400) |
||||
.json({ |
||||
message: |
||||
"Missing _fields query params eg: /api/tableName/groupby?_fields=column1" |
||||
}); |
||||
} |
||||
} |
||||
|
||||
async ugroupby(req, res) { |
||||
if (req.query && req.query._fields) { |
||||
let queryParamsObj = {}; |
||||
queryParamsObj.query = ""; |
||||
queryParamsObj.params = []; |
||||
let uGrpByResults = {}; |
||||
|
||||
/**************** add fields with count(*) *****************/ |
||||
let fields = req.query._fields.split(","); |
||||
|
||||
for (var i = 0; i < fields.length; ++i) { |
||||
uGrpByResults[fields[i]] = []; |
||||
|
||||
if (i) { |
||||
queryParamsObj.query += " UNION "; |
||||
} |
||||
queryParamsObj.query += |
||||
" SELECT IFNULL(CONCAT(?,?,??),?) as ugroupby, count(*) as _count from ?? GROUP BY ?? "; |
||||
queryParamsObj.params.push(fields[i]); |
||||
queryParamsObj.params.push("~"); |
||||
queryParamsObj.params.push(fields[i]); |
||||
queryParamsObj.params.push(fields[i] + "~"); |
||||
queryParamsObj.params.push(req.app.locals._tableName); |
||||
queryParamsObj.params.push(fields[i]); |
||||
} |
||||
|
||||
//console.log(queryParamsObj.query, queryParamsObj.params);
|
||||
var results = await this.mysql.exec( |
||||
queryParamsObj.query, |
||||
queryParamsObj.params |
||||
); |
||||
|
||||
for (var i = 0; i < results.length; ++i) { |
||||
let grpByColName = results[i]["ugroupby"].split("~")[0]; |
||||
let grpByColValue = results[i]["ugroupby"].split("~")[1]; |
||||
|
||||
let obj = {}; |
||||
obj[grpByColValue] = results[i]["_count"]; |
||||
|
||||
uGrpByResults[grpByColName].push(obj); |
||||
} |
||||
|
||||
res.status(200).json(uGrpByResults); |
||||
} else { |
||||
res |
||||
.status(400) |
||||
.json({ |
||||
message: |
||||
"Missing _fields query params eg: /api/tableName/ugroupby?_fields=column1,column2" |
||||
}); |
||||
} |
||||
} |
||||
|
||||
async aggregate(req, res) { |
||||
if (req.query && req.query._fields) { |
||||
let tableName = req.app.locals._tableName; |
||||
let query = "select "; |
||||
let params = []; |
||||
let fields = req.query._fields.split(","); |
||||
|
||||
for (var i = 0; i < fields.length; ++i) { |
||||
if (i) { |
||||
query = query + ","; |
||||
} |
||||
query = |
||||
query + |
||||
" min(??) as ?,max(??) as ?,avg(??) as ?,sum(??) as ?,stddev(??) as ?,variance(??) as ? "; |
||||
params.push(fields[i]); |
||||
params.push("min_of_" + fields[i]); |
||||
params.push(fields[i]); |
||||
params.push("max_of_" + fields[i]); |
||||
params.push(fields[i]); |
||||
params.push("avg_of_" + fields[i]); |
||||
params.push(fields[i]); |
||||
params.push("sum_of_" + fields[i]); |
||||
params.push(fields[i]); |
||||
params.push("stddev_of_" + fields[i]); |
||||
params.push(fields[i]); |
||||
params.push("variance_of_" + fields[i]); |
||||
} |
||||
|
||||
query = query + " from ??"; |
||||
params.push(tableName); |
||||
|
||||
var results = await this.mysql.exec(query, params); |
||||
|
||||
res.status(200).json(results); |
||||
} else { |
||||
res |
||||
.status(400) |
||||
.json({ |
||||
message: |
||||
"Missing _fields in query params eg: /api/tableName/aggregate?_fields=numericColumn1" |
||||
}); |
||||
} |
||||
} |
||||
|
||||
async chart(req, res) { |
||||
let query = ""; |
||||
let params = []; |
||||
let obj = {}; |
||||
|
||||
if (req.query) { |
||||
let isRange = false; |
||||
if (req.query.range) { |
||||
isRange = true; |
||||
} |
||||
|
||||
if (req.query && req.query.min && req.query.max && req.query.step) { |
||||
//console.log(req.params.min, req.params.max, req.params.step);
|
||||
|
||||
obj = this.mysql.getChartQueryAndParamsFromMinMaxStep( |
||||
req.app.locals._tableName, |
||||
req.query._fields, |
||||
parseInt(req.query.min), |
||||
parseInt(req.query.max), |
||||
parseInt(req.query.step), |
||||
isRange |
||||
); |
||||
} else if ( |
||||
req.query && |
||||
req.query.steparray && |
||||
req.query.steparray.length > 1 |
||||
) { |
||||
obj = this.mysql.getChartQueryAndParamsFromStepArray( |
||||
req.app.locals._tableName, |
||||
req.query._fields, |
||||
req.query.steparray.split(",").map(Number), |
||||
isRange |
||||
); |
||||
} else if ( |
||||
req.query && |
||||
req.query.steppair && |
||||
req.query.steppair.length > 1 |
||||
) { |
||||
obj = this.mysql.getChartQueryAndParamsFromStepPair( |
||||
req.app.locals._tableName, |
||||
req.query._fields, |
||||
req.query.steppair.split(",").map(Number), |
||||
false |
||||
); |
||||
} else { |
||||
query = |
||||
"select min(??) as min,max(??) as max,stddev(??) as stddev,avg(??) as avg from ??"; |
||||
params = []; |
||||
|
||||
params.push(req.query._fields); |
||||
params.push(req.query._fields); |
||||
params.push(req.query._fields); |
||||
params.push(req.query._fields); |
||||
params.push(req.app.locals._tableName); |
||||
|
||||
let _this = this; |
||||
|
||||
let results = await _this.mysql.exec(query, params); |
||||
|
||||
//console.log(results, results['max'], req.params);
|
||||
|
||||
obj = _this.mysql.getChartQueryAndParamsFromMinMaxStddev( |
||||
req.app.locals._tableName, |
||||
req.query._fields, |
||||
results[0]["min"], |
||||
results[0]["max"], |
||||
results[0]["stddev"], |
||||
isRange |
||||
); |
||||
} |
||||
|
||||
this.mysql.getWhereClause( |
||||
req.query._where, |
||||
req.app.locals._tableName, |
||||
obj, |
||||
" where " |
||||
); |
||||
|
||||
let results = await this.mysql.exec(obj.query, obj.params); |
||||
|
||||
res.status(200).json(results); |
||||
} else { |
||||
res |
||||
.status(400) |
||||
.json({ |
||||
message: |
||||
"Missing _fields in query params eg: /api/tableName/chart?_fields=numericColumn1" |
||||
}); |
||||
} |
||||
} |
||||
|
||||
async autoChart(req, res) { |
||||
let query = "describe ??"; |
||||
let params = [req.app.locals._tableName]; |
||||
let obj = {}; |
||||
let results = []; |
||||
|
||||
let isRange = false; |
||||
if (req.query.range) { |
||||
isRange = true; |
||||
} |
||||
|
||||
let describeResults = await this.mysql.exec(query, params); |
||||
|
||||
//console.log(describeResults);
|
||||
|
||||
for (var i = 0; i < describeResults.length; ++i) { |
||||
//console.log('is this numeric column', describeResults[i]['Type']);
|
||||
|
||||
if ( |
||||
describeResults[i]["Key"] !== "PRI" && |
||||
this.mysql.isTypeOfColumnNumber(describeResults[i]["Type"]) |
||||
) { |
||||
query = |
||||
"select min(??) as min,max(??) as max,stddev(??) as stddev,avg(??) as avg from ??"; |
||||
params = []; |
||||
|
||||
params.push(describeResults[i]["Field"]); |
||||
params.push(describeResults[i]["Field"]); |
||||
params.push(describeResults[i]["Field"]); |
||||
params.push(describeResults[i]["Field"]); |
||||
params.push(req.app.locals._tableName); |
||||
|
||||
let _this = this; |
||||
|
||||
let minMaxResults = await _this.mysql.exec(query, params); |
||||
|
||||
//console.log(minMaxResults, minMaxResults['max'], req.params);
|
||||
|
||||
query = ""; |
||||
params = []; |
||||
|
||||
obj = _this.mysql.getChartQueryAndParamsFromMinMaxStddev( |
||||
req.app.locals._tableName, |
||||
describeResults[i]["Field"], |
||||
minMaxResults[0]["min"], |
||||
minMaxResults[0]["max"], |
||||
minMaxResults[0]["stddev"], |
||||
isRange |
||||
); |
||||
|
||||
let r = await this.mysql.exec(obj.query, obj.params); |
||||
|
||||
let resultObj = {}; |
||||
resultObj["column"] = describeResults[i]["Field"]; |
||||
resultObj["chart"] = r; |
||||
|
||||
results.push(resultObj); |
||||
} |
||||
} |
||||
|
||||
res.status(200).json(results); |
||||
} |
||||
} |
||||
|
||||
//expose class
|
||||
module.exports = xctrl; |
@ -0,0 +1,27 @@
|
||||
# Start |
||||
|
||||
# Social Media |
||||
- Twitter |
||||
- Youtube |
||||
- Discord |
||||
|
||||
|
||||
# Github |
||||
|
||||
## Github org rename |
||||
- rename xgenecloud into nocodb |
||||
|
||||
## Create xgenecloud organisation |
||||
- migrate repos : |
||||
- xc-seed-rest, xc-seed-gql, xc-desktop-app |
||||
- xgenecloud |
||||
|
||||
## Incorporate xmysql to nocodb |
||||
- Move xmysql to nocodb |
||||
- rename it to nocodb |
||||
- make a commit mentioning xmysql will be nocodb |
||||
- commit xgenecloud-ts into nocodb |
||||
- create roadmap |
||||
- remove/close issues |
||||
- remove/close pull requests |
||||
- headings |
@ -0,0 +1,25 @@
|
||||
# Google Oauth |
||||
|
||||
|
||||
![Step 1](../../static/google-oauth/1.png) |
||||
|
||||
![Step 2](../../static/google-oauth/2.png) |
||||
|
||||
![Step 3](../../static/google-oauth/3.png) |
||||
|
||||
![Step 4](../../static/google-oauth/4.png) |
||||
|
||||
![Step 1](../../static/google-oauth/1.png) |
||||
|
||||
![Step 5](../../static/google-oauth/5.png) |
||||
|
||||
![Step 6](../../static/google-oauth/6.png) |
||||
|
||||
![Step 7](../../static/google-oauth/7.png) |
||||
|
||||
![Step 8](../../static/google-oauth/8.png) |
||||
|
||||
![Step 9](../../static/google-oauth/9.png) |
||||
|
||||
![Step 10](../../static/google-oauth/10.png) |
||||
|
@ -0,0 +1,22 @@
|
||||
# AWS S3 |
||||
|
||||
|
||||
![Step 1](../../static/s3/1.png) |
||||
|
||||
![Step 2](../../static/s3/2.png) |
||||
|
||||
![Step 3](../../static/s3/3.png) |
||||
|
||||
![Step 4](../../static/s3/4.png) |
||||
|
||||
![Step 1](../../static/s3/1.png) |
||||
|
||||
![Step 5](../../static/s3/5.png) |
||||
|
||||
![Step 6](../../static/s3/6.png) |
||||
|
||||
![Step 7](../../static/s3/7.png) |
||||
|
||||
![Step 8](../../static/s3/8.png) |
||||
|
||||
![Step 9](../../static/s3/9.png) |
@ -0,0 +1,16 @@
|
||||
language: node_js |
||||
node_js: |
||||
- 12 |
||||
services: |
||||
- mysql |
||||
before_install: |
||||
- curl https://raw.githubusercontent.com/xgenecloud/xc-data/master/sakila-data.sql > sakila-data.sql |
||||
- curl https://raw.githubusercontent.com/xgenecloud/xc-data/master/sakila-schema.sql > sakila-schema.sql |
||||
- mysql -e 'CREATE DATABASE IF NOT EXISTS sakila;' |
||||
- mysql -u root --default-character-set=utf8 sakila < sakila-schema.sql |
||||
- mysql -u root --default-character-set=utf8 sakila < sakila-data.sql |
||||
install: |
||||
- npm install |
||||
- npm run lerna:install |
||||
script: |
||||
- npm run test:travis |
@ -1,48 +1,20 @@
|
||||
{ |
||||
"name": "xmysql", |
||||
"version": "0.6.0", |
||||
"description": "One command to generate REST APIs for any MySql database", |
||||
"main": "index.js", |
||||
"scripts": { |
||||
"test": "./node_modules/.bin/mocha tests/*.js --exit" |
||||
}, |
||||
"keywords": [ |
||||
"mysql-rest-api-generator", |
||||
"node-mysql-rest-api", |
||||
"rest", |
||||
"restful", |
||||
"rest-apis" |
||||
], |
||||
"engines": { |
||||
"node": ">= 7.6.0" |
||||
"name": "root", |
||||
"private": true, |
||||
"devDependencies": { |
||||
"cypress": "^7.3.0", |
||||
"jsdoc-to-markdown": "^5.0.3", |
||||
"lerna": "^3.20.1" |
||||
}, |
||||
"bin": { |
||||
"xmysql": "bin/index.js" |
||||
"scripts": { |
||||
"test:travis": "git log --pretty=format:'%h' -n 1 --skip 1 | xargs lerna run test:travis --since", |
||||
"lerna:install": "git log --pretty=format:'%h' -n 1 --skip 1 | xargs lerna bootstrap --ignore nc-cli --since", |
||||
"updated:xc-migrator": "lerna run publish --scope xc-migrator && lerna run xc && lerna publish && npm install -f xc-cli", |
||||
"doc": "lerna run doc", |
||||
"install:local:dep": "cd packages/nc-lib-gui;npm uninstall -S xc-lib;rm package-lock.json; npm i ../../../xc-lib-private; cd ../xc-instant;npm uninstall -S xc-lib xc-lib-gui;npm i ../../../xc-lib-private;npm i ../xc-lib-gui", |
||||
"install:npm:dep": "cd packages/nc-lib-gui;npm uninstall -S xc-lib; npm i -S xc-lib@latest; cd ../xc-instant;npm uninstall -S xc-lib xc-lib-gui;npm i -S xc-lib@latest xc-lib-gui@latest;npm i ../xc-lib-gui" |
||||
}, |
||||
"repository": "o1lab/xmysql", |
||||
"homepage": "https://github.com/o1lab/xmysql", |
||||
"author": "o1lab", |
||||
"license": "MIT", |
||||
"dependencies": { |
||||
"assert": "^1.4.1", |
||||
"body-parser": "^1.18.2", |
||||
"cluster": "^0.7.7", |
||||
"colors": "^1.1.2", |
||||
"commander": "^2.11.0", |
||||
"cors": "^2.8.4", |
||||
"express": "^4.16.1", |
||||
"morgan": "^1.9.0", |
||||
"multer": "^1.3.0", |
||||
"mysql": "^2.18.1" |
||||
}, |
||||
"devDependencies": { |
||||
"eslint-config-airbnb-base": "^13.1.0", |
||||
"eslint-config-prettier": "^4.1.0", |
||||
"eslint-plugin-import": "^2.16.0", |
||||
"eslint-plugin-prettier": "^3.0.1", |
||||
"mocha": "^5.2.0", |
||||
"should": "^13.1.2", |
||||
"sleep": "^6.1.0", |
||||
"supertest": "^3.0.0" |
||||
"nc-lib-gui": "^0.1.201" |
||||
} |
||||
} |
||||
|
@ -0,0 +1,10 @@
|
||||
/node_modules/ |
||||
#build |
||||
src/**.js |
||||
.idea/* |
||||
dist |
||||
coverage |
||||
.nyc_output |
||||
*.log |
||||
help |
||||
yarn.lock |
@ -0,0 +1,16 @@
|
||||
/node_modules/ |
||||
help |
||||
src |
||||
.idea/* |
||||
coverage |
||||
.nyc_output |
||||
*.log |
||||
yarn.lock |
||||
webpack.config.js |
||||
tsconfig.json |
||||
tsconfig.module.json |
||||
tslint.json |
||||
package-lock.json |
||||
dist/*.db-shm |
||||
dist/*.db-wal |
||||
/buildScript/ |
@ -0,0 +1,661 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE |
||||
Version 3, 19 November 2007 |
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> |
||||
Everyone is permitted to copy and distribute verbatim copies |
||||
of this license document, but changing it is not allowed. |
||||
|
||||
Preamble |
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for |
||||
software and other kinds of works, specifically designed to ensure |
||||
cooperation with the community in the case of network server software. |
||||
|
||||
The licenses for most software and other practical works are designed |
||||
to take away your freedom to share and change the works. By contrast, |
||||
our General Public Licenses are intended to guarantee your freedom to |
||||
share and change all versions of a program--to make sure it remains free |
||||
software for all its users. |
||||
|
||||
When we speak of free software, we are referring to freedom, not |
||||
price. Our General Public Licenses are designed to make sure that you |
||||
have the freedom to distribute copies of free software (and charge for |
||||
them if you wish), that you receive source code or can get it if you |
||||
want it, that you can change the software or use pieces of it in new |
||||
free programs, and that you know you can do these things. |
||||
|
||||
Developers that use our General Public Licenses protect your rights |
||||
with two steps: (1) assert copyright on the software, and (2) offer |
||||
you this License which gives you legal permission to copy, distribute |
||||
and/or modify the software. |
||||
|
||||
A secondary benefit of defending all users' freedom is that |
||||
improvements made in alternate versions of the program, if they |
||||
receive widespread use, become available for other developers to |
||||
incorporate. Many developers of free software are heartened and |
||||
encouraged by the resulting cooperation. However, in the case of |
||||
software used on network servers, this result may fail to come about. |
||||
The GNU General Public License permits making a modified version and |
||||
letting the public access it on a server without ever releasing its |
||||
source code to the public. |
||||
|
||||
The GNU Affero General Public License is designed specifically to |
||||
ensure that, in such cases, the modified source code becomes available |
||||
to the community. It requires the operator of a network server to |
||||
provide the source code of the modified version running there to the |
||||
users of that server. Therefore, public use of a modified version, on |
||||
a publicly accessible server, gives the public access to the source |
||||
code of the modified version. |
||||
|
||||
An older license, called the Affero General Public License and |
||||
published by Affero, was designed to accomplish similar goals. This is |
||||
a different license, not a version of the Affero GPL, but Affero has |
||||
released a new version of the Affero GPL which permits relicensing under |
||||
this license. |
||||
|
||||
The precise terms and conditions for copying, distribution and |
||||
modification follow. |
||||
|
||||
TERMS AND CONDITIONS |
||||
|
||||
0. Definitions. |
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License. |
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of |
||||
works, such as semiconductor masks. |
||||
|
||||
"The Program" refers to any copyrightable work licensed under this |
||||
License. Each licensee is addressed as "you". "Licensees" and |
||||
"recipients" may be individuals or organizations. |
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work |
||||
in a fashion requiring copyright permission, other than the making of an |
||||
exact copy. The resulting work is called a "modified version" of the |
||||
earlier work or a work "based on" the earlier work. |
||||
|
||||
A "covered work" means either the unmodified Program or a work based |
||||
on the Program. |
||||
|
||||
To "propagate" a work means to do anything with it that, without |
||||
permission, would make you directly or secondarily liable for |
||||
infringement under applicable copyright law, except executing it on a |
||||
computer or modifying a private copy. Propagation includes copying, |
||||
distribution (with or without modification), making available to the |
||||
public, and in some countries other activities as well. |
||||
|
||||
To "convey" a work means any kind of propagation that enables other |
||||
parties to make or receive copies. Mere interaction with a user through |
||||
a computer network, with no transfer of a copy, is not conveying. |
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices" |
||||
to the extent that it includes a convenient and prominently visible |
||||
feature that (1) displays an appropriate copyright notice, and (2) |
||||
tells the user that there is no warranty for the work (except to the |
||||
extent that warranties are provided), that licensees may convey the |
||||
work under this License, and how to view a copy of this License. If |
||||
the interface presents a list of user commands or options, such as a |
||||
menu, a prominent item in the list meets this criterion. |
||||
|
||||
1. Source Code. |
||||
|
||||
The "source code" for a work means the preferred form of the work |
||||
for making modifications to it. "Object code" means any non-source |
||||
form of a work. |
||||
|
||||
A "Standard Interface" means an interface that either is an official |
||||
standard defined by a recognized standards body, or, in the case of |
||||
interfaces specified for a particular programming language, one that |
||||
is widely used among developers working in that language. |
||||
|
||||
The "System Libraries" of an executable work include anything, other |
||||
than the work as a whole, that (a) is included in the normal form of |
||||
packaging a Major Component, but which is not part of that Major |
||||
Component, and (b) serves only to enable use of the work with that |
||||
Major Component, or to implement a Standard Interface for which an |
||||
implementation is available to the public in source code form. A |
||||
"Major Component", in this context, means a major essential component |
||||
(kernel, window system, and so on) of the specific operating system |
||||
(if any) on which the executable work runs, or a compiler used to |
||||
produce the work, or an object code interpreter used to run it. |
||||
|
||||
The "Corresponding Source" for a work in object code form means all |
||||
the source code needed to generate, install, and (for an executable |
||||
work) run the object code and to modify the work, including scripts to |
||||
control those activities. However, it does not include the work's |
||||
System Libraries, or general-purpose tools or generally available free |
||||
programs which are used unmodified in performing those activities but |
||||
which are not part of the work. For example, Corresponding Source |
||||
includes interface definition files associated with source files for |
||||
the work, and the source code for shared libraries and dynamically |
||||
linked subprograms that the work is specifically designed to require, |
||||
such as by intimate data communication or control flow between those |
||||
subprograms and other parts of the work. |
||||
|
||||
The Corresponding Source need not include anything that users |
||||
can regenerate automatically from other parts of the Corresponding |
||||
Source. |
||||
|
||||
The Corresponding Source for a work in source code form is that |
||||
same work. |
||||
|
||||
2. Basic Permissions. |
||||
|
||||
All rights granted under this License are granted for the term of |
||||
copyright on the Program, and are irrevocable provided the stated |
||||
conditions are met. This License explicitly affirms your unlimited |
||||
permission to run the unmodified Program. The output from running a |
||||
covered work is covered by this License only if the output, given its |
||||
content, constitutes a covered work. This License acknowledges your |
||||
rights of fair use or other equivalent, as provided by copyright law. |
||||
|
||||
You may make, run and propagate covered works that you do not |
||||
convey, without conditions so long as your license otherwise remains |
||||
in force. You may convey covered works to others for the sole purpose |
||||
of having them make modifications exclusively for you, or provide you |
||||
with facilities for running those works, provided that you comply with |
||||
the terms of this License in conveying all material for which you do |
||||
not control copyright. Those thus making or running the covered works |
||||
for you must do so exclusively on your behalf, under your direction |
||||
and control, on terms that prohibit them from making any copies of |
||||
your copyrighted material outside their relationship with you. |
||||
|
||||
Conveying under any other circumstances is permitted solely under |
||||
the conditions stated below. Sublicensing is not allowed; section 10 |
||||
makes it unnecessary. |
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law. |
||||
|
||||
No covered work shall be deemed part of an effective technological |
||||
measure under any applicable law fulfilling obligations under article |
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or |
||||
similar laws prohibiting or restricting circumvention of such |
||||
measures. |
||||
|
||||
When you convey a covered work, you waive any legal power to forbid |
||||
circumvention of technological measures to the extent such circumvention |
||||
is effected by exercising rights under this License with respect to |
||||
the covered work, and you disclaim any intention to limit operation or |
||||
modification of the work as a means of enforcing, against the work's |
||||
users, your or third parties' legal rights to forbid circumvention of |
||||
technological measures. |
||||
|
||||
4. Conveying Verbatim Copies. |
||||
|
||||
You may convey verbatim copies of the Program's source code as you |
||||
receive it, in any medium, provided that you conspicuously and |
||||
appropriately publish on each copy an appropriate copyright notice; |
||||
keep intact all notices stating that this License and any |
||||
non-permissive terms added in accord with section 7 apply to the code; |
||||
keep intact all notices of the absence of any warranty; and give all |
||||
recipients a copy of this License along with the Program. |
||||
|
||||
You may charge any price or no price for each copy that you convey, |
||||
and you may offer support or warranty protection for a fee. |
||||
|
||||
5. Conveying Modified Source Versions. |
||||
|
||||
You may convey a work based on the Program, or the modifications to |
||||
produce it from the Program, in the form of source code under the |
||||
terms of section 4, provided that you also meet all of these conditions: |
||||
|
||||
a) The work must carry prominent notices stating that you modified |
||||
it, and giving a relevant date. |
||||
|
||||
b) The work must carry prominent notices stating that it is |
||||
released under this License and any conditions added under section |
||||
7. This requirement modifies the requirement in section 4 to |
||||
"keep intact all notices". |
||||
|
||||
c) You must license the entire work, as a whole, under this |
||||
License to anyone who comes into possession of a copy. This |
||||
License will therefore apply, along with any applicable section 7 |
||||
additional terms, to the whole of the work, and all its parts, |
||||
regardless of how they are packaged. This License gives no |
||||
permission to license the work in any other way, but it does not |
||||
invalidate such permission if you have separately received it. |
||||
|
||||
d) If the work has interactive user interfaces, each must display |
||||
Appropriate Legal Notices; however, if the Program has interactive |
||||
interfaces that do not display Appropriate Legal Notices, your |
||||
work need not make them do so. |
||||
|
||||
A compilation of a covered work with other separate and independent |
||||
works, which are not by their nature extensions of the covered work, |
||||
and which are not combined with it such as to form a larger program, |
||||
in or on a volume of a storage or distribution medium, is called an |
||||
"aggregate" if the compilation and its resulting copyright are not |
||||
used to limit the access or legal rights of the compilation's users |
||||
beyond what the individual works permit. Inclusion of a covered work |
||||
in an aggregate does not cause this License to apply to the other |
||||
parts of the aggregate. |
||||
|
||||
6. Conveying Non-Source Forms. |
||||
|
||||
You may convey a covered work in object code form under the terms |
||||
of sections 4 and 5, provided that you also convey the |
||||
machine-readable Corresponding Source under the terms of this License, |
||||
in one of these ways: |
||||
|
||||
a) Convey the object code in, or embodied in, a physical product |
||||
(including a physical distribution medium), accompanied by the |
||||
Corresponding Source fixed on a durable physical medium |
||||
customarily used for software interchange. |
||||
|
||||
b) Convey the object code in, or embodied in, a physical product |
||||
(including a physical distribution medium), accompanied by a |
||||
written offer, valid for at least three years and valid for as |
||||
long as you offer spare parts or customer support for that product |
||||
model, to give anyone who possesses the object code either (1) a |
||||
copy of the Corresponding Source for all the software in the |
||||
product that is covered by this License, on a durable physical |
||||
medium customarily used for software interchange, for a price no |
||||
more than your reasonable cost of physically performing this |
||||
conveying of source, or (2) access to copy the |
||||
Corresponding Source from a network server at no charge. |
||||
|
||||
c) Convey individual copies of the object code with a copy of the |
||||
written offer to provide the Corresponding Source. This |
||||
alternative is allowed only occasionally and noncommercially, and |
||||
only if you received the object code with such an offer, in accord |
||||
with subsection 6b. |
||||
|
||||
d) Convey the object code by offering access from a designated |
||||
place (gratis or for a charge), and offer equivalent access to the |
||||
Corresponding Source in the same way through the same place at no |
||||
further charge. You need not require recipients to copy the |
||||
Corresponding Source along with the object code. If the place to |
||||
copy the object code is a network server, the Corresponding Source |
||||
may be on a different server (operated by you or a third party) |
||||
that supports equivalent copying facilities, provided you maintain |
||||
clear directions next to the object code saying where to find the |
||||
Corresponding Source. Regardless of what server hosts the |
||||
Corresponding Source, you remain obligated to ensure that it is |
||||
available for as long as needed to satisfy these requirements. |
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided |
||||
you inform other peers where the object code and Corresponding |
||||
Source of the work are being offered to the general public at no |
||||
charge under subsection 6d. |
||||
|
||||
A separable portion of the object code, whose source code is excluded |
||||
from the Corresponding Source as a System Library, need not be |
||||
included in conveying the object code work. |
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any |
||||
tangible personal property which is normally used for personal, family, |
||||
or household purposes, or (2) anything designed or sold for incorporation |
||||
into a dwelling. In determining whether a product is a consumer product, |
||||
doubtful cases shall be resolved in favor of coverage. For a particular |
||||
product received by a particular user, "normally used" refers to a |
||||
typical or common use of that class of product, regardless of the status |
||||
of the particular user or of the way in which the particular user |
||||
actually uses, or expects or is expected to use, the product. A product |
||||
is a consumer product regardless of whether the product has substantial |
||||
commercial, industrial or non-consumer uses, unless such uses represent |
||||
the only significant mode of use of the product. |
||||
|
||||
"Installation Information" for a User Product means any methods, |
||||
procedures, authorization keys, or other information required to install |
||||
and execute modified versions of a covered work in that User Product from |
||||
a modified version of its Corresponding Source. The information must |
||||
suffice to ensure that the continued functioning of the modified object |
||||
code is in no case prevented or interfered with solely because |
||||
modification has been made. |
||||
|
||||
If you convey an object code work under this section in, or with, or |
||||
specifically for use in, a User Product, and the conveying occurs as |
||||
part of a transaction in which the right of possession and use of the |
||||
User Product is transferred to the recipient in perpetuity or for a |
||||
fixed term (regardless of how the transaction is characterized), the |
||||
Corresponding Source conveyed under this section must be accompanied |
||||
by the Installation Information. But this requirement does not apply |
||||
if neither you nor any third party retains the ability to install |
||||
modified object code on the User Product (for example, the work has |
||||
been installed in ROM). |
||||
|
||||
The requirement to provide Installation Information does not include a |
||||
requirement to continue to provide support service, warranty, or updates |
||||
for a work that has been modified or installed by the recipient, or for |
||||
the User Product in which it has been modified or installed. Access to a |
||||
network may be denied when the modification itself materially and |
||||
adversely affects the operation of the network or violates the rules and |
||||
protocols for communication across the network. |
||||
|
||||
Corresponding Source conveyed, and Installation Information provided, |
||||
in accord with this section must be in a format that is publicly |
||||
documented (and with an implementation available to the public in |
||||
source code form), and must require no special password or key for |
||||
unpacking, reading or copying. |
||||
|
||||
7. Additional Terms. |
||||
|
||||
"Additional permissions" are terms that supplement the terms of this |
||||
License by making exceptions from one or more of its conditions. |
||||
Additional permissions that are applicable to the entire Program shall |
||||
be treated as though they were included in this License, to the extent |
||||
that they are valid under applicable law. If additional permissions |
||||
apply only to part of the Program, that part may be used separately |
||||
under those permissions, but the entire Program remains governed by |
||||
this License without regard to the additional permissions. |
||||
|
||||
When you convey a copy of a covered work, you may at your option |
||||
remove any additional permissions from that copy, or from any part of |
||||
it. (Additional permissions may be written to require their own |
||||
removal in certain cases when you modify the work.) You may place |
||||
additional permissions on material, added by you to a covered work, |
||||
for which you have or can give appropriate copyright permission. |
||||
|
||||
Notwithstanding any other provision of this License, for material you |
||||
add to a covered work, you may (if authorized by the copyright holders of |
||||
that material) supplement the terms of this License with terms: |
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the |
||||
terms of sections 15 and 16 of this License; or |
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or |
||||
author attributions in that material or in the Appropriate Legal |
||||
Notices displayed by works containing it; or |
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or |
||||
requiring that modified versions of such material be marked in |
||||
reasonable ways as different from the original version; or |
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or |
||||
authors of the material; or |
||||
|
||||
e) Declining to grant rights under trademark law for use of some |
||||
trade names, trademarks, or service marks; or |
||||
|
||||
f) Requiring indemnification of licensors and authors of that |
||||
material by anyone who conveys the material (or modified versions of |
||||
it) with contractual assumptions of liability to the recipient, for |
||||
any liability that these contractual assumptions directly impose on |
||||
those licensors and authors. |
||||
|
||||
All other non-permissive additional terms are considered "further |
||||
restrictions" within the meaning of section 10. If the Program as you |
||||
received it, or any part of it, contains a notice stating that it is |
||||
governed by this License along with a term that is a further |
||||
restriction, you may remove that term. If a license document contains |
||||
a further restriction but permits relicensing or conveying under this |
||||
License, you may add to a covered work material governed by the terms |
||||
of that license document, provided that the further restriction does |
||||
not survive such relicensing or conveying. |
||||
|
||||
If you add terms to a covered work in accord with this section, you |
||||
must place, in the relevant source files, a statement of the |
||||
additional terms that apply to those files, or a notice indicating |
||||
where to find the applicable terms. |
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the |
||||
form of a separately written license, or stated as exceptions; |
||||
the above requirements apply either way. |
||||
|
||||
8. Termination. |
||||
|
||||
You may not propagate or modify a covered work except as expressly |
||||
provided under this License. Any attempt otherwise to propagate or |
||||
modify it is void, and will automatically terminate your rights under |
||||
this License (including any patent licenses granted under the third |
||||
paragraph of section 11). |
||||
|
||||
However, if you cease all violation of this License, then your |
||||
license from a particular copyright holder is reinstated (a) |
||||
provisionally, unless and until the copyright holder explicitly and |
||||
finally terminates your license, and (b) permanently, if the copyright |
||||
holder fails to notify you of the violation by some reasonable means |
||||
prior to 60 days after the cessation. |
||||
|
||||
Moreover, your license from a particular copyright holder is |
||||
reinstated permanently if the copyright holder notifies you of the |
||||
violation by some reasonable means, this is the first time you have |
||||
received notice of violation of this License (for any work) from that |
||||
copyright holder, and you cure the violation prior to 30 days after |
||||
your receipt of the notice. |
||||
|
||||
Termination of your rights under this section does not terminate the |
||||
licenses of parties who have received copies or rights from you under |
||||
this License. If your rights have been terminated and not permanently |
||||
reinstated, you do not qualify to receive new licenses for the same |
||||
material under section 10. |
||||
|
||||
9. Acceptance Not Required for Having Copies. |
||||
|
||||
You are not required to accept this License in order to receive or |
||||
run a copy of the Program. Ancillary propagation of a covered work |
||||
occurring solely as a consequence of using peer-to-peer transmission |
||||
to receive a copy likewise does not require acceptance. However, |
||||
nothing other than this License grants you permission to propagate or |
||||
modify any covered work. These actions infringe copyright if you do |
||||
not accept this License. Therefore, by modifying or propagating a |
||||
covered work, you indicate your acceptance of this License to do so. |
||||
|
||||
10. Automatic Licensing of Downstream Recipients. |
||||
|
||||
Each time you convey a covered work, the recipient automatically |
||||
receives a license from the original licensors, to run, modify and |
||||
propagate that work, subject to this License. You are not responsible |
||||
for enforcing compliance by third parties with this License. |
||||
|
||||
An "entity transaction" is a transaction transferring control of an |
||||
organization, or substantially all assets of one, or subdividing an |
||||
organization, or merging organizations. If propagation of a covered |
||||
work results from an entity transaction, each party to that |
||||
transaction who receives a copy of the work also receives whatever |
||||
licenses to the work the party's predecessor in interest had or could |
||||
give under the previous paragraph, plus a right to possession of the |
||||
Corresponding Source of the work from the predecessor in interest, if |
||||
the predecessor has it or can get it with reasonable efforts. |
||||
|
||||
You may not impose any further restrictions on the exercise of the |
||||
rights granted or affirmed under this License. For example, you may |
||||
not impose a license fee, royalty, or other charge for exercise of |
||||
rights granted under this License, and you may not initiate litigation |
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that |
||||
any patent claim is infringed by making, using, selling, offering for |
||||
sale, or importing the Program or any portion of it. |
||||
|
||||
11. Patents. |
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this |
||||
License of the Program or a work on which the Program is based. The |
||||
work thus licensed is called the contributor's "contributor version". |
||||
|
||||
A contributor's "essential patent claims" are all patent claims |
||||
owned or controlled by the contributor, whether already acquired or |
||||
hereafter acquired, that would be infringed by some manner, permitted |
||||
by this License, of making, using, or selling its contributor version, |
||||
but do not include claims that would be infringed only as a |
||||
consequence of further modification of the contributor version. For |
||||
purposes of this definition, "control" includes the right to grant |
||||
patent sublicenses in a manner consistent with the requirements of |
||||
this License. |
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free |
||||
patent license under the contributor's essential patent claims, to |
||||
make, use, sell, offer for sale, import and otherwise run, modify and |
||||
propagate the contents of its contributor version. |
||||
|
||||
In the following three paragraphs, a "patent license" is any express |
||||
agreement or commitment, however denominated, not to enforce a patent |
||||
(such as an express permission to practice a patent or covenant not to |
||||
sue for patent infringement). To "grant" such a patent license to a |
||||
party means to make such an agreement or commitment not to enforce a |
||||
patent against the party. |
||||
|
||||
If you convey a covered work, knowingly relying on a patent license, |
||||
and the Corresponding Source of the work is not available for anyone |
||||
to copy, free of charge and under the terms of this License, through a |
||||
publicly available network server or other readily accessible means, |
||||
then you must either (1) cause the Corresponding Source to be so |
||||
available, or (2) arrange to deprive yourself of the benefit of the |
||||
patent license for this particular work, or (3) arrange, in a manner |
||||
consistent with the requirements of this License, to extend the patent |
||||
license to downstream recipients. "Knowingly relying" means you have |
||||
actual knowledge that, but for the patent license, your conveying the |
||||
covered work in a country, or your recipient's use of the covered work |
||||
in a country, would infringe one or more identifiable patents in that |
||||
country that you have reason to believe are valid. |
||||
|
||||
If, pursuant to or in connection with a single transaction or |
||||
arrangement, you convey, or propagate by procuring conveyance of, a |
||||
covered work, and grant a patent license to some of the parties |
||||
receiving the covered work authorizing them to use, propagate, modify |
||||
or convey a specific copy of the covered work, then the patent license |
||||
you grant is automatically extended to all recipients of the covered |
||||
work and works based on it. |
||||
|
||||
A patent license is "discriminatory" if it does not include within |
||||
the scope of its coverage, prohibits the exercise of, or is |
||||
conditioned on the non-exercise of one or more of the rights that are |
||||
specifically granted under this License. You may not convey a covered |
||||
work if you are a party to an arrangement with a third party that is |
||||
in the business of distributing software, under which you make payment |
||||
to the third party based on the extent of your activity of conveying |
||||
the work, and under which the third party grants, to any of the |
||||
parties who would receive the covered work from you, a discriminatory |
||||
patent license (a) in connection with copies of the covered work |
||||
conveyed by you (or copies made from those copies), or (b) primarily |
||||
for and in connection with specific products or compilations that |
||||
contain the covered work, unless you entered into that arrangement, |
||||
or that patent license was granted, prior to 28 March 2007. |
||||
|
||||
Nothing in this License shall be construed as excluding or limiting |
||||
any implied license or other defenses to infringement that may |
||||
otherwise be available to you under applicable patent law. |
||||
|
||||
12. No Surrender of Others' Freedom. |
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or |
||||
otherwise) that contradict the conditions of this License, they do not |
||||
excuse you from the conditions of this License. If you cannot convey a |
||||
covered work so as to satisfy simultaneously your obligations under this |
||||
License and any other pertinent obligations, then as a consequence you may |
||||
not convey it at all. For example, if you agree to terms that obligate you |
||||
to collect a royalty for further conveying from those to whom you convey |
||||
the Program, the only way you could satisfy both those terms and this |
||||
License would be to refrain entirely from conveying the Program. |
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License. |
||||
|
||||
Notwithstanding any other provision of this License, if you modify the |
||||
Program, your modified version must prominently offer all users |
||||
interacting with it remotely through a computer network (if your version |
||||
supports such interaction) an opportunity to receive the Corresponding |
||||
Source of your version by providing access to the Corresponding Source |
||||
from a network server at no charge, through some standard or customary |
||||
means of facilitating copying of software. This Corresponding Source |
||||
shall include the Corresponding Source for any work covered by version 3 |
||||
of the GNU General Public License that is incorporated pursuant to the |
||||
following paragraph. |
||||
|
||||
Notwithstanding any other provision of this License, you have |
||||
permission to link or combine any covered work with a work licensed |
||||
under version 3 of the GNU General Public License into a single |
||||
combined work, and to convey the resulting work. The terms of this |
||||
License will continue to apply to the part which is the covered work, |
||||
but the work with which it is combined will remain governed by version |
||||
3 of the GNU General Public License. |
||||
|
||||
14. Revised Versions of this License. |
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of |
||||
the GNU Affero General Public License from time to time. Such new versions |
||||
will be similar in spirit to the present version, but may differ in detail to |
||||
address new problems or concerns. |
||||
|
||||
Each version is given a distinguishing version number. If the |
||||
Program specifies that a certain numbered version of the GNU Affero General |
||||
Public License "or any later version" applies to it, you have the |
||||
option of following the terms and conditions either of that numbered |
||||
version or of any later version published by the Free Software |
||||
Foundation. If the Program does not specify a version number of the |
||||
GNU Affero General Public License, you may choose any version ever published |
||||
by the Free Software Foundation. |
||||
|
||||
If the Program specifies that a proxy can decide which future |
||||
versions of the GNU Affero General Public License can be used, that proxy's |
||||
public statement of acceptance of a version permanently authorizes you |
||||
to choose that version for the Program. |
||||
|
||||
Later license versions may give you additional or different |
||||
permissions. However, no additional obligations are imposed on any |
||||
author or copyright holder as a result of your choosing to follow a |
||||
later version. |
||||
|
||||
15. Disclaimer of Warranty. |
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY |
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT |
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY |
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, |
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM |
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF |
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION. |
||||
|
||||
16. Limitation of Liability. |
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING |
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS |
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY |
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE |
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF |
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD |
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), |
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF |
||||
SUCH DAMAGES. |
||||
|
||||
17. Interpretation of Sections 15 and 16. |
||||
|
||||
If the disclaimer of warranty and limitation of liability provided |
||||
above cannot be given local legal effect according to their terms, |
||||
reviewing courts shall apply local law that most closely approximates |
||||
an absolute waiver of all civil liability in connection with the |
||||
Program, unless a warranty or assumption of liability accompanies a |
||||
copy of the Program in return for a fee. |
||||
|
||||
END OF TERMS AND CONDITIONS |
||||
|
||||
How to Apply These Terms to Your New Programs |
||||
|
||||
If you develop a new program, and you want it to be of the greatest |
||||
possible use to the public, the best way to achieve this is to make it |
||||
free software which everyone can redistribute and change under these terms. |
||||
|
||||
To do so, attach the following notices to the program. It is safest |
||||
to attach them to the start of each source file to most effectively |
||||
state the exclusion of warranty; and each file should have at least |
||||
the "copyright" line and a pointer to where the full notice is found. |
||||
|
||||
<one line to give the program's name and a brief idea of what it does.> |
||||
Copyright (C) <year> <name of author> |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU Affero General Public License as published |
||||
by the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU Affero General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU Affero General Public License |
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
|
||||
Also add information on how to contact you by electronic and paper mail. |
||||
|
||||
If your software can interact with users remotely through a computer |
||||
network, you should also make sure that it provides a way for users to |
||||
get its source. For example, if your program is a web application, its |
||||
interface could display a "Source" link that leads users to an archive |
||||
of the code. There are many ways you could offer source, and different |
||||
solutions will be better for different programs; see section 13 for the |
||||
specific requirements. |
||||
|
||||
You should also get your employer (if you work as a programmer) or school, |
||||
if any, to sign a "copyright disclaimer" for the program, if necessary. |
||||
For more information on this, and how to apply and follow the GNU AGPL, see |
||||
<http://www.gnu.org/licenses/>. |
@ -0,0 +1,11 @@
|
||||
const fs = require('fs'); |
||||
const path = require('path'); |
||||
const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname,'..','package.json'), 'utf8')); |
||||
|
||||
const version = packageJson.version.replace(/\.(\d+)$/, (_, v) => { |
||||
return `.${++v}` |
||||
}); |
||||
|
||||
packageJson.version = version; |
||||
|
||||
fs.writeFileSync(path.join(__dirname,'..','package.json'), JSON.stringify(packageJson, null, 2)); |
@ -0,0 +1,22 @@
|
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* 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/>. |
||||
* |
||||
*/ |
@ -0,0 +1,139 @@
|
||||
{ |
||||
"name": "create-nocodb-app", |
||||
"version": "0.1.21", |
||||
"description": "nc-cli", |
||||
"main": "dist/bundle.js", |
||||
"module": "dist/bundle.js", |
||||
"repository": "https://github.com/pranavxc/nc-cli", |
||||
"license": "AGPL-3.0-or-later", |
||||
"keywords": [], |
||||
"bin": { |
||||
"nc": "./dist/bundle.js", |
||||
"create-nocodb-app": "./dist/bundle.js", |
||||
"nc-cli": "./dist/bundle.js" |
||||
}, |
||||
"scripts": { |
||||
"describe": "npm-scripts-info", |
||||
"build": "run-s clean && run-p build:*", |
||||
"build:main": "tsc -p tsconfig.json", |
||||
"build:module": "tsc -p tsconfig.module.json", |
||||
"build:obfuscate": "webpack --config webpack.config.js", |
||||
"build:obfuscate:publish": "rm -rf dist && npm run build:obfuscate && npm publish .", |
||||
"fix": "run-s fix:*", |
||||
"fix:prettier": "prettier \"src/**/*.ts\" --write", |
||||
"fix:tslint": "tslint --fix --project .", |
||||
"test": "run-s build test:*", |
||||
"test:lint": "tslint --project . && prettier \"src/**/*.ts\" --list-different", |
||||
"test:unit": "nyc --silent ava", |
||||
"watch": "run-s clean build:main && run-p \"build:main -- -w\" \"test:unit -- --watch\"", |
||||
"cov": "run-s build test:unit cov:html && open-cli coverage/index.html", |
||||
"cov:html": "nyc report --reporter=html", |
||||
"cov:send": "nyc report --reporter=lcov && codecov", |
||||
"cov:check": "nyc report && nyc check-coverage --lines 100 --functions 100 --branches 100", |
||||
"doc": "run-s doc:html && open-cli build/docs/index.html", |
||||
"doc:html": "typedoc src/ --exclude **/*.spec.ts --target ES6 --mode file --out build/docs", |
||||
"doc:json": "typedoc src/ --exclude **/*.spec.ts --target ES6 --mode file --json build/docs/typedoc.json", |
||||
"doc:publish": "gh-pages -m \"[ci skip] Updates\" -d build/docs", |
||||
"version": "standard-version", |
||||
"reset": "git clean -dfx && git reset --hard && npm i", |
||||
"clean": "trash build test", |
||||
"prepare-release": "run-s reset test cov:check doc:html version doc:publish", |
||||
"test:new": "./dist/bundle.js new " |
||||
}, |
||||
"scripts-info": { |
||||
"info": "Display information about the package scripts", |
||||
"build": "Clean and rebuild the project", |
||||
"fix": "Try to automatically fix any linting problems", |
||||
"test": "Lint and unit test the project", |
||||
"watch": "Watch and rebuild the project on save, then rerun relevant tests", |
||||
"cov": "Rebuild, run tests, then create and open the coverage report", |
||||
"doc": "Generate HTML API documentation and open it in a browser", |
||||
"doc:json": "Generate API documentation in typedoc JSON format", |
||||
"version": "Bump package.json version, update CHANGELOG.md, tag release", |
||||
"reset": "Delete all untracked files and reset the repo to the last commit", |
||||
"prepare-release": "One-step: clean, build, test, publish docs, and prep a release" |
||||
}, |
||||
"engines": { |
||||
"node": ">=8.9" |
||||
}, |
||||
"dependencies": { |
||||
"axios": "^0.19.2", |
||||
"boxen": "^4.2.0", |
||||
"chalk-pipe": "^4.0.0", |
||||
"cli-table3": "^0.6.0", |
||||
"colors": "^1.4.0", |
||||
"download": "^8.0.0", |
||||
"download-git-repo": "^3.0.2", |
||||
"express": "^4.17.1", |
||||
"fs-extra": "^9.0.1", |
||||
"glob": "^7.1.6", |
||||
"inquirer": "^7.3.3", |
||||
"jsonfile": "^6.0.1", |
||||
"knex": "^0.21.5", |
||||
"linux-os-info": "^2.0.0", |
||||
"mkdirp": "^1.0.4", |
||||
"mssql": "^6.2.1", |
||||
"mysql": "^2.18.1", |
||||
"open": "^7.1.0", |
||||
"ora": "^4.0.5", |
||||
"os-locale": "^5.0.0", |
||||
"pg": "^8.3.2", |
||||
"sha.js": "^2.4.11", |
||||
"shelljs": "^0.8.4", |
||||
"tcp-port-used": "^1.0.1", |
||||
"unique-names-generator": "^4.5.0", |
||||
"update-notifier": "^4.1.0", |
||||
"uuid": "^8.3.1", |
||||
"yargs": "^15.4.1" |
||||
}, |
||||
"devDependencies": { |
||||
"@bitjson/npm-scripts-info": "^1.0.0", |
||||
"@bitjson/typedoc": "^0.15.0-0", |
||||
"@istanbuljs/nyc-config-typescript": "^0.1.3", |
||||
"ava": "2.2.0", |
||||
"codecov": "^3.5.0", |
||||
"copy-webpack-plugin": "^7.0.0", |
||||
"cz-conventional-changelog": "^2.1.0", |
||||
"gh-pages": "^2.0.1", |
||||
"javascript-obfuscator": "^2.6.0", |
||||
"npm-run-all": "^4.1.5", |
||||
"nyc": "^14.1.1", |
||||
"open-cli": "^5.0.0", |
||||
"prettier": "^1.18.2", |
||||
"standard-version": "^6.0.1", |
||||
"trash-cli": "^3.0.0", |
||||
"ts-loader": "^8.0.5", |
||||
"ts-node": "^8.10.2", |
||||
"tslint": "^5.18.0", |
||||
"tslint-config-prettier": "^1.18.0", |
||||
"tslint-immutable": "^6.0.1", |
||||
"typescript": "^3.5.3", |
||||
"webpack": "^5.1.0", |
||||
"webpack-cli": "^4.0.0", |
||||
"webpack-node-externals": "^2.5.2", |
||||
"webpack-obfuscator": "^2.6.0" |
||||
}, |
||||
"ava": { |
||||
"failFast": true, |
||||
"files": [ |
||||
"build/main/**/*.spec.js" |
||||
], |
||||
"sources": [ |
||||
"build/main/**/*.js" |
||||
] |
||||
}, |
||||
"config": { |
||||
"commitizen": { |
||||
"path": "cz-conventional-changelog" |
||||
} |
||||
}, |
||||
"prettier": { |
||||
"singleQuote": true |
||||
}, |
||||
"nyc": { |
||||
"extends": "@istanbuljs/nyc-config-typescript", |
||||
"exclude": [ |
||||
"**/*.spec.js" |
||||
] |
||||
} |
||||
} |
@ -0,0 +1,74 @@
|
||||
// #! /usr/bin/env node
|
||||
|
||||
import CliMgr from './lib/CliMgr'; |
||||
import SocialMgr from './lib/mgr/SocialMgr'; |
||||
import Util from './lib/util/Util'; |
||||
|
||||
import updateNotifier from 'update-notifier'; |
||||
|
||||
|
||||
// tslint:disable-next-line:no-var-requires
|
||||
const pkg = require('../package.json'); |
||||
import yargs from 'yargs'; |
||||
|
||||
const {uniqueNamesGenerator, starWars, adjectives, animals} = require('unique-names-generator'); |
||||
|
||||
|
||||
/* get cli args */ |
||||
const args = yargs |
||||
.alias('u', 'url') |
||||
.alias('m', 'module') |
||||
.alias('n', 'nomodel') |
||||
.help('help') |
||||
.argv; |
||||
|
||||
/* cwd is reference to all commands */ |
||||
args.folder = process.cwd(); |
||||
|
||||
/* handle command */ |
||||
(async () => { |
||||
|
||||
// Checks for available update and returns an instance
|
||||
const notifier = updateNotifier({pkg}); |
||||
|
||||
// Notify using the built-in convenience method
|
||||
notifier.notify(); |
||||
|
||||
if (args._) { |
||||
if (!args._.length) { |
||||
await SocialMgr.showPrompt(); |
||||
args._.unshift(uniqueNamesGenerator({ |
||||
dictionaries: [[starWars], [adjectives, animals]][Math.floor(Math.random() * 2)] |
||||
}).toLowerCase().replace(/[ -]/g, '_')); |
||||
} |
||||
await CliMgr.process(args); |
||||
} else { |
||||
Util.showHelp(args); |
||||
process.exit(0) |
||||
} |
||||
})().catch(err => { |
||||
console.error('\n\nThere was an error processing command:'); |
||||
console.error(err); |
||||
}); |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* 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/>. |
||||
* |
||||
*/ |
@ -0,0 +1,451 @@
|
||||
import Util from './util/Util'; |
||||
|
||||
import('colors'); |
||||
import shell from 'shelljs'; |
||||
// import AppMgr from './mgr/AppMgr';
|
||||
import client from './mgr/Client'; |
||||
// import ComponentMgr from './mgr/ComponentMgr';
|
||||
// import MigrationMgr from './mgr/MigrationMgr';
|
||||
// import ModelMgr from './mgr/ModelMgr';
|
||||
// import OldNewMgr from './mgr/NewMgr';
|
||||
// import InstantMgr from './mgr/InstantMgr';
|
||||
// import PermissionsMgr from './mgr/PermissionsMgr';
|
||||
// import SocialMgr from './mgr/SocialMgr';
|
||||
// import DockerMgr from "./mgr/DockerMgr";
|
||||
import NewMgr from "./mgr/NewMgr"; |
||||
// import TryMgr from "./mgr/TryMgr";
|
||||
|
||||
let cmdProcessedOnce = 0; |
||||
let cmdOriginalArgs = null; |
||||
|
||||
class CliMgr { |
||||
|
||||
public static async runCmd(str) { |
||||
shell.echo(`\nNow, executing command : ${str}\n\n`.blue); |
||||
if (shell.exec(str).code !== 0) { |
||||
shell.echo(`\n\nError running command internally\n\n\t"${str}"`.red); |
||||
shell.echo(`\nExiting...`.red); |
||||
shell.exit(1); |
||||
} |
||||
} |
||||
|
||||
public static async process(args) { |
||||
|
||||
cmdOriginalArgs = cmdOriginalArgs ? cmdOriginalArgs : args; |
||||
|
||||
try { |
||||
if (args._ && args._.length) { |
||||
switch (args._[0]) { |
||||
|
||||
case 'man': |
||||
if (args._.length > 1) { |
||||
Util.showHelpForCommand(args); |
||||
} else { |
||||
Util.showHelp(args); |
||||
} |
||||
|
||||
process.exit(0); |
||||
break; |
||||
|
||||
/**************** START : new project with apis creation ****************/ |
||||
case 'n': |
||||
case 'new': |
||||
if (await NewMgr.getNewProjectInput(args)) { |
||||
if (args._[0] === 'gap') { |
||||
console.log('gRPC code generation is not yet supported.'); |
||||
process.exit(0) |
||||
} |
||||
await client.requestSend(args) |
||||
} else { |
||||
process.exit(0) |
||||
} |
||||
|
||||
break; |
||||
|
||||
/**************** END : new project with apis creation ****************/ |
||||
// /**************** START : new project with apis creation ****************/
|
||||
// case 'nold':
|
||||
// case 'newold':
|
||||
// if (await NewMgr.getNewProjectInputOld(args)) {
|
||||
// if (args._[0] === 'gap') {
|
||||
// console.log('gRPC code generation is not yet supported.');
|
||||
// process.exit(0)
|
||||
// }
|
||||
// await client.requestSend(args)
|
||||
// } else {
|
||||
// process.exit(0)
|
||||
// }
|
||||
//
|
||||
// break;
|
||||
//
|
||||
// /**************** END : new project with apis creation ****************/
|
||||
// /**************** START : try xc-instant with in-memory ****************/
|
||||
// case 't':
|
||||
// case 'try':
|
||||
// await TryMgr.getProjectInput(args);
|
||||
// process.exit(0)
|
||||
// break;
|
||||
//
|
||||
// case 'sg':
|
||||
// case 'sample.gql':
|
||||
// case 'sample.graphql':
|
||||
// await TryMgr.getProjectInput({_: ['sample', 'gql']});
|
||||
// process.exit(0)
|
||||
// break;
|
||||
//
|
||||
// case 'sr':
|
||||
// case 'sample.rest':
|
||||
// case 'sample':
|
||||
// case 's':
|
||||
// await TryMgr.getProjectInput({_: ['sample', 'rest']});
|
||||
// process.exit(0)
|
||||
// break;
|
||||
//
|
||||
// /**************** END : try xc-instant with in-memory ****************/
|
||||
//
|
||||
// /**************** START : new project with apis creation ****************/
|
||||
// // case 'nold':
|
||||
// // case 'newold':
|
||||
// // if (await OldNewMgr.getNewProjectInput(args)) {
|
||||
// // if (args._[0] === 'gap') {
|
||||
// // console.log('gRPC code generation is not yet supported.');
|
||||
// // process.exit(0)
|
||||
// // }
|
||||
// // await client.requestSend(args)
|
||||
// // } else {
|
||||
// // process.exit(0)
|
||||
// // }
|
||||
// //
|
||||
// // break;
|
||||
//
|
||||
// /**************** END : new project with apis creation ****************/
|
||||
//
|
||||
//
|
||||
// /**************** START : new project with apis creation ****************/
|
||||
// case 'd':
|
||||
// case 'docker':
|
||||
// await DockerMgr.genDockerfile(args)
|
||||
// break;
|
||||
//
|
||||
// /**************** END : new project with apis creation ****************/
|
||||
//
|
||||
// //
|
||||
// // /**************** START : instant apis creation ****************/
|
||||
// // case 'i':
|
||||
// // case 'instant':
|
||||
// // await InstantMgr.getNewProjectInput(args);
|
||||
// // break;
|
||||
// //
|
||||
// // /**************** END : instant apis creation ****************/
|
||||
// /**************** START : Init xc instant project ****************/
|
||||
// case 'i':
|
||||
// case 'init':
|
||||
// await NewMgr.initProject(args);
|
||||
// break;
|
||||
//
|
||||
// /**************** END : Init xc instant project ****************/
|
||||
//
|
||||
// /**************** START : apis creation ****************/
|
||||
// case 'ga':
|
||||
// case 'gar':
|
||||
// case 'gen.apis':
|
||||
// case 'gen.apis.rest':
|
||||
// // console.log(`xc : Generating REST APIs`.green, args);
|
||||
// await client.requestSend(args);
|
||||
// break;
|
||||
//
|
||||
// case 'gag':
|
||||
// case 'gen.apis.graphql':
|
||||
// case 'gen.apis.gql':
|
||||
// // console.log(`xc : Generating GraphQL APIs`.green);
|
||||
// await client.requestSend(args);
|
||||
// break;
|
||||
// /**************** END : apis creation ****************/
|
||||
//
|
||||
//
|
||||
// case 'gm':
|
||||
// case 'gen.module':
|
||||
// await client.requestSend(args);
|
||||
// break;
|
||||
//
|
||||
// /**************** START : rest components scaffolding ****************/
|
||||
// case 'gmr':
|
||||
// case 'gen.module.router':
|
||||
// await client.requestSend(args);
|
||||
// break;
|
||||
//
|
||||
// case 'gmm':
|
||||
// case 'gen.module.middleware':
|
||||
// await client.requestSend(args);
|
||||
// break;
|
||||
//
|
||||
// case 'gms':
|
||||
// case 'gen.module.service':
|
||||
// await client.requestSend(args);
|
||||
// break;
|
||||
// /**************** END : rest components scaffolding ****************/
|
||||
//
|
||||
//
|
||||
// /**************** START : graphql components scaffolding ****************/
|
||||
// case 'ggm':
|
||||
// case 'gen.gql.module':
|
||||
// await client.requestSend(args);
|
||||
// break;
|
||||
//
|
||||
// case 'ggmr':
|
||||
// case 'gen.gql.module.resolver':
|
||||
// await client.requestSend(args);
|
||||
// break;
|
||||
//
|
||||
// case 'ggmm':
|
||||
// case 'gen.gql.module.middleware':
|
||||
// await client.requestSend(args);
|
||||
// break;
|
||||
//
|
||||
// case 'ggms':
|
||||
// case 'gen.gql.module.service':
|
||||
// await client.requestSend(args);
|
||||
// break;
|
||||
// /**************** END : graphql components scaffolding ****************/
|
||||
//
|
||||
//
|
||||
// /**************** START : Migration stuff ****************/
|
||||
// case 'db.migrate.init' :
|
||||
// case 'dmi' :
|
||||
// console.log('migration init', args);
|
||||
// await MigrationMgr.init(args);
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// case 'db.migrate.sync' :
|
||||
// case 'dms' :
|
||||
// console.log('migration sync', args);
|
||||
// await MigrationMgr.sync(args);
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// case 'db.migrate.list' :
|
||||
// case 'dml' :
|
||||
// console.log('migration list');
|
||||
// await MigrationMgr.list(args);
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// case 'db.migrate.create' :
|
||||
// case 'dmc' :
|
||||
// console.log('migration create', args);
|
||||
// await MigrationMgr.create(args);
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// case 'db.migrate.up' :
|
||||
// case 'dmu' :
|
||||
// console.log('migration create', args);
|
||||
// await MigrationMgr.up(args);
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// case 'db.migrate.down' :
|
||||
// case 'dmd' :
|
||||
// console.log('migration down', args);
|
||||
// await MigrationMgr.down(args);
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// case 'db.migrate.term' :
|
||||
// case 'dmt' :
|
||||
// console.log('migration init', args);
|
||||
// await MigrationMgr.clean(args);
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// case 'db.migrate.sql.dump' :
|
||||
// case 'dmsd' :
|
||||
// console.log('migration meta dump', args);
|
||||
// await client.requestSend(args);
|
||||
// break;
|
||||
// /**************** END : Migration stuff ****************/
|
||||
//
|
||||
//
|
||||
// /**************** START : Meta stuff ****************/
|
||||
//
|
||||
// case 'meta.export' :
|
||||
// case 'me' :
|
||||
// console.log('meta export', args);
|
||||
// if (!('env' in args)) {
|
||||
// console.log(`Missing '${'env'.bold}' parameter`.red);
|
||||
// process.exit(0);
|
||||
// }
|
||||
//
|
||||
// await client.requestSend(args);
|
||||
// break;
|
||||
// case 'meta.import' :
|
||||
// case 'mi' :
|
||||
// console.log('meta import', args)
|
||||
// if (!('env' in args)) {
|
||||
// console.log(`Missing '${'env'.bold}' parameter`.red);
|
||||
// process.exit(0);
|
||||
// }
|
||||
//
|
||||
// await client.requestSend(args);
|
||||
// break;
|
||||
// case 'meta.reset' :
|
||||
// case 'mr' :
|
||||
// console.log('met reset', args)
|
||||
// if (!('env' in args)) {
|
||||
// console.log(`Missing '${'env'.bold}' parameter`.red);
|
||||
// process.exit(0);
|
||||
// }
|
||||
//
|
||||
// await client.requestSend(args);
|
||||
// break;
|
||||
// /**************** END : Meta stuff ****************/
|
||||
//
|
||||
//
|
||||
// /**************** START : ACL stuff ****************/
|
||||
// case 'permissions.set':
|
||||
// case 'ps':
|
||||
// await PermissionsMgr.set(args)
|
||||
// process.exit(0);
|
||||
// break;
|
||||
// case 'permissions.get':
|
||||
// case 'pg':
|
||||
// await PermissionsMgr.get(args)
|
||||
// process.exit(0);
|
||||
// break;
|
||||
// case 'permissions.role.add':
|
||||
// case 'pra':
|
||||
// await PermissionsMgr.userAdd(args)
|
||||
// process.exit(0);
|
||||
// break;
|
||||
// case 'permissions.role.delete':
|
||||
// case 'prd':
|
||||
// await PermissionsMgr.userDelete(args)
|
||||
// process.exit(0);
|
||||
// break;
|
||||
// case 'permissions.role.rename':
|
||||
// case 'prr':
|
||||
// await PermissionsMgr.userRename(args)
|
||||
// process.exit(0);
|
||||
// break;
|
||||
// /**************** END : ACL stuff ****************/
|
||||
//
|
||||
// /**************** START : App stuff ****************/
|
||||
// case 'app.install':
|
||||
// case 'ai':
|
||||
// await AppMgr.install(args);
|
||||
// process.exit(0);
|
||||
// break;
|
||||
// case 'app.open':
|
||||
// case 'ao':
|
||||
// await AppMgr.open(args);
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// /**************** END : App stuff ****************/
|
||||
//
|
||||
// /**************** START : Component stuff ****************/
|
||||
// case 'component.add':
|
||||
// case 'ca':
|
||||
// const prevComponentName = await ComponentMgr.add(args);
|
||||
// if (prevComponentName) {
|
||||
// args._.push(prevComponentName)
|
||||
// await client.requestSend(args);
|
||||
// } else {
|
||||
// process.exit(0)
|
||||
// }
|
||||
// break;
|
||||
//
|
||||
// case 'component.rename':
|
||||
// case 'cr':
|
||||
// await ComponentMgr.rename(args);
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// case 'component.delete':
|
||||
// case 'cd':
|
||||
// await ComponentMgr.add(args);
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// /**************** END : Component stuff ****************/
|
||||
//
|
||||
//
|
||||
// /**************** START : Social media stuff ****************/
|
||||
// case 'share':
|
||||
// await SocialMgr.shareSocial(args);
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// case 'noshare':
|
||||
// await SocialMgr.setShareRules('dontPrompt', true);
|
||||
// process.exit(0);
|
||||
// break;
|
||||
// /**************** END : Social media stuff ****************/
|
||||
//
|
||||
// /**************** START : Remove stuff ****************/
|
||||
// case 'remove.model.backup':
|
||||
// case 'rmb':
|
||||
// await ModelMgr.removeModelBackups(args);
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// /**************** END : Remove stuff ****************/
|
||||
//
|
||||
// // case 'menu':
|
||||
// // case 'm':
|
||||
// // await Menu.prepareCmd(client, args);
|
||||
// // // console.log(args);
|
||||
// // await this.process(client, args);
|
||||
// // break;
|
||||
//
|
||||
// case 'yelp':
|
||||
// case 'y':
|
||||
// Util.showHelp(args);
|
||||
// process.exit(0)
|
||||
// break;
|
||||
|
||||
|
||||
default: |
||||
if (!cmdProcessedOnce) { |
||||
cmdProcessedOnce = 1; |
||||
// await this.process(Util.getShortVersion(args));
|
||||
args._.unshift('new'); |
||||
await this.process(args); |
||||
} else { |
||||
console.log(`\nUnknown command. ${cmdOriginalArgs._[0]} -- please see help below`); |
||||
Util.showHelp(cmdOriginalArgs); |
||||
process.exit(0) |
||||
} |
||||
break; |
||||
} |
||||
} |
||||
} catch (e) { |
||||
throw e; |
||||
} |
||||
} |
||||
} |
||||
|
||||
export default CliMgr; |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* 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/>. |
||||
* |
||||
*/ |
@ -0,0 +1,170 @@
|
||||
import axios from 'axios'; |
||||
import download from 'download'; |
||||
import osInfo from 'linux-os-info'; |
||||
import open from 'open'; |
||||
import ora from 'ora'; |
||||
import os from 'os'; |
||||
import shell from 'shelljs'; |
||||
|
||||
import('colors'); |
||||
|
||||
const PROGRESS_WIDTH = 30; |
||||
|
||||
class AppMgr { |
||||
|
||||
public static async install(args) { |
||||
try { |
||||
const spinner = ora({text: 'Downloading Desktop App from Github..'.green.bold(), spinner: 'dots2', color: 'green'}).start(); |
||||
const {src, dest} = await AppMgr.getDownloadLink(args); |
||||
|
||||
console.log(`\t${src}`); |
||||
|
||||
await download(src, '.').on('downloadProgress', progress => { |
||||
// console.log(progress)
|
||||
// Report download progress
|
||||
const p = PROGRESS_WIDTH * progress.percent; |
||||
spinner.text = `Downloading Desktop App now..\n[${ |
||||
Array.from({length: PROGRESS_WIDTH}, (_, i) => i <= p ? '=' : ' ').join('') |
||||
}] ${(progress.transferred / (1024 * 1024)).toFixed(2)}MB/${(progress.total / (1024 * 1024)).toFixed(2)}MB\n`.green.bold()
|
||||
}); |
||||
// spinner.prefixText = '';
|
||||
spinner.succeed(`Installable downloaded successfully at ./${dest}`.green.bold()); |
||||
console.log(`\nInstallable will open automatically now.`.green.bold); |
||||
console.log(`If not, please install it manually.`.green.bold); |
||||
if (os.type() === 'Windows_NT') { |
||||
// open(dest, {wait: true, app: 'explorer.exe'})
|
||||
} else { |
||||
open(dest, {wait: true}); |
||||
} |
||||
|
||||
} catch (e) { |
||||
console.error(`Error in xc app.install`, e); |
||||
} |
||||
} |
||||
|
||||
public static async open(args) { |
||||
try { |
||||
const runCommand = AppMgr.getOpenCommand(args); |
||||
if (!runCommand) { return } |
||||
if (shell.exec(runCommand).code !== 0) { |
||||
shell.echo(`\n\nError running command internally`.red); |
||||
shell.echo(`\nExiting...`.red); |
||||
shell.exit(1); |
||||
} |
||||
} catch (e) { |
||||
console.error(`Error in xc app.open`, e); |
||||
} |
||||
} |
||||
|
||||
public static async getDownloadLink(args):Promise<any> { |
||||
try { |
||||
let src; |
||||
let dest; |
||||
const urls:any = {}; |
||||
|
||||
const res = await axios.get('https://api.github.com/repos/xgenecloud/xc-desktop-app/releases?page=1') |
||||
|
||||
let status = 0; |
||||
|
||||
for (let i = 0; i < res.data.length && status !== 15; i++) { |
||||
const assets = res.data[i].assets; |
||||
for (const {name, browser_download_url} of assets) { |
||||
switch (name.split('.').pop().toLowerCase()) { |
||||
case 'dmg': |
||||
urls.dmg = urls.dmg || browser_download_url; |
||||
status = status | 1; |
||||
break; |
||||
case 'deb': |
||||
urls.deb = urls.deb || browser_download_url; |
||||
status = status | 2; |
||||
break; |
||||
case 'rpm': |
||||
urls.rpm = urls.rpm || browser_download_url; |
||||
status = status | 4; |
||||
break; |
||||
case 'exe': |
||||
urls.exe = urls.exe || browser_download_url; |
||||
status = status | 8; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
switch (os.type()) { |
||||
case 'Linux': |
||||
const linuxInfo = osInfo({mode: 'sync'}); |
||||
if (args.debian) { src = urls.deb; } |
||||
else if (args.rpm) { src = urls.rpm; } |
||||
else { |
||||
switch (linuxInfo.id) { |
||||
case 'ubuntu': |
||||
case 'raspberry': |
||||
src = urls.deb |
||||
break; |
||||
case 'fedora': |
||||
src = urls.rpm |
||||
break; |
||||
default: |
||||
src = urls.rpm |
||||
} |
||||
} |
||||
break; |
||||
case 'Darwin': |
||||
src = urls.dmg; |
||||
break; |
||||
case 'Windows_NT': |
||||
src = urls.exe; |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
|
||||
dest = src.split('/').pop(); |
||||
return {src, dest} |
||||
} catch (e) { |
||||
console.log(e) |
||||
} |
||||
} |
||||
|
||||
public static getOpenCommand(_args):any { |
||||
switch (os.type()) { |
||||
case 'Linux': |
||||
return 'xgenecloud' |
||||
break; |
||||
case 'Darwin': |
||||
return 'open -a xgenecloud' |
||||
break; |
||||
case 'Windows_NT': |
||||
console.info('Open xgenecloud desktop app from Windows start menu') |
||||
|
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
export default AppMgr; |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* 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/>. |
||||
* |
||||
*/ |
@ -0,0 +1,186 @@
|
||||
// #! /usr/bin/env node
|
||||
import boxen from 'boxen'; |
||||
import os from 'os'; |
||||
import path from 'path'; |
||||
import shell from 'shelljs'; |
||||
// import tcpPortUsed from 'tcp-port-used';
|
||||
// import promprRunOrDownload from '../prompts/RunOrDownload';
|
||||
// import config from '../util/config';
|
||||
// import {CmdSocketServer} from 'nc-help';
|
||||
// import SocialMgr from './SocialMgr';
|
||||
|
||||
|
||||
class Client { |
||||
|
||||
public static async requestSend(_args) { |
||||
|
||||
try { |
||||
|
||||
// await CmdSocketServer.cmdHandler(null, null, args);
|
||||
|
||||
|
||||
// const portOpen = await tcpPortUsed.check(config.port, 'localhost');
|
||||
|
||||
// if (portOpen) {
|
||||
//
|
||||
// // client.emit('__REQUEST__', args);
|
||||
// } else {
|
||||
// await promprRunOrDownload.handle(args)
|
||||
// process.exit(0);
|
||||
// }
|
||||
|
||||
} catch (e) { |
||||
console.log('Error generating code.', e.message); |
||||
throw e; |
||||
} |
||||
process.exit(0); |
||||
} |
||||
|
||||
public static async runCmd(str): Promise<any> { |
||||
|
||||
shell.echo(`\nNow, executing command : ${str}\n\n`.blue); |
||||
const code = shell.exec(str).code; |
||||
if (code !== 0) { |
||||
// shell.echo(`\n\nError running command internally\n\n\t"${str}" - ${code}`.red);
|
||||
// shell.echo(`\nExiting...`.red);
|
||||
shell.exit(1); |
||||
} |
||||
return 0 |
||||
|
||||
} |
||||
|
||||
public static async responseHandle(args) { |
||||
|
||||
try { |
||||
|
||||
switch (args._[0]) { |
||||
|
||||
case 'ga': |
||||
case 'gar': |
||||
case 'gen.apis': |
||||
case 'gen.apis.rest': |
||||
case 'gag': |
||||
case 'gen.apis.graphql': |
||||
case 'gen.apis.gql': |
||||
if (!args.err) { |
||||
// SocialMgr.setCreatedApis(true)
|
||||
if (os.type() === 'Windows_NT') { |
||||
console.log(boxen(`# APIs generated successfully\n\n# Please run the following commands\n${('cd ' + path.dirname(args.folder) + '; npm install ; npm run dev\n').green.bold}`, { |
||||
borderColor: 'green', |
||||
borderStyle: 'round', |
||||
margin: 1, |
||||
padding: 1 |
||||
} as any)); |
||||
} else { |
||||
await Client.runCmd(`npm install; npm run dev;`); |
||||
} |
||||
} |
||||
break; |
||||
|
||||
|
||||
case 'gc': |
||||
case 'gen.controller': |
||||
break; |
||||
|
||||
case 'gm': |
||||
case 'gen.middleware': |
||||
break; |
||||
|
||||
case 'gs': |
||||
case 'gen.service': |
||||
break; |
||||
|
||||
case 'ggt': |
||||
case 'gen.gql.type': |
||||
break; |
||||
|
||||
case 'gen.gql.schema': |
||||
break; |
||||
|
||||
case 'ggr': |
||||
case 'gen.resolver': |
||||
break; |
||||
|
||||
case 'gen.block': |
||||
break; |
||||
|
||||
case 'gen.cmd': |
||||
break; |
||||
|
||||
case 'ac': |
||||
case 'add.cmd': |
||||
break; |
||||
|
||||
case 'ae': |
||||
case 'add.env': |
||||
break; |
||||
|
||||
case 'ad': |
||||
case 'add.db': |
||||
break; |
||||
|
||||
case 'menu': |
||||
case 'm': |
||||
break; |
||||
|
||||
case 'db.migrate.sql.dump' : |
||||
case 'dmm' : |
||||
case 'dmmd' : |
||||
console.log('db.migrate.sql.dump finished') |
||||
break; |
||||
|
||||
default: |
||||
break; |
||||
} |
||||
} catch (e) { |
||||
throw e; |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
//
|
||||
// const client = require('socket.io-client')(`http://localhost:${config.port}`);
|
||||
//
|
||||
// client.on('__DISCONNECT__', function () {
|
||||
// console.log('Disconnected from desktop app - Restart XGENE application'.red)
|
||||
// });
|
||||
//
|
||||
// client.on('__CONNECT__', () => {
|
||||
// console.log('>>'.blue)
|
||||
// });
|
||||
//
|
||||
// client.on('__RESPONSE__', (data) => {
|
||||
// console.log(`\n\t${data}`.green);
|
||||
// });
|
||||
//
|
||||
// client.on('__FINISHED__', async (args) => {
|
||||
// console.log('\n<<'.blue);
|
||||
// await Client.responseHandle(args);
|
||||
// process.exit(0)
|
||||
// });
|
||||
//
|
||||
|
||||
export default Client; |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* 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/>. |
||||
* |
||||
*/ |
@ -0,0 +1,58 @@
|
||||
import inquirer from 'inquirer'; |
||||
import path from 'path'; |
||||
|
||||
class ComponentMgr { |
||||
|
||||
public static async add(_args) { |
||||
|
||||
const components = require(path.join(process.cwd(), './server/app.components.js')) |
||||
|
||||
const answer = await inquirer |
||||
.prompt([ |
||||
{ |
||||
choices: components.components.map(component => component.title), |
||||
message: 'Choose after which component should we insert the new component?', |
||||
name: 'compoonent', |
||||
type: 'list' |
||||
} |
||||
]) |
||||
|
||||
return answer.compoonent; |
||||
} |
||||
|
||||
// tslint:disable-next-line:no-empty
|
||||
public static async rename(_args) { |
||||
|
||||
} |
||||
|
||||
// tslint:disable-next-line:no-empty
|
||||
public static async delete(_args) { |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
export default ComponentMgr; |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* 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/>. |
||||
* |
||||
*/ |
@ -0,0 +1,65 @@
|
||||
import NewMgr from "./NewMgr"; |
||||
|
||||
import('colors'); |
||||
import fs from 'fs'; |
||||
import path from 'path'; |
||||
|
||||
|
||||
class DockerMgr extends NewMgr { |
||||
|
||||
public static async genDockerfile(args: any) { |
||||
const dockerFilePath = path.join(process.cwd(), 'Dockerfile'); |
||||
try { |
||||
args._.push('placeholder') |
||||
if(await this.getNewProjectInput(args)) { |
||||
const dockerfileContent = this.getDockerfileContent(args); |
||||
fs.writeFileSync(dockerFilePath, dockerfileContent); |
||||
console.log(`Docker file created successfully`.green.bold) |
||||
console.log(`\tFile location : ${dockerFilePath}`.green) |
||||
console.log(`\tdocker build . -t xc`.green.bold) |
||||
}else{ |
||||
// console.log('Database connection failed')
|
||||
} |
||||
} catch (e) { |
||||
console.log('Error in docker file creation', e); |
||||
} |
||||
} |
||||
|
||||
private static getDockerfileContent(args) { |
||||
return `FROM nocodb/nocodb:latest
|
||||
|
||||
ENV PORT 8080 |
||||
ENV NODE_ENV=dev |
||||
ENV TOOL_DIR=/tool |
||||
${ |
||||
args.url.map((url, i) => `ENV DB_URL${i > 0 ? i + 1 : ''}=${url}`).join('\r\n') |
||||
} |
||||
|
||||
` |
||||
} |
||||
|
||||
} |
||||
|
||||
export default DockerMgr; |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* 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/>. |
||||
* |
||||
*/ |
@ -0,0 +1,281 @@
|
||||
// import chalkPipe from 'chalk-pipe';
|
||||
// import inquirer from 'inquirer';
|
||||
// import mkdirp from 'mkdirp';
|
||||
// import path from 'path';
|
||||
// import URL from 'url';
|
||||
import express from 'express'; |
||||
import {XcApis, XcConfigFactory,} from "xc-instant"; |
||||
|
||||
import('colors'); |
||||
|
||||
// import tcpPortUsed from 'tcp-port-used';
|
||||
|
||||
// const dbDefaults = {
|
||||
// mysql: {
|
||||
// host: 'localhost',
|
||||
// port: '3306',
|
||||
// username: 'root',
|
||||
// password: '',
|
||||
// database: ''
|
||||
// },
|
||||
// pg: {
|
||||
// host: 'localhost',
|
||||
// port: '5432',
|
||||
// username: 'postgres',
|
||||
// password: '',
|
||||
// database: ''
|
||||
// },
|
||||
// mssql: {
|
||||
// host: 'localhost',
|
||||
// port: '1433',
|
||||
// username: 'sa',
|
||||
// password: '',
|
||||
// database: ''
|
||||
// },
|
||||
// sqlite3: {
|
||||
// host: 'localhost',
|
||||
// port: '1433',
|
||||
// username: 'sa',
|
||||
// password: '',
|
||||
// database: ''
|
||||
// },
|
||||
// };
|
||||
|
||||
class InstantMgr { |
||||
|
||||
public static async getNewProjectInput(args): Promise<any> { |
||||
|
||||
if (args._.length < 2) { |
||||
const usage = '\n$ xc instant "mysql://localhost:3306?u=root&p=password&d=database&a=rest"'.green.bold |
||||
console.log(`\n\nWarning! missing connection URL\n\nExample Usage:\n${usage}\n`.red.bold); |
||||
return false; |
||||
} |
||||
|
||||
|
||||
const config = XcConfigFactory.makeFromUrls(args._.slice(1)) |
||||
|
||||
const server = express(); |
||||
|
||||
server.set('view engine', 'ejs'); |
||||
const app = new XcApis(config) |
||||
|
||||
server.use(await app.init()); |
||||
|
||||
server.listen('8080', () => console.log('App started')); |
||||
|
||||
|
||||
// /* Construct database URL from prompt */
|
||||
// const dbTypes = Object.keys(dbDefaults);
|
||||
// args.url = []
|
||||
//
|
||||
// const answers = await inquirer
|
||||
// .prompt([
|
||||
// {
|
||||
// name: 'type',
|
||||
// type: 'list',
|
||||
// message: '🔥 Choose SQL Database type\t:',
|
||||
// choices: dbTypes.map(t => ({
|
||||
// name: t,
|
||||
// value: t,
|
||||
// short: t.green.bold
|
||||
// })),
|
||||
// default: 'mysql',
|
||||
// transformer (color) {
|
||||
// return chalkPipe(color)(color.green.bold);
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// name: 'host',
|
||||
// type: 'input',
|
||||
// message: '👉 Enter database host name\t:',
|
||||
// default(ans) {
|
||||
// return dbDefaults[ans.type].host
|
||||
// },
|
||||
// transformer (color) {
|
||||
// return chalkPipe(color)(color.green.bold);
|
||||
// },
|
||||
// when({type}) {
|
||||
// return type !== 'sqlite3'
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// name: 'port',
|
||||
// type: 'number',
|
||||
// message: '👉 Enter database port number\t:',
|
||||
// default(ans) {
|
||||
// return dbDefaults[ans.type].port
|
||||
// },
|
||||
// transformer (color) {
|
||||
// try {
|
||||
// return color.green.bold;
|
||||
// } catch (e) {
|
||||
// return color
|
||||
// }
|
||||
// },
|
||||
// validate (port, answers) {
|
||||
// let done = this.async();
|
||||
// NewMgr.isPortOpen(answers.host, port).then(isOpen => {
|
||||
// if (isOpen) {
|
||||
// done(null, true)
|
||||
// } else {
|
||||
// // done('Port is not open')
|
||||
// console.log(`\n\n😩 ${answers.host}:${port} is not open please start the database if you haven't\n`.red.bold)
|
||||
// process.exit(0);
|
||||
// }
|
||||
// }).catch(done)
|
||||
// },
|
||||
// when({type}) {
|
||||
// return type !== 'sqlite3'
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// name: 'username',
|
||||
// type: 'input',
|
||||
// message: '👉 Enter database username\t:',
|
||||
// default(ans) {
|
||||
// return dbDefaults[ans.type].username
|
||||
// },
|
||||
// transformer (color) {
|
||||
// return chalkPipe(color)(color.green.bold);
|
||||
// },
|
||||
// when({type}) {
|
||||
// return type !== 'sqlite3'
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// name: 'password',
|
||||
// type: 'input',
|
||||
// mask: true,
|
||||
// message: '🙈 Enter database password\t:',
|
||||
// transformer (color) {
|
||||
// return new Array(color.length).fill('*'.green.bold).join('')
|
||||
// },
|
||||
// when({type}) {
|
||||
// return type !== 'sqlite3'
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// name: 'database',
|
||||
// type: 'input',
|
||||
// default(_ans) {
|
||||
// return args._[1] + '_dev';
|
||||
// },
|
||||
// message: '👉 Enter database/schema name\t:',
|
||||
// transformer (color) {
|
||||
// return chalkPipe(color)(color.green.bold);
|
||||
// },
|
||||
// when({type}) {
|
||||
// return type !== 'sqlite3'
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// name: 'projectType',
|
||||
// type: 'list',
|
||||
// message: '🚀 Enter API type to generate\t:',
|
||||
// choices: ['REST APIs', 'GRAPHQL APIs'].map(t => ({
|
||||
// name: t,
|
||||
// value: t,
|
||||
// short: t.green.bold
|
||||
// })),
|
||||
// transformer (color) {
|
||||
// return chalkPipe(color)(color.green.bold);
|
||||
// },
|
||||
// when({type}) {
|
||||
// return type !== 'sqlite3'
|
||||
// }
|
||||
// }
|
||||
// ])
|
||||
//
|
||||
// // console.log(answers);
|
||||
//
|
||||
// if (answers.type === 'sqlite3') {
|
||||
// console.log('Please use desktop app to create Sqlite project'.green.bold)
|
||||
// process.exit(0);
|
||||
// }
|
||||
//
|
||||
// /* if not valid retry getting right input */
|
||||
// if (!answers.database) {
|
||||
// console.log('\n\tWarning! Database name can NOT be empty. Retry.\n '.red.bold);
|
||||
// this.getNewProjectInput(args);
|
||||
// }
|
||||
//
|
||||
// /* attach new project name to path and 'cd' to that folder */
|
||||
// args.folder = path.join(args.folder, args._[1]);
|
||||
// mkdirp.sync(args.folder);
|
||||
// process.chdir(args.folder);
|
||||
// // await Util.runCmd(`cd ${args.folder}`);
|
||||
//
|
||||
// /* prepare the args */
|
||||
// const url = `${answers.type}://${answers.host}:${answers.port}?u=${answers.username}&p=${answers.password}&d=${answers.database}`;
|
||||
// args._[0] = answers.projectType === 'REST APIs' ? 'gar' : 'gag';
|
||||
// args.url.push(url);
|
||||
// return NewMgr.testConnection(args)
|
||||
|
||||
} |
||||
|
||||
|
||||
// public static async testConnection({url}) {
|
||||
//
|
||||
// for (const u of url) {
|
||||
// const parsedUrlData = URL.parse(u, true);
|
||||
// const queryParams = parsedUrlData.query;
|
||||
// const client = parsedUrlData.protocol.slice(0, -1);
|
||||
// const config = {
|
||||
// client,
|
||||
// connection: {
|
||||
// host: parsedUrlData.hostname,
|
||||
// port: +parsedUrlData.port,
|
||||
// user: queryParams.u,
|
||||
// password: queryParams.p,
|
||||
// database: client === 'pg' ? 'postgres' : (client === 'mssql' ? undefined : null)
|
||||
// }
|
||||
// };
|
||||
//
|
||||
//
|
||||
// try {
|
||||
// const knex = require('knex')(config)
|
||||
// await knex.raw("SELECT 1+1 as data");
|
||||
// } catch (e) {
|
||||
// console.log(`\n😩 Test connection failed for : ${url}\n`.red.bold)
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
|
||||
|
||||
// public static async isPortOpen(host, port) {
|
||||
// try {
|
||||
// return await tcpPortUsed.check(+port, host)
|
||||
// } catch (e) {
|
||||
// console.log(e)
|
||||
// console.log(`\n😩 ${host}:${port} is not reachable please check\n`.red.bold)
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
|
||||
} |
||||
|
||||
export default InstantMgr; |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* 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/>. |
||||
* |
||||
*/ |
@ -0,0 +1,159 @@
|
||||
import {Migrator} from 'xc-migrator-ts'; |
||||
const migrator = new Migrator() |
||||
|
||||
class MigrationMgr { |
||||
|
||||
public static _getFolder(args) { |
||||
args.folder = args.folder || process.cwd(); |
||||
} |
||||
|
||||
public static async init(args) { |
||||
try { |
||||
args.type = args.type || "mysql"; |
||||
await migrator.init(args); |
||||
} catch (e) { |
||||
console.log('db.migrate.init : Error occured', e); |
||||
} |
||||
} |
||||
|
||||
public static async sync(args) { |
||||
try { |
||||
this._getFolder(args); |
||||
await migrator.sync(args); |
||||
} catch (e) { |
||||
console.log('db.migrate.sync : Error occured', e); |
||||
} |
||||
} |
||||
|
||||
public static async create(args) { |
||||
try { |
||||
this._getFolder(args) |
||||
await migrator.migrationsCreate({ |
||||
dbAlias: args.dbAlias || "primary", |
||||
folder: args.folder |
||||
}); |
||||
} catch (e) { |
||||
console.log('db.migrate.create : Error occured', e); |
||||
} |
||||
} |
||||
|
||||
public static async up(args) { |
||||
|
||||
try { |
||||
|
||||
this._getFolder(args) |
||||
|
||||
let migrationSteps = args.steps || 9999; |
||||
|
||||
if (args.file) { |
||||
migrationSteps = 0; |
||||
} |
||||
|
||||
const migratorInst = new Migrator() |
||||
await migratorInst.migrationsUp({ |
||||
dbAlias: args.dbAlias || "primary", |
||||
env: args.env || "dev", |
||||
file: args.file || null, |
||||
folder: args.folder, |
||||
migrationSteps, |
||||
onlyList: args.list, |
||||
sqlContentMigrate: (+args.sqlContentMigrate === 0 ? 0 : 1) |
||||
}); |
||||
|
||||
} catch (e) { |
||||
console.log('db.migrate.up : Error occured', e); |
||||
} |
||||
} |
||||
|
||||
public static async down(args) { |
||||
|
||||
try { |
||||
|
||||
this._getFolder(args) |
||||
|
||||
let migrationSteps = args.steps || 9999; |
||||
|
||||
if (args.file) { |
||||
migrationSteps = 0; |
||||
} |
||||
|
||||
await migrator.migrationsDown({ |
||||
dbAlias: args.dbAlias || "primary", |
||||
env: args.env || "dev", |
||||
file: args.file || null, |
||||
folder: args.folder, |
||||
migrationSteps, |
||||
onlyList: args.list, |
||||
sqlContentMigrate: (+args.sqlContentMigrate === 0 ? 0 : 1) |
||||
}); |
||||
|
||||
} catch (e) { |
||||
console.log('db.migrate.down : Error occured', e); |
||||
} |
||||
} |
||||
|
||||
public static async list(args) { |
||||
|
||||
try { |
||||
|
||||
this._getFolder(args) |
||||
|
||||
let migrationSteps = args.steps || 9999; |
||||
|
||||
if (args.file) { |
||||
migrationSteps = 0; |
||||
} |
||||
|
||||
const data = await migrator.migrationsUp({ |
||||
dbAlias: args.dbAlias || "primary", |
||||
env: args.env || "dev", |
||||
file: args.file || null, |
||||
folder: args.folder, |
||||
migrationSteps, |
||||
onlyList: true |
||||
}); |
||||
|
||||
console.log(data.data.object.list); |
||||
|
||||
} catch (e) { |
||||
console.log('db.migrate.up : Error occured', e); |
||||
} |
||||
|
||||
} |
||||
|
||||
public static async clean(args) { |
||||
try { |
||||
args.type = args.type || "mysql"; |
||||
await migrator.clean(args); |
||||
} catch (e) { |
||||
console.log('db.migrate.clean : Error occured', e); |
||||
} |
||||
} |
||||
|
||||
|
||||
} |
||||
|
||||
// expose class
|
||||
export default MigrationMgr; |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* 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/>. |
||||
* |
||||
*/ |
@ -0,0 +1,63 @@
|
||||
import fsExtra from 'fs-extra'; |
||||
import glob from 'glob'; |
||||
import path from 'path'; |
||||
|
||||
class ModelMgr { |
||||
|
||||
|
||||
/*** |
||||
* |
||||
* @param args |
||||
* @param args.models |
||||
* @param args.dbAlias |
||||
* @param args.folder |
||||
* @returns {Promise<void>} |
||||
*/ |
||||
public static async removeModelBackups(args) { |
||||
try { |
||||
args.dbAlias = args.dbAlias || '*'; |
||||
if (args.models) { |
||||
for (const model of args.models.split('.')) { |
||||
for (const file of glob.sync(path.join(args.folder, 'server', 'models', args.dbAlias,model, `${model}.meta.*.js`))) { |
||||
await fsExtra.remove(file) |
||||
console.log(`Removed successfully : ${path.basename(file)}`.green.bold) |
||||
} |
||||
} |
||||
} else { |
||||
for (const file of glob.sync(path.join(args.folder, 'server', 'models', args.dbAlias, '*', '*.meta.*.js'))) { |
||||
await fsExtra.remove(file) |
||||
console.log(`Removed successfully : ${path.basename(file)}`.green.bold) |
||||
} |
||||
} |
||||
}catch (e) { |
||||
console.log(`Error while removing backup file`.red.bold) |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
export default ModelMgr; |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* 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/>. |
||||
* |
||||
*/ |
@ -0,0 +1,739 @@
|
||||
import chalkPipe from 'chalk-pipe'; |
||||
import download from 'download-git-repo'; |
||||
import inquirer from 'inquirer'; |
||||
import mkdirp from 'mkdirp'; |
||||
import path from 'path'; |
||||
import tcpPortUsed from 'tcp-port-used'; |
||||
import URL from 'url'; |
||||
import {promisify} from "util"; |
||||
import Util from "../util/Util"; |
||||
|
||||
import {v4 as uuidv4} from 'uuid'; |
||||
import * as fs from "fs"; |
||||
import Lang, {STR} from "../util/Lang"; |
||||
import os from 'os'; |
||||
import boxen from "boxen"; |
||||
|
||||
// @ts-ignore
|
||||
const colors = require('colors'); |
||||
|
||||
const dbDefaults = { |
||||
sqlite3: { |
||||
host: 'localhost', |
||||
port: '1433', |
||||
username: 'sa', |
||||
password: '', |
||||
database: '' |
||||
}, |
||||
mysql2: { |
||||
host: 'localhost', |
||||
port: '3306', |
||||
username: 'root', |
||||
password: '', |
||||
database: '' |
||||
}, |
||||
pg: { |
||||
host: 'localhost', |
||||
port: '5432', |
||||
username: 'postgres', |
||||
password: '', |
||||
database: '' |
||||
}, |
||||
mssql: { |
||||
host: 'localhost', |
||||
port: '1433', |
||||
username: 'sa', |
||||
password: '', |
||||
database: '' |
||||
}, |
||||
}; |
||||
|
||||
// const apiTypeMapping = {
|
||||
// 'GRAPHQL APIs': 'graphql',
|
||||
// 'REST APIs': 'rest',
|
||||
// 'gRPC APIs': 'grpc'
|
||||
// }
|
||||
// const languageMapping = {
|
||||
// 'Javascript': 'js',
|
||||
// 'Typescript': 'ts',
|
||||
// }
|
||||
|
||||
|
||||
class NewMgr { |
||||
|
||||
/** |
||||
* |
||||
* Does the below : |
||||
* - Get database input and make a DB URL from it. |
||||
* - Create new folder and 'cd' to that folder. |
||||
* - Return true/success |
||||
* - Else failure |
||||
* |
||||
* @param args |
||||
* @returns {Promise<string|string|boolean|*>} |
||||
*/ |
||||
public static async getNewProjectInput(args): Promise<any> { |
||||
|
||||
if (args._.length < 2) { |
||||
const usage = '\n$ xc new project_name'.green.bold |
||||
console.log(`\n\nWarning! missing project name\n\nExample Usage:\n${usage}\n`.red.bold); |
||||
return false; |
||||
} |
||||
|
||||
/* Construct database URL from prompt */ |
||||
const dbTypes = Object.keys(dbDefaults); |
||||
args.url = [] |
||||
|
||||
console.log(`NocoDB requires a database to store metadata of database-spreadsheets.\nPlease enter the database credentials (defaults to SQLite3)`.green.bold) |
||||
const answers = await inquirer |
||||
.prompt([ |
||||
{ |
||||
name: 'type', |
||||
type: 'list', |
||||
message: Lang.getString(STR.DB_TYPE),// '🔥 Choose SQL Database type\t:',
|
||||
choices: dbTypes.map(t => ({ |
||||
name: t, |
||||
value: t, |
||||
short: t.green.bold |
||||
})), |
||||
default: 'sqlite3', |
||||
transformer(color) { |
||||
return chalkPipe(color)(color.green.bold); |
||||
} |
||||
}, |
||||
{ |
||||
name: 'host', |
||||
type: 'input', |
||||
message: Lang.getString(STR.DB_HOST),// '👉 Enter database host name\t:',
|
||||
default(ans) { |
||||
return dbDefaults[ans.type].host |
||||
}, |
||||
transformer(color) { |
||||
return chalkPipe(color)(color.green.bold); |
||||
}, |
||||
when({type}) { |
||||
return type !== 'sqlite3' |
||||
} |
||||
}, |
||||
{ |
||||
name: 'port', |
||||
type: 'number', |
||||
message: Lang.getString(STR.DB_PORT),// '👉 Enter database port number\t:',
|
||||
default(ans) { |
||||
return dbDefaults[ans.type].port |
||||
}, |
||||
transformer(color) { |
||||
try { |
||||
return color.green.bold; |
||||
} catch (e) { |
||||
return color |
||||
} |
||||
}, |
||||
validate(port, answers) { |
||||
const done = this.async(); |
||||
if (answers.host === 'host.docker.internal' || answers.host === 'docker.for.win.localhost') { |
||||
done(null, true) |
||||
} else { |
||||
NewMgr.isPortOpen(answers.host, port).then(isOpen => { |
||||
if (isOpen) { |
||||
done(null, true) |
||||
} else { |
||||
// done('Port is not open')
|
||||
console.log(`\n\n😩 ${answers.host}:${port} is not open please start the database if you haven't\n`.red.bold) |
||||
process.exit(0); |
||||
} |
||||
}).catch(done) |
||||
} |
||||
}, |
||||
when({type}) { |
||||
return type !== 'sqlite3' |
||||
} |
||||
}, |
||||
{ |
||||
name: 'username', |
||||
type: 'input', |
||||
message: Lang.getString(STR.DB_USER), // '👉 Enter database username\t:',
|
||||
default(ans) { |
||||
return dbDefaults[ans.type].username |
||||
}, |
||||
transformer(color) { |
||||
return chalkPipe(color)(color.green.bold); |
||||
}, |
||||
when({type}) { |
||||
return type !== 'sqlite3' |
||||
} |
||||
}, |
||||
{ |
||||
name: 'password', |
||||
type: 'input', |
||||
mask: true, |
||||
message: Lang.getString(STR.DB_PASSWORD), // '🙈 Enter database password\t:',
|
||||
transformer(color) { |
||||
return new Array(color.length).fill('*'.green.bold).join('') |
||||
}, |
||||
when({type}) { |
||||
return type !== 'sqlite3' |
||||
} |
||||
}, |
||||
{ |
||||
name: 'database', |
||||
type: 'input', |
||||
default(_ans) { |
||||
return args._[1] + '_dev'; |
||||
}, |
||||
message: Lang.getString(STR.DB_SCHEMA), // '👉 Enter database/schema name\t:',
|
||||
transformer(color) { |
||||
return chalkPipe(color)(color.green.bold); |
||||
}, |
||||
when({type}) { |
||||
return type !== 'sqlite3' |
||||
} |
||||
}, |
||||
|
||||
{ |
||||
name: 'projectType', |
||||
type: 'list', |
||||
message: Lang.getString(STR.PROJECT_TYPE), // '🚀 How do you want to run it\t:',
|
||||
choices: [ 'As Node.js Project','As Docker', //'Try XC Instant App (Without scaffolding code)'
|
||||
].map(t => ({ |
||||
name: t, |
||||
value: t, |
||||
short: t.green.bold |
||||
})), |
||||
transformer(color) { |
||||
return chalkPipe(color)(color.green.bold); |
||||
}, |
||||
}, |
||||
]) |
||||
|
||||
|
||||
/* if not valid retry getting right input */ |
||||
if (answers.type !== 'sqlite3' && !answers.database) { |
||||
console.log('\n\tWarning! Database name can NOT be empty. Retry.\n '.red.bold); |
||||
this.getNewProjectInput(args); |
||||
} |
||||
|
||||
switch (answers.projectType) { |
||||
case 'As Docker': |
||||
answers.projectType = 'docker'; |
||||
break; |
||||
case 'As Node.js Project': |
||||
answers.projectType = 'mvc'; |
||||
break; |
||||
default: |
||||
answers.projectType = 'docker'; |
||||
break; |
||||
} |
||||
|
||||
if (answers.projectType === 'mvc' && !answers.isForExisting) { |
||||
|
||||
/* attach new project name to path and 'cd' to that folder */ |
||||
args.folder = path.join(args.folder, args._[1]); |
||||
mkdirp.sync(args.folder); |
||||
process.chdir(args.folder); |
||||
await Util.runCmd(`cd ${args.folder}`); |
||||
await promisify(download)('direct:https://github.com/nocodb/nocodb-seed/archive/refs/heads/main.zip', args.folder); |
||||
|
||||
if (answers.type !== 'sqlite3') { |
||||
fs.appendFileSync(path.join(args.folder, '.env'), `NC_DB=${answers.type}://${answers.host}:${answers.port}?u=${answers.username}&p=${answers.password}&d=${answers.database}`) |
||||
} |
||||
|
||||
if (os.type() === 'Windows_NT') { |
||||
console.log(boxen(`# Project created successfully\n\n# Please run the following commands\n\n${('cd ' + args.folder + '\nnpm install \nnpm start\n').green.bold}`, { |
||||
borderColor: 'green', |
||||
borderStyle: 'round', |
||||
margin: 1, |
||||
padding: 1 |
||||
} as any)); |
||||
} else { |
||||
await Util.runCmd(`npm install; npm run start;`); |
||||
} |
||||
} else if (answers.projectType) { |
||||
let env = ''; |
||||
if (answers.type !== 'sqlite3') { |
||||
env = `--env NC_DB="${answers.type}://${answers.host}:${answers.port}?u=${answers.username}&p=${answers.password}&d=${answers.database}&t=${args._[1]}"` |
||||
} |
||||
const linuxHost = os.type() === 'Linux' ? '--net=host' : ''; |
||||
if (os.type() === 'Windows_NT') { |
||||
// tslint:disable-next-line:ban-comma-operator
|
||||
console.log(boxen(`# Please run the following docker commands\n\n${(`docker run -p 8080:8080 ${linuxHost} -d ${env} nocodb/nocodb:latest`).green.bold}\n`, { |
||||
borderColor: 'green', |
||||
borderStyle: 'round', |
||||
margin: 1, |
||||
padding: 1 |
||||
} as any)); |
||||
} else { |
||||
await Util.runCmd(`docker run -p 8080:8080 ${linuxHost} -d ${env} nocodb/nocodb:latest`); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
public static async getNewProjectInputOld(args): Promise<any> { |
||||
|
||||
if (args._.length < 2) { |
||||
const usage = '\n$ xc new project_name'.green.bold |
||||
console.log(`\n\nWarning! missing project name\n\nExample Usage:\n${usage}\n`.red.bold); |
||||
return false; |
||||
} |
||||
|
||||
/* Construct database URL from prompt */ |
||||
const dbTypes = Object.keys(dbDefaults); |
||||
args.url = [] |
||||
|
||||
const answers = await inquirer |
||||
.prompt([ |
||||
{ |
||||
name: 'type', |
||||
type: 'list', |
||||
message: Lang.getString(STR.DB_TYPE),// '🔥 Choose SQL Database type\t:',
|
||||
choices: dbTypes.map(t => ({ |
||||
name: t, |
||||
value: t, |
||||
short: t.green.bold |
||||
})), |
||||
default: 'mysql2', |
||||
transformer(color) { |
||||
return chalkPipe(color)(color.green.bold); |
||||
} |
||||
}, |
||||
{ |
||||
name: 'host', |
||||
type: 'input', |
||||
message: Lang.getString(STR.DB_HOST),// '👉 Enter database host name\t:',
|
||||
default(ans) { |
||||
return dbDefaults[ans.type].host |
||||
}, |
||||
transformer(color) { |
||||
return chalkPipe(color)(color.green.bold); |
||||
}, |
||||
when({type}) { |
||||
return type !== 'sqlite3' |
||||
} |
||||
}, |
||||
{ |
||||
name: 'port', |
||||
type: 'number', |
||||
message: Lang.getString(STR.DB_PORT),// '👉 Enter database port number\t:',
|
||||
default(ans) { |
||||
return dbDefaults[ans.type].port |
||||
}, |
||||
transformer(color) { |
||||
try { |
||||
return color.green.bold; |
||||
} catch (e) { |
||||
return color |
||||
} |
||||
}, |
||||
validate(port, answers) { |
||||
const done = this.async(); |
||||
if (answers.host === 'host.docker.internal' || answers.host === 'docker.for.win.localhost') { |
||||
done(null, true) |
||||
} else { |
||||
NewMgr.isPortOpen(answers.host, port).then(isOpen => { |
||||
if (isOpen) { |
||||
done(null, true) |
||||
} else { |
||||
// done('Port is not open')
|
||||
console.log(`\n\n😩 ${answers.host}:${port} is not open please start the database if you haven't\n`.red.bold) |
||||
process.exit(0); |
||||
} |
||||
}).catch(done) |
||||
} |
||||
}, |
||||
when({type}) { |
||||
return type !== 'sqlite3' |
||||
} |
||||
}, |
||||
{ |
||||
name: 'username', |
||||
type: 'input', |
||||
message: Lang.getString(STR.DB_USER), // '👉 Enter database username\t:',
|
||||
default(ans) { |
||||
return dbDefaults[ans.type].username |
||||
}, |
||||
transformer(color) { |
||||
return chalkPipe(color)(color.green.bold); |
||||
}, |
||||
when({type}) { |
||||
return type !== 'sqlite3' |
||||
} |
||||
}, |
||||
{ |
||||
name: 'password', |
||||
type: 'input', |
||||
mask: true, |
||||
message: Lang.getString(STR.DB_PASSWORD), // '🙈 Enter database password\t:',
|
||||
transformer(color) { |
||||
return new Array(color.length).fill('*'.green.bold).join('') |
||||
}, |
||||
when({type}) { |
||||
return type !== 'sqlite3' |
||||
} |
||||
}, |
||||
{ |
||||
name: 'database', |
||||
type: 'input', |
||||
default(_ans) { |
||||
return args._[1] + '_dev'; |
||||
}, |
||||
message: Lang.getString(STR.DB_SCHEMA), // '👉 Enter database/schema name\t:',
|
||||
transformer(color) { |
||||
return chalkPipe(color)(color.green.bold); |
||||
}, |
||||
when({type}) { |
||||
return type !== 'sqlite3' |
||||
} |
||||
}, |
||||
{ |
||||
name: 'apiType', |
||||
type: 'list', |
||||
message: Lang.getString(STR.DB_API), // '🚀 Enter API type to generate\t:',
|
||||
choices: ['REST APIs', 'GRAPHQL APIs', 'gRPC APIs'].map(t => ({ |
||||
name: t, |
||||
value: t, |
||||
short: t.green.bold |
||||
})), |
||||
transformer(color) { |
||||
return chalkPipe(color)(color.green.bold); |
||||
}, |
||||
when({type}) { |
||||
return type !== 'sqlite3' |
||||
} |
||||
}, |
||||
{ |
||||
name: 'projectType', |
||||
type: 'list', |
||||
message: Lang.getString(STR.PROJECT_TYPE), // '🚀 How do you want to run it\t:',
|
||||
choices: ['Docker', 'New Node.js project', 'Existing Node.js project', 'Try XC Instant App (Without scaffolding code)'].map(t => ({ |
||||
name: t, |
||||
value: t, |
||||
short: t.green.bold |
||||
})), |
||||
transformer(color) { |
||||
return chalkPipe(color)(color.green.bold); |
||||
}, |
||||
when({type}) { |
||||
return type !== 'sqlite3' |
||||
} |
||||
}, |
||||
// {
|
||||
// name: 'programmingLanguage',
|
||||
// type: 'list',
|
||||
// message: '🚀 Enter preferred programming language\t:',
|
||||
// choices: ['Javascript', 'Typescript'].map(t => ({
|
||||
// name: t,
|
||||
// value: t,
|
||||
// short: t.green.bold
|
||||
// })),
|
||||
// transformer(color) {
|
||||
// return chalkPipe(color)(color.green.bold);
|
||||
// },
|
||||
// when({type}) {
|
||||
// return type !== 'sqlite3'
|
||||
// }
|
||||
// }
|
||||
]) |
||||
|
||||
// console.log(answers);
|
||||
|
||||
if (answers.type === 'sqlite3') { |
||||
console.log('Please use desktop app to create Sqlite project'.green.bold) |
||||
process.exit(0); |
||||
} |
||||
|
||||
/* if not valid retry getting right input */ |
||||
if (!answers.database) { |
||||
console.log('\n\tWarning! Database name can NOT be empty. Retry.\n '.red.bold); |
||||
this.getNewProjectInput(args); |
||||
} |
||||
//
|
||||
|
||||
|
||||
/* prepare the args */ |
||||
// const url = `${answers.type}://${answers.host}:${answers.port}?u=${answers.username}&p=${answers.password}&d=${answers.database}&api=${apiTypeMapping[answers.projectType]}`;
|
||||
// args._[0] = answers.projectType === 'REST APIs' ? 'gar' : 'gag';
|
||||
switch (answers.apiType) { |
||||
case 'REST APIs': |
||||
answers.apiType = 'rest'; |
||||
break; |
||||
case 'GRAPHQL APIs': |
||||
answers.apiType = 'graphql'; |
||||
break; |
||||
case 'gRPC APIs': |
||||
answers.apiType = 'grpc'; |
||||
break; |
||||
default: |
||||
answers.apiType = 'rest'; |
||||
break; |
||||
} |
||||
|
||||
switch (answers.projectType) { |
||||
case 'Docker': |
||||
answers.projectType = 'docker'; |
||||
break; |
||||
case 'New Node.js project': |
||||
answers.projectType = 'mvc'; |
||||
break; |
||||
case 'Existing Node.js project': |
||||
answers.projectType = 'mvc'; |
||||
answers.isForExisting = true; |
||||
break; |
||||
default: |
||||
answers.projectType = 'docker'; |
||||
break; |
||||
} |
||||
|
||||
if (answers.projectType === 'mvc' && !answers.isForExisting) { |
||||
|
||||
/* attach new project name to path and 'cd' to that folder */ |
||||
args.folder = path.join(args.folder, args._[1]); |
||||
mkdirp.sync(args.folder); |
||||
process.chdir(args.folder); |
||||
await Util.runCmd(`cd ${args.folder}`); |
||||
|
||||
await promisify(download)('gitlab:xc-public/test10', args.folder); |
||||
const config = { |
||||
title: args._[1], |
||||
"version": "0.6", |
||||
"envs": { |
||||
"dev": { |
||||
"db": [ |
||||
{ |
||||
"client": answers.type, |
||||
"connection": { |
||||
"host": answers.host, |
||||
"port": answers.port, |
||||
"user": answers.username, |
||||
"password": answers.password, |
||||
"database": answers.database, |
||||
"multipleStatements": true |
||||
}, |
||||
"meta": { |
||||
"tn": "xc_evolutions", |
||||
"dbAlias": "db", |
||||
"api": { |
||||
"type": answers.apiType, |
||||
"prefix": "", |
||||
"graphqlDepthLimit": 10 |
||||
}, |
||||
"inflection": { |
||||
"tn": "none", |
||||
"cn": "none" |
||||
} |
||||
} |
||||
} |
||||
], |
||||
"apiClient": { |
||||
"data": [] |
||||
} |
||||
} |
||||
}, |
||||
"workingEnv": "dev", |
||||
"seedsFolder": "seeds", |
||||
"queriesFolder": "queries", |
||||
"apisFolder": "apis", |
||||
"projectType": answers.apiType, |
||||
"type": "docker", |
||||
"language": "ts", |
||||
"apiClient": { |
||||
"data": [] |
||||
}, |
||||
"auth": { |
||||
"jwt": { |
||||
"secret": uuidv4(), |
||||
"dbAlias": "db" |
||||
} |
||||
}, "meta": { |
||||
"db": { |
||||
"client": "sqlite3", |
||||
"connection": { |
||||
"filename": "xc.db" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
fs.writeFileSync(path.join(args.folder, 'config.xc.json'), JSON.stringify(config, null, 2)) |
||||
|
||||
await Util.runCmd(`npm install; npm run start;`); |
||||
|
||||
|
||||
} else if (answers.projectType === 'mvc') { |
||||
|
||||
console.log(` |
||||
1. Install our npm package using following command |
||||
|
||||
\t${`npm install --save nocodb`.green} |
||||
|
||||
2. Add the following code in your existing express project |
||||
|
||||
\t${`const {Noco} = require("nocodb");
|
||||
|
||||
\tnew Noco({ |
||||
title: "${args._[1]}", |
||||
"version": "0.6", |
||||
"envs": { |
||||
"dev": { |
||||
"db": [ |
||||
{ |
||||
"client": "${answers.type}", |
||||
"connection": { |
||||
"host": "${answers.host}", |
||||
"port": "${answers.port}", |
||||
"user": "${answers.username}", |
||||
"password": "${answers.password}", |
||||
"database": "${answers.database}", |
||||
"multipleStatements": true |
||||
}, |
||||
"meta": { |
||||
"tn": "xc_evolutions", |
||||
"dbAlias": "db", |
||||
"api": { |
||||
"type": "${answers.apiType}", |
||||
"prefix": "", |
||||
"graphqlDepthLimit": 10 |
||||
}, |
||||
"inflection": { |
||||
"tn": "none", |
||||
"cn": "none" |
||||
} |
||||
} |
||||
} |
||||
], |
||||
"apiClient": { |
||||
"data": [] |
||||
} |
||||
} |
||||
}, |
||||
"workingEnv": "dev", |
||||
"seedsFolder": "seeds", |
||||
"queriesFolder": "queries", |
||||
"apisFolder": "apis", |
||||
"projectType": "${answers.apiType}", |
||||
"type": "docker", |
||||
"language": "ts", |
||||
"apiClient": { |
||||
"data": [] |
||||
}, |
||||
"auth": { |
||||
"jwt": { |
||||
"secret": "${uuidv4()}", |
||||
"dbAlias": "db" |
||||
} |
||||
}, |
||||
"meta":{
|
||||
"db": { |
||||
"client": "sqlite3", |
||||
"connection": { |
||||
"filename": "xc.db" |
||||
} |
||||
} |
||||
} |
||||
}).init().then(mw => app.use(mw))`.green}
|
||||
|
||||
|
||||
|
||||
${`Note: ${'app'.bold} - refers to your express server instance`} |
||||
|
||||
|
||||
|
||||
`)
|
||||
|
||||
} else if (answers.projectType === 'docker') { |
||||
const dbUrl = `${answers.type}://${answers.host}:${answers.port}?u=${answers.username}&p=${answers.password}&d=${answers.database}&t=${args._[1]}`; |
||||
// console.log(`
|
||||
// You can create docker container using following command
|
||||
//
|
||||
// \t${`docker run -p 8080:8080 -p 8081:8081 -p 8082:8082 -d --name xc-instant --env DB_URL=${dbUrl} -d xgenecloud/xc:latest`.green}
|
||||
//
|
||||
//
|
||||
// Then visit http://localhost:8080/xc to access the Dashboard
|
||||
//
|
||||
// `)
|
||||
|
||||
const linuxHost = os.type() === 'Linux' ? '--net=host' : ''; |
||||
|
||||
|
||||
await Util.runCmd(`docker run -p 8080:8080 -p 8081:8081 -p 8082:8082 ${linuxHost} --name xc-instant --env NC_DB_URL="${dbUrl}" xgenecloud/xc:latest`); |
||||
|
||||
|
||||
} |
||||
// args.url.push(url);
|
||||
//
|
||||
// args.language = languageMapping[answers.programmingLanguage];
|
||||
|
||||
// return OldNewMgr.testConnection(args)
|
||||
|
||||
} |
||||
|
||||
|
||||
public static async testConnection({url}) { |
||||
|
||||
for (const u of url) { |
||||
const parsedUrlData = URL.parse(u, true); |
||||
const queryParams = parsedUrlData.query; |
||||
const client = parsedUrlData.protocol.slice(0, -1); |
||||
const config = { |
||||
client, |
||||
connection: { |
||||
host: parsedUrlData.hostname, |
||||
port: +parsedUrlData.port, |
||||
user: queryParams.u, |
||||
password: queryParams.p, |
||||
database: client === 'pg' ? 'postgres' : (client === 'mssql' ? undefined : null) |
||||
} |
||||
}; |
||||
|
||||
|
||||
try { |
||||
const knex = require('knex')(config) |
||||
await knex.raw("SELECT 1+1 as data"); |
||||
} catch (e) { |
||||
console.log(`\n😩 Test connection failed for : ${url}\n`.red.bold) |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
|
||||
public static async isPortOpen(host, port) { |
||||
try { |
||||
return await tcpPortUsed.check(+port, host) |
||||
} catch (e) { |
||||
console.log(e) |
||||
console.log(`\n😩 ${host}:${port} is not reachable please check\n`.red.bold) |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
public static async initProject(_args: any): Promise<void> { |
||||
await promisify(download)('gitlab:xc-public/test10', process.cwd()); |
||||
await Util.runCmd(`npm install; npm run start;`); |
||||
} |
||||
} |
||||
|
||||
export default NewMgr; |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* 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/>. |
||||
* |
||||
*/ |
@ -0,0 +1,321 @@
|
||||
import chalkPipe from 'chalk-pipe'; |
||||
import inquirer from 'inquirer'; |
||||
import mkdirp from 'mkdirp'; |
||||
import path from 'path'; |
||||
import URL from 'url'; |
||||
|
||||
import('colors'); |
||||
|
||||
let tcpPortUsed = require('tcp-port-used'); |
||||
|
||||
const dbDefaults = { |
||||
mysql: { |
||||
host: 'localhost', |
||||
port: '3306', |
||||
username: 'root', |
||||
password: '', |
||||
database: '' |
||||
}, |
||||
pg: { |
||||
host: 'localhost', |
||||
port: '5432', |
||||
username: 'postgres', |
||||
password: '', |
||||
database: '' |
||||
}, |
||||
mssql: { |
||||
host: 'localhost', |
||||
port: '1433', |
||||
username: 'sa', |
||||
password: '', |
||||
database: '' |
||||
}, |
||||
sqlite3: { |
||||
host: 'localhost', |
||||
port: '1433', |
||||
username: 'sa', |
||||
password: '', |
||||
database: '' |
||||
}, |
||||
}; |
||||
|
||||
const apiTypeMapping = { |
||||
'GRAPHQL APIs': 'graphql', |
||||
'REST APIs': 'rest', |
||||
'gRPC APIs': 'grpc' |
||||
} |
||||
const languageMapping = { |
||||
'Javascript': 'js', |
||||
'Typescript': 'ts', |
||||
} |
||||
|
||||
|
||||
class OldNewMgr { |
||||
|
||||
/** |
||||
* |
||||
* Does the below : |
||||
* - Get database input and make a DB URL from it. |
||||
* - Create new folder and 'cd' to that folder. |
||||
* - Return true/success |
||||
* - Else failure |
||||
* |
||||
* @param args |
||||
* @returns {Promise<string|string|boolean|*>} |
||||
*/ |
||||
public static async getNewProjectInput(args) { |
||||
|
||||
if (args._.length < 2) { |
||||
const usage = '\n$ xc new project_name'.green.bold |
||||
console.log(`\n\nWarning! missing project name\n\nExample Usage:\n${usage}\n`.red.bold); |
||||
return false; |
||||
} |
||||
|
||||
/* Construct database URL from prompt */ |
||||
const dbTypes = Object.keys(dbDefaults); |
||||
args.url = [] |
||||
|
||||
const answers = await inquirer |
||||
.prompt([ |
||||
{ |
||||
name: 'type', |
||||
type: 'list', |
||||
message: '🔥 Choose SQL Database type\t:', |
||||
choices: dbTypes.map(t => ({ |
||||
name: t, |
||||
value: t, |
||||
short: t.green.bold |
||||
})), |
||||
default: 'mysql', |
||||
transformer(color) { |
||||
return chalkPipe(color)(color.green.bold); |
||||
} |
||||
}, |
||||
{ |
||||
name: 'host', |
||||
type: 'input', |
||||
message: '👉 Enter database host name\t:', |
||||
default(ans) { |
||||
return dbDefaults[ans.type].host |
||||
}, |
||||
transformer(color) { |
||||
return chalkPipe(color)(color.green.bold); |
||||
}, |
||||
when({type}) { |
||||
return type !== 'sqlite3' |
||||
} |
||||
}, |
||||
{ |
||||
name: 'port', |
||||
type: 'number', |
||||
message: '👉 Enter database port number\t:', |
||||
default(ans) { |
||||
return dbDefaults[ans.type].port |
||||
}, |
||||
transformer(color) { |
||||
try { |
||||
return color.green.bold; |
||||
} catch (e) { |
||||
return color |
||||
} |
||||
}, |
||||
validate(port, answers) { |
||||
let done = this.async(); |
||||
OldNewMgr.isPortOpen(answers.host, port).then(isOpen => { |
||||
if (isOpen) { |
||||
done(null, true) |
||||
} else { |
||||
// done('Port is not open')
|
||||
console.log(`\n\n😩 ${answers.host}:${port} is not open please start the database if you haven't\n`.red.bold) |
||||
process.exit(0); |
||||
} |
||||
}).catch(done) |
||||
}, |
||||
when({type}) { |
||||
return type !== 'sqlite3' |
||||
} |
||||
}, |
||||
{ |
||||
name: 'username', |
||||
type: 'input', |
||||
message: '👉 Enter database username\t:', |
||||
default(ans) { |
||||
return dbDefaults[ans.type].username |
||||
}, |
||||
transformer(color) { |
||||
return chalkPipe(color)(color.green.bold); |
||||
}, |
||||
when({type}) { |
||||
return type !== 'sqlite3' |
||||
} |
||||
}, |
||||
{ |
||||
name: 'password', |
||||
type: 'input', |
||||
mask: true, |
||||
message: '🙈 Enter database password\t:', |
||||
transformer(color) { |
||||
return new Array(color.length).fill('*'.green.bold).join('') |
||||
}, |
||||
when({type}) { |
||||
return type !== 'sqlite3' |
||||
} |
||||
}, |
||||
{ |
||||
name: 'database', |
||||
type: 'input', |
||||
default(_ans) { |
||||
return args._[1] + '_dev'; |
||||
}, |
||||
message: '👉 Enter database/schema name\t:', |
||||
transformer(color) { |
||||
return chalkPipe(color)(color.green.bold); |
||||
}, |
||||
when({type}) { |
||||
return type !== 'sqlite3' |
||||
} |
||||
}, |
||||
{ |
||||
name: 'projectType', |
||||
type: 'list', |
||||
message: '🚀 Enter API type to generate\t:', |
||||
choices: ['REST APIs', 'GRAPHQL APIs'].map(t => ({ |
||||
name: t, |
||||
value: t, |
||||
short: t.green.bold |
||||
})), |
||||
transformer(color) { |
||||
return chalkPipe(color)(color.green.bold); |
||||
}, |
||||
when({type}) { |
||||
return type !== 'sqlite3' |
||||
} |
||||
}, |
||||
{ |
||||
name: 'programmingLanguage', |
||||
type: 'list', |
||||
message: '🚀 Enter preferred programming language\t:', |
||||
choices: ['Javascript', 'Typescript'].map(t => ({ |
||||
name: t, |
||||
value: t, |
||||
short: t.green.bold |
||||
})), |
||||
transformer(color) { |
||||
return chalkPipe(color)(color.green.bold); |
||||
}, |
||||
when({type}) { |
||||
return type !== 'sqlite3' |
||||
} |
||||
} |
||||
]) |
||||
|
||||
// console.log(answers);
|
||||
|
||||
if (answers.type === 'sqlite3') { |
||||
console.log('Please use desktop app to create Sqlite project'.green.bold) |
||||
process.exit(0); |
||||
} |
||||
|
||||
/* if not valid retry getting right input */ |
||||
if (!answers.database) { |
||||
console.log('\n\tWarning! Database name can NOT be empty. Retry.\n '.red.bold); |
||||
this.getNewProjectInput(args); |
||||
} |
||||
|
||||
/* attach new project name to path and 'cd' to that folder */ |
||||
args.folder = path.join(args.folder, args._[1]); |
||||
mkdirp.sync(args.folder); |
||||
process.chdir(args.folder); |
||||
// await Util.runCmd(`cd ${args.folder}`);
|
||||
|
||||
/* prepare the args */ |
||||
const url = `${answers.type}://${answers.host}:${answers.port}?u=${answers.username}&p=${answers.password}&d=${answers.database}&api=${apiTypeMapping[answers.projectType]}`; |
||||
args._[0] = answers.projectType === 'REST APIs' ? 'gar' : 'gag'; |
||||
switch (answers.projectType) { |
||||
case 'REST APIs': |
||||
args._[0] = 'gar'; |
||||
break; |
||||
case 'GRAPHQL APIs': |
||||
args._[0] = 'gag'; |
||||
break; |
||||
case 'gRPC APIs': |
||||
args._[0] = 'gap'; |
||||
break; |
||||
default: |
||||
args._[0] = 'gar'; |
||||
break; |
||||
} |
||||
args.url.push(url); |
||||
|
||||
args.language = languageMapping[answers.programmingLanguage]; |
||||
|
||||
return OldNewMgr.testConnection(args) |
||||
|
||||
} |
||||
|
||||
|
||||
public static async testConnection({url}) { |
||||
|
||||
for (const u of url) { |
||||
const parsedUrlData = URL.parse(u, true); |
||||
const queryParams = parsedUrlData.query; |
||||
const client = parsedUrlData.protocol.slice(0, -1); |
||||
const config = { |
||||
client, |
||||
connection: { |
||||
host: parsedUrlData.hostname, |
||||
port: +parsedUrlData.port, |
||||
user: queryParams.u, |
||||
password: queryParams.p, |
||||
database: client === 'pg' ? 'postgres' : (client === 'mssql' ? undefined : null) |
||||
} |
||||
}; |
||||
|
||||
|
||||
try { |
||||
const knex = require('knex')(config) |
||||
await knex.raw("SELECT 1+1 as data"); |
||||
} catch (e) { |
||||
console.log(`\n😩 Test connection failed for : ${url}\n`.red.bold) |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
|
||||
public static async isPortOpen(host, port) { |
||||
try { |
||||
return await tcpPortUsed.check(+port, host) |
||||
} catch (e) { |
||||
console.log(e) |
||||
console.log(`\n😩 ${host}:${port} is not reachable please check\n`.red.bold) |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
export default OldNewMgr; |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* 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/>. |
||||
* |
||||
*/ |
@ -0,0 +1,697 @@
|
||||
/* tslint:disable:prefer-const */ |
||||
import colors from 'colors'; |
||||
import fs from 'fs'; |
||||
import glob from 'glob'; |
||||
import jsonfile from 'jsonfile'; |
||||
import path from 'path'; |
||||
import Util from '../util/Util'; |
||||
|
||||
import Table from 'cli-table3'; |
||||
|
||||
class PermissionsMgr { |
||||
|
||||
public static async set(args) { |
||||
|
||||
if (Util.isProjectGraphql()) { |
||||
try { |
||||
|
||||
if (args._.length < 4) { |
||||
console.warn('Invalid arguments for : xc permissions.set') |
||||
return; |
||||
} |
||||
|
||||
|
||||
// @ts-ignore
|
||||
let [_, models, users, ...resolvers] = args._; |
||||
models = models.split('.'); |
||||
users = users.split('.'); |
||||
|
||||
/* get all policies */ |
||||
const policiesPath = path.join(process.cwd(), 'server', PermissionsMgr.getPolicyPath(), '**', '*.policy.js'); |
||||
|
||||
glob.sync(policiesPath).forEach((file) => { |
||||
|
||||
const modelName = path.basename(file).split('.')[0]; |
||||
|
||||
if (models.includes(modelName) || models[0] === '$') { |
||||
const filePermissions = require(file); |
||||
|
||||
const roles = this.extractUniqueGqlPolicyRoles(filePermissions); |
||||
|
||||
if (users[0] === '$') { |
||||
for (const [route, rolesObj] of Object.entries(filePermissions)) { |
||||
|
||||
if (resolvers[0] === '$=1') { |
||||
const permObj = roles.reduce((obj, role) => { |
||||
obj[role] = true; |
||||
return obj; |
||||
}, {}); |
||||
|
||||
Object.assign(rolesObj, permObj) |
||||
console.log(`Setting Permissions for model:${modelName} roles:${roles.join(', ')} resolver: ${route}`); |
||||
} else if (resolvers[0] === '$=0') { |
||||
const permObj = roles.reduce((obj, role) => { |
||||
obj[role] = false; |
||||
return obj; |
||||
}, {}); |
||||
|
||||
Object.assign(rolesObj, permObj) |
||||
console.log(`Setting Permissions for model:${modelName} roles:${roles.join(', ')} resolver: ${route}`); |
||||
} else { |
||||
resolvers.forEach(permission => { |
||||
const permTuple = permission.split('=') |
||||
if (route === permTuple[0]) { |
||||
const permObj = roles.reduce((obj, role) => { |
||||
const val = !!(permTuple.length === 1 ? 1 : +permTuple[1] || 0); |
||||
obj[role] = val; |
||||
return obj; |
||||
}, {}); |
||||
Object.assign(rolesObj, permObj) |
||||
console.log(`Setting Permissions for model:${modelName} roles:${roles.join(', ')} , resolver: ${route}`); |
||||
} |
||||
}) |
||||
} |
||||
|
||||
} |
||||
|
||||
} else { |
||||
|
||||
|
||||
for (const [route, rolesObj] of Object.entries(filePermissions)) { |
||||
resolvers.forEach(permission => { |
||||
const permTuple = permission.split('=') |
||||
if (route === permTuple[0] || permTuple[0] === '$') { |
||||
const permObj = users.reduce((obj, role) => { |
||||
const val = !!(permTuple.length === 1 ? 1 : +permTuple[1] || 0); |
||||
obj[role] = val; |
||||
return obj; |
||||
}, {}); |
||||
Object.assign(rolesObj, permObj) |
||||
console.log(`Setting Permissions for model:${modelName} roles:${users} , resolver: ${route}`); |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
|
||||
const policyFileContent = `module.exports = ${JSON.stringify(filePermissions, null, 2)};\n`; |
||||
fs.writeFileSync(file, policyFileContent) |
||||
|
||||
} |
||||
|
||||
}); |
||||
|
||||
} catch (e) { |
||||
console.error(`Error in xc permissions.set`, e); |
||||
} |
||||
} else { |
||||
try { |
||||
if (args._.length < 4) { |
||||
console.warn('Invalid arguments for : xc permissions.set') |
||||
return; |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
let [_, models, users, ...permissions] = args._; |
||||
|
||||
models = models.split('.'); |
||||
users = users.split('.'); |
||||
|
||||
/* get all policies */ |
||||
const policiesPath = path.join(process.cwd(), 'server', PermissionsMgr.getPolicyPath(), '**', '*.routes.js'); |
||||
|
||||
glob.sync(policiesPath).forEach((file) => { |
||||
|
||||
const modelName = path.basename(file).split('.')[0]; |
||||
if (models.includes(modelName) || models[0] === '$') { |
||||
|
||||
const routesList: Route[] = require(file); |
||||
const roles = this.extractUniqueRoles(routesList); |
||||
|
||||
if (users[0] === '$') { |
||||
for (const route of routesList) { |
||||
|
||||
if (permissions[0] === '$=1') { |
||||
const permObj = roles.reduce((obj, role) => { |
||||
obj[role] = true; |
||||
return obj; |
||||
}, {}); |
||||
Object.assign(route.acl, permObj) |
||||
console.log(`Setting Permissions for model:${modelName} roles:${roles.join(', ')} method: ${route.type}=true, route: ${route.path}`); |
||||
} else if (permissions[0] === '$=0') { |
||||
const permObj = roles.reduce((obj, role) => { |
||||
obj[role] = false; |
||||
return obj; |
||||
}, {}); |
||||
Object.assign(route.acl, permObj) |
||||
console.log(`Setting Permissions for model:${modelName} roles:${roles.join(', ')} method: ${route.type}=false, route: ${route.path}`); |
||||
} else { |
||||
permissions.forEach(permission => { |
||||
const permTuple = permission.split('=') |
||||
const val = !!(permTuple.length === 1 ? 1 : +permTuple[1] || 0); |
||||
const permObj = roles.reduce((obj, role) => { |
||||
obj[role] = val; |
||||
return obj; |
||||
}, {}); |
||||
if (route.type === permTuple[0]) { |
||||
Object.assign(route.acl, permObj) |
||||
console.log(`Setting Permissions for model:${modelName} roles:${roles.join(', ')} method: ${permTuple[0]}=${val}, route: ${route.path}`); |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
} else { |
||||
for (const route of routesList) { |
||||
permissions.forEach(permission => { |
||||
const permTuple = permission.split('=') |
||||
const val = !!(permTuple.length === 1 ? 1 : +permTuple[1] || 0); |
||||
const permObj = users.reduce((obj, role) => { |
||||
obj[role] = val; |
||||
return obj; |
||||
}, {}); |
||||
|
||||
if (route.type === permTuple[0]) { |
||||
Object.assign(route.acl, permObj) |
||||
console.log(`Setting Permissions for model:${modelName} roles:${users.join(', ')} method: ${permTuple[0]}=${val}, route: ${route.path}`); |
||||
} else if (permTuple[0] === '*') { |
||||
Object.assign(route.acl, permObj) |
||||
console.log(`Setting Permissions for model:${modelName} roles:${users.join(', ')} method: ${route.type}=${val}, route: ${route.path}`); |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
|
||||
const policyFileContent = `module.exports = ${JSON.stringify(routesList, null, 2)};\n`; |
||||
fs.writeFileSync(file, policyFileContent) |
||||
|
||||
} |
||||
}); |
||||
} catch (e) { |
||||
console.error(`Error in xc permissions.set`, e); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public static async get(args) { |
||||
|
||||
if (Util.isProjectGraphql()) { |
||||
try { |
||||
if (args._.length < 2) { |
||||
console.warn('Invalid arguments for : xc permissions.get') |
||||
return; |
||||
} |
||||
|
||||
let {1: models} = args._; |
||||
models = models.split('.'); |
||||
|
||||
/* get all policies */ |
||||
const policiesPath = path.join(process.cwd(), 'server', PermissionsMgr.getPolicyPath(), '**', '*.policy.js'); |
||||
|
||||
|
||||
// instantiate
|
||||
let rows: any[] = []; |
||||
let roles: any[] = []; |
||||
|
||||
|
||||
glob.sync(policiesPath).forEach((file) => { |
||||
// let filePermissions = require(file);
|
||||
|
||||
const modelName = path.basename(file).split('.')[0]; |
||||
|
||||
if (models.includes(modelName)) { |
||||
const filePermissions = require(file); |
||||
|
||||
|
||||
roles = this.extractUniqueGqlPolicyRoles(filePermissions); |
||||
|
||||
rows.push([{ |
||||
colSpan: roles.length + 1, |
||||
content: colors.green(file), |
||||
hAlign: 'center' |
||||
}]) |
||||
|
||||
for (const [route, methods] of Object.entries(filePermissions)) { |
||||
const row: any[] = [{content: route, vAlign: 'center'}]; |
||||
for (const role of roles) { |
||||
row.push(methods[role] ? colors.green('✔') : colors.red('x')); |
||||
} |
||||
rows.push(row) |
||||
} |
||||
} |
||||
}) |
||||
|
||||
|
||||
const table = new Table({ |
||||
head: ['Route', ...roles] |
||||
}); |
||||
|
||||
table.push(...rows); |
||||
console.log(table.toString()); |
||||
|
||||
} catch (e) { |
||||
console.error(`Error in xc permissions.get`, e); |
||||
} |
||||
} else { |
||||
|
||||
|
||||
try { |
||||
|
||||
if (args._.length < 2) { |
||||
console.warn('Invalid arguments for : xc permissions.get') |
||||
return; |
||||
} |
||||
|
||||
let {1: models} = args._; |
||||
models = models.split('.'); |
||||
|
||||
/* get all policies */ |
||||
const policiesPath = path.join(process.cwd(), 'server', PermissionsMgr.getPolicyPath(), '**', '*.routes.js'); |
||||
|
||||
|
||||
// instantiate
|
||||
const table = new Table({ |
||||
head: ['Route', 'Role', 'GET', 'POST', 'PUT', 'DELETE', 'PATCH'] |
||||
}); |
||||
|
||||
|
||||
glob.sync(policiesPath) |
||||
.sort(this.sortFiles) |
||||
.forEach((file) => { |
||||
const modelName = path.basename(file).split('.')[0]; |
||||
|
||||
if (models.includes(modelName)) { |
||||
const routesList: Route[] = require(file); |
||||
const groupedRoutes = this.groupRoutes(routesList); |
||||
|
||||
|
||||
// extract unique roles
|
||||
const roles = this.extractUniqueRoles(routesList); |
||||
|
||||
table.push([{ |
||||
colSpan: 7, |
||||
content: colors.green(file), |
||||
hAlign: 'center' |
||||
}]) |
||||
|
||||
|
||||
for (const [routePath, methods] of Object.entries(groupedRoutes)) { |
||||
let i = 0; |
||||
for (const role of roles) { |
||||
{ |
||||
table.push([...(i++ ? [] : [{ |
||||
content: routePath, |
||||
rowSpan: roles.length, |
||||
vAlign: 'center' |
||||
}]), role, |
||||
methods?.get?.acl?.[role] ? colors.green('✔') : colors.red('x'), |
||||
methods?.post?.acl?.[role] ? colors.green('✔') : colors.red('x'), |
||||
methods?.put?.acl?.[role] ? colors.green('✔') : colors.red('x'), |
||||
methods?.delete?.acl?.[role] ? colors.green('✔') : colors.red('x'), |
||||
methods?.patch?.acl?.[role] ? colors.green('✔') : colors.red('x'), |
||||
] as any) |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
}) |
||||
|
||||
console.log(table.toString()); |
||||
|
||||
|
||||
} catch (e) { |
||||
console.error(`Error in xc permissions.get`, e); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
public static async userAdd(args) { |
||||
|
||||
if (Util.isProjectGraphql()) { |
||||
|
||||
try { |
||||
if (args._.length < 2) { |
||||
console.warn('Invalid arguments for : xc permissions.userAdd') |
||||
return; |
||||
} |
||||
|
||||
const {1: user} = args._; |
||||
|
||||
/* get all policies */ |
||||
const policiesPath = path.join(process.cwd(), 'server', PermissionsMgr.getPolicyPath(), '**', '*.policy.js'); |
||||
|
||||
glob.sync(policiesPath).forEach((file) => { |
||||
|
||||
const modelName = path.basename(file).split('.')[0]; |
||||
|
||||
|
||||
const filePermissions = require(file); |
||||
|
||||
const roles = this.extractUniqueGqlPolicyRoles(filePermissions); |
||||
|
||||
|
||||
if (roles.includes(user)) { |
||||
console.warn(`${user} already exist in ${modelName} policy`); |
||||
return; |
||||
} |
||||
|
||||
for (const roles1 of Object.values(filePermissions)) { |
||||
roles1[user] = true; |
||||
} |
||||
|
||||
|
||||
console.log(`Adding new role permission for model:${modelName} roles:${user}`); |
||||
|
||||
|
||||
const policyFileContent = `module.exports = ${JSON.stringify(filePermissions, null, 2)};\n`; |
||||
fs.writeFileSync(file, policyFileContent) |
||||
|
||||
}); |
||||
|
||||
} catch (e) { |
||||
console.error(`Error in xc permissions.user.add`, e); |
||||
} |
||||
|
||||
} else { |
||||
|
||||
try { |
||||
if (args._.length < 2) { |
||||
console.warn('Invalid arguments for : xc permissions.userAdd') |
||||
return; |
||||
} |
||||
|
||||
const {1: user} = args._; |
||||
|
||||
/* get all policies */ |
||||
const policiesPath = path.join(process.cwd(), 'server', PermissionsMgr.getPolicyPath(), '**', '*.routes.js'); |
||||
|
||||
glob.sync(policiesPath).forEach((file) => { |
||||
|
||||
const modelName = path.basename(file).split('.')[0]; |
||||
|
||||
|
||||
const routes: Route[] = require(file); |
||||
|
||||
const roles = this.extractUniqueRoles(routes); |
||||
|
||||
|
||||
if (roles.includes(user)) { |
||||
console.warn(`${user} already exist in ${modelName} policy`); |
||||
return; |
||||
} |
||||
|
||||
for (const route of routes) { |
||||
route.acl[user] = true; |
||||
} |
||||
|
||||
|
||||
console.log(`Adding new role permission for model:${modelName} roles:${user}`); |
||||
|
||||
|
||||
const policyFileContent = `module.exports = ${JSON.stringify(routes, null, 2)};\n`; |
||||
fs.writeFileSync(file, policyFileContent) |
||||
|
||||
}); |
||||
|
||||
} catch (e) { |
||||
console.error(`Error in xc permissions.user.add`, e); |
||||
} |
||||
} |
||||
|
||||
|
||||
} |
||||
|
||||
|
||||
public static async userDelete(args) { |
||||
|
||||
if (Util.isProjectGraphql()) { |
||||
try { |
||||
if (args._.length < 2) { |
||||
console.warn('Invalid arguments for : xc permissions.userAdd') |
||||
return; |
||||
} |
||||
|
||||
const {1: user} = args._; |
||||
|
||||
/* get all policies */ |
||||
const policiesPath = path.join(process.cwd(), 'server', PermissionsMgr.getPolicyPath(), '**', '*.policy.js'); |
||||
|
||||
glob.sync(policiesPath).forEach((file) => { |
||||
|
||||
const modelName = path.basename(file).split('.')[0]; |
||||
|
||||
|
||||
const filePermissions = require(file); |
||||
|
||||
|
||||
const roles = this.extractUniqueGqlPolicyRoles(filePermissions); |
||||
|
||||
|
||||
if (!roles.includes(user)) { |
||||
console.warn(`${user} not exist in ${modelName} policy`); |
||||
return; |
||||
} |
||||
|
||||
for (const roles1 of Object.values(filePermissions)) { |
||||
delete roles1[user]; |
||||
} |
||||
|
||||
|
||||
console.log(`Deleting user permission for model:${modelName} roles:${user}`); |
||||
|
||||
|
||||
const policyFileContent = `module.exports = ${JSON.stringify(filePermissions, null, 2)};\n`; |
||||
fs.writeFileSync(file, policyFileContent) |
||||
|
||||
}); |
||||
|
||||
} catch (e) { |
||||
console.error(`Error in xc permissions.user.delete`, e); |
||||
} |
||||
} else { |
||||
try { |
||||
if (args._.length < 2) { |
||||
console.warn('Invalid arguments for : xc permissions.userAdd') |
||||
return; |
||||
} |
||||
|
||||
const {1: user} = args._; |
||||
|
||||
/* get all policies */ |
||||
const policiesPath = path.join(process.cwd(), 'server', PermissionsMgr.getPolicyPath(), '**', '*.routes.js'); |
||||
|
||||
glob.sync(policiesPath).forEach((file) => { |
||||
|
||||
const modelName = path.basename(file).split('.')[0]; |
||||
|
||||
|
||||
const routes: Route[] = require(file); |
||||
|
||||
|
||||
const roles = this.extractUniqueRoles(routes) |
||||
|
||||
|
||||
if (!roles.includes(user)) { |
||||
console.warn(`${user} not exist in ${modelName} policy`); |
||||
return; |
||||
} |
||||
|
||||
for (const route of routes) { |
||||
delete route.acl[user]; |
||||
} |
||||
|
||||
|
||||
console.log(`Deleting user permission for model:${modelName} roles:${user}`); |
||||
|
||||
|
||||
const policyFileContent = `module.exports = ${JSON.stringify(routes, null, 2)};\n`; |
||||
fs.writeFileSync(file, policyFileContent) |
||||
|
||||
}); |
||||
|
||||
} catch (e) { |
||||
console.error(`Error in xc permissions.user.delete`, e); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public static async userRename(args) { |
||||
|
||||
if (Util.isProjectGraphql()) { |
||||
try { |
||||
if (args._.length < 3) { |
||||
console.warn('Invalid arguments for : xc permissions.userAdd') |
||||
return; |
||||
} |
||||
|
||||
const {1: oldUser, 2: newUser} = args._; |
||||
|
||||
/* get all policies */ |
||||
const policiesPath = path.join(process.cwd(), 'server', PermissionsMgr.getPolicyPath(), '**', '*.policy.js'); |
||||
|
||||
glob.sync(policiesPath).forEach((file) => { |
||||
|
||||
const modelName = path.basename(file).split('.')[0]; |
||||
|
||||
|
||||
const filePermissions = require(file); |
||||
|
||||
const roles = this.extractUniqueGqlPolicyRoles(filePermissions); |
||||
|
||||
|
||||
if (!roles.includes(oldUser)) { |
||||
console.warn(`${oldUser} not exist in ${modelName} policy`); |
||||
return; |
||||
} |
||||
if (roles.includes(newUser)) { |
||||
console.warn(`${newUser} is already exist in ${modelName} policy`); |
||||
return; |
||||
} |
||||
|
||||
for (const roles1 of Object.values(filePermissions)) { |
||||
roles1[newUser] = roles1[oldUser]; |
||||
delete roles1[oldUser]; |
||||
} |
||||
|
||||
|
||||
console.log(`Renaming user permission ${oldUser} to ${newUser} for model:${modelName}`); |
||||
const policyFileContent = `module.exports = ${JSON.stringify(filePermissions, null, 2)};\n`; |
||||
fs.writeFileSync(file, policyFileContent) |
||||
|
||||
}); |
||||
|
||||
} catch (e) { |
||||
console.error(`Error in xc permissions.user.delete`, e); |
||||
} |
||||
} else { |
||||
try { |
||||
if (args._.length < 3) { |
||||
console.warn('Invalid arguments for : xc permissions.userAdd') |
||||
return; |
||||
} |
||||
|
||||
const {1: oldUser, 2: newUser} = args._; |
||||
|
||||
/* get all policies */ |
||||
const policiesPath = path.join(process.cwd(), 'server', PermissionsMgr.getPolicyPath(), '**', '*.routes.js'); |
||||
|
||||
glob.sync(policiesPath).forEach((file) => { |
||||
|
||||
const modelName = path.basename(file).split('.')[0]; |
||||
|
||||
|
||||
const routes: Route[] = require(file); |
||||
|
||||
const roles = this.extractUniqueRoles(routes); |
||||
|
||||
if (!roles.includes(oldUser)) { |
||||
console.warn(`${oldUser} not exist in ${modelName} policy`); |
||||
return; |
||||
} |
||||
if (roles.includes(newUser)) { |
||||
console.warn(`${newUser} is already exist in ${modelName} policy`); |
||||
return; |
||||
} |
||||
|
||||
for (const route of routes) { |
||||
route.acl[newUser] = route.acl[oldUser]; |
||||
delete route.acl[oldUser]; |
||||
} |
||||
|
||||
|
||||
console.log(`Renaming user permission ${oldUser} to ${newUser} for model:${modelName}`); |
||||
const policyFileContent = `module.exports = ${JSON.stringify(routes, null, 2)};\n`; |
||||
fs.writeFileSync(file, policyFileContent) |
||||
|
||||
}); |
||||
|
||||
} catch (e) { |
||||
console.error(`Error in xc permissions.user.delete`, e); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public static getPolicyPath() { |
||||
|
||||
const projectConfig = jsonfile.readFileSync(path.join(process.cwd(), 'config.xc.json')); |
||||
return projectConfig.meta.projectType === 'rest' ? 'routers' : 'resolvers'; |
||||
} |
||||
|
||||
private static extractUniqueGqlPolicyRoles(filePermissions) { |
||||
return Object.values(filePermissions) |
||||
.flatMap(roles1 => Object.keys(roles1)) |
||||
.filter((v, i, arr) => arr.indexOf(v) === i); |
||||
} |
||||
|
||||
private static extractUniqueRoles(routesList: Route[]) { |
||||
const roles = routesList |
||||
.flatMap( |
||||
route => Object.keys(route.acl) |
||||
) |
||||
.filter((v, i, arr) => arr.indexOf(v) === i); |
||||
return roles; |
||||
} |
||||
|
||||
|
||||
private static groupRoutes(routes: Route[]): GroupedRoutes { |
||||
const groupedRoutes: GroupedRoutes = {}; |
||||
for (const route of routes) { |
||||
groupedRoutes[route.path] = groupedRoutes[route.path] || {}; |
||||
groupedRoutes[route.path][route.type] = route; |
||||
} |
||||
return groupedRoutes; |
||||
} |
||||
|
||||
private static sortFiles(file1: string, file2: string): number { |
||||
return ((file1.indexOf('.bt.') > -1 ? 1 : 0) || (file1.indexOf('.hm.') > -1 ? 2 : 0)) |
||||
- ((file2.indexOf('.bt.') > -1 ? 1 : 0) || (file2.indexOf('.hm.') > -1 ? 2 : 0)); |
||||
} |
||||
} |
||||
|
||||
|
||||
export default PermissionsMgr; |
||||
|
||||
export interface Route { |
||||
type: string; |
||||
path: string; |
||||
acl: { |
||||
[key: string]: boolean |
||||
}; |
||||
} |
||||
|
||||
type httpMethods = 'get' | 'post' | 'patch' | 'put' | 'delete'; |
||||
|
||||
export interface GroupedRoutes { |
||||
[key: string]: { |
||||
[key in httpMethods]?: { |
||||
type: string; |
||||
path: string; |
||||
acl: { |
||||
[key: string]: boolean |
||||
}; |
||||
} |
||||
} |
||||
} |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* 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/>. |
||||
* |
||||
*/ |
@ -0,0 +1,123 @@
|
||||
export default { |
||||
mid: ['\n@NocoDB - generates instant REST/GraphQL API on any database.\n+ Has GUI based schema migrations!\n+ Built in API-Client and terminal!\n+ Dark theme and much more!\n'], |
||||
prefix: [ |
||||
'Super slick!', |
||||
'Very handy!', |
||||
'Impressive!', |
||||
'Awesome!', |
||||
'Excited & happy to see this idea!', |
||||
'What an awesome idea!', |
||||
'Pretty Awesome!', |
||||
'Amazing!', |
||||
'Phenomenal!', |
||||
'Incredible!', |
||||
'Fantastic!', |
||||
'Great!', |
||||
'Mind blowing!', |
||||
'Very Cool!', |
||||
'Stellar!', |
||||
'Neat!', |
||||
'Exciting!', |
||||
'Brilliant!', |
||||
'Extremely Interesting!', |
||||
'So useful!', |
||||
|
||||
'Wow, Super slick!', |
||||
'Wow, Very handy!', |
||||
'Wow, Impressive!', |
||||
'Wow, Awesome!', |
||||
'Wow, Excited & happy to see this idea!', |
||||
'Wow, What an awesome idea!', |
||||
'Wow, Pretty Awesome!', |
||||
'Wow, Amazing!', |
||||
'Wow, Phenomenal!', |
||||
'Wow, Incredible!', |
||||
'Wow, Fantastic!', |
||||
'Wow, Great!', |
||||
'Wow, Mind blowing!', |
||||
'Wow, Very Cool!', |
||||
'Wow, Stellar!', |
||||
'Wow, Neat!', |
||||
'Wow, Exciting!', |
||||
'Wow, Brilliant!', |
||||
'Wow, Extremely Interesting!', |
||||
'Wow, So useful!', |
||||
|
||||
'Woah, Super slick!', |
||||
'Woah, Very handy!', |
||||
'Woah, Impressive!', |
||||
'Woah, Awesome!', |
||||
'Woah, Excited & happy to see this idea!', |
||||
'Woah, What an awesome idea!', |
||||
'Woah, Pretty Awesome!', |
||||
'Woah, Amazing!', |
||||
'Woah, Phenomenal!', |
||||
'Woah, Incredible!', |
||||
'Woah, Fantastic!', |
||||
'Woah, Great!', |
||||
'Woah, Mind blowing!', |
||||
'Woah, Very Cool!', |
||||
'Woah, Stellar!', |
||||
'Woah, Neat!', |
||||
'Woah, Exciting!', |
||||
'Woah, Brilliant!', |
||||
'Woah, Extremely Interesting!', |
||||
'Woah, So useful!', |
||||
|
||||
|
||||
'Super slick API Framework!', |
||||
'Very handy API Framework!', |
||||
'Impressive API Framework!', |
||||
'Awesome API Framework!', |
||||
'Excited & happy to see this API Framework!', |
||||
'What an awesome API Framework!', |
||||
'Pretty Awesome API Framework!', |
||||
'Amazing API Framework!', |
||||
'Phenomenal API Framework!', |
||||
'Incredible API Framework!', |
||||
'Fantastic API Framework!', |
||||
'Great API Framework!', |
||||
'Mind blowing API Framework!', |
||||
'Very Cool API Framework!', |
||||
'Stellar API Framework!', |
||||
'Neat API Framework!', |
||||
'Exciting API Framework!', |
||||
'Brilliant API Framework!', |
||||
'Extremely Interesting API Framework!', |
||||
'So useful API Framework', |
||||
|
||||
|
||||
'Cool Stuff!', |
||||
'Woha!', |
||||
|
||||
'I\'m amazed by how well this works!', |
||||
'I\'m impressed by how well this works!', |
||||
|
||||
], |
||||
suffix: [ |
||||
'https://github.com/nocodb/nocodb', |
||||
'www.nocodb.com' |
||||
] |
||||
} |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* 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/>. |
||||
* |
||||
*/ |
@ -0,0 +1,292 @@
|
||||
import chalkPipe from 'chalk-pipe'; |
||||
import inquirer from 'inquirer'; |
||||
import tcpPortUsed from 'tcp-port-used'; |
||||
import URL from 'url'; |
||||
import Util from "../util/Util"; |
||||
import path from 'path'; |
||||
import Lang, {STR} from "../util/Lang"; |
||||
|
||||
import('colors'); |
||||
|
||||
const dbDefaults = { |
||||
mysql2: { |
||||
host: 'localhost', |
||||
port: '3306', |
||||
username: 'root', |
||||
password: '', |
||||
database: '' |
||||
}, |
||||
pg: { |
||||
host: 'localhost', |
||||
port: '5432', |
||||
username: 'postgres', |
||||
password: '', |
||||
database: '' |
||||
}, |
||||
mssql: { |
||||
host: 'localhost', |
||||
port: '1433', |
||||
username: 'sa', |
||||
password: '', |
||||
database: '' |
||||
}, |
||||
sqlite3: { |
||||
host: 'localhost', |
||||
port: '1433', |
||||
username: 'sa', |
||||
password: '', |
||||
database: '' |
||||
}, |
||||
}; |
||||
|
||||
|
||||
class TryMgr { |
||||
|
||||
/** |
||||
* |
||||
* Does the below : |
||||
* - Get database input and make a DB URL from it. |
||||
* - Create new folder and 'cd' to that folder. |
||||
* - Return true/success |
||||
* - Else failure |
||||
* |
||||
* @param args |
||||
* @returns {Promise<string|string|boolean|*>} |
||||
*/ |
||||
public static async getProjectInput(args): Promise<any> { |
||||
let dbUrl; |
||||
|
||||
if (args._[0] !== 't' && args._[0] !== 'try') { |
||||
if ( args._[1] === 'rest') { |
||||
dbUrl = `sqlite3://sqlite3?d=${path.join(__dirname, 'sakila.db')}`; |
||||
} else if (args._[1] === 'gql') { |
||||
dbUrl = `sqlite3://sqlite3?d=${path.join(__dirname, 'sakila.db')}&api=graphql`; |
||||
} |
||||
} else { |
||||
/* Construct database URL from prompt */ |
||||
const dbTypes = Object.keys(dbDefaults); |
||||
args.url = [] |
||||
|
||||
const finalAnswers = await inquirer |
||||
.prompt([ |
||||
{ |
||||
name: 'type', |
||||
type: 'list', |
||||
message: Lang.getString(STR.DB_TYPE),// '🔥 Choose SQL Database type\t:',
|
||||
choices: dbTypes.map(t => ({ |
||||
name: t, |
||||
value: t, |
||||
short: t.green.bold |
||||
})), |
||||
default: 'mysql2', |
||||
transformer(color) { |
||||
return chalkPipe(color)(color.green.bold); |
||||
} |
||||
}, |
||||
{ |
||||
name: 'host', |
||||
type: 'input', |
||||
message: Lang.getString(STR.DB_HOST),// '👉 Enter database host name\t:',
|
||||
default(ans) { |
||||
return dbDefaults[ans.type].host |
||||
}, |
||||
transformer(color) { |
||||
return chalkPipe(color)(color.green.bold); |
||||
}, |
||||
when({type}) { |
||||
return type !== 'sqlite3' |
||||
} |
||||
}, |
||||
{ |
||||
name: 'port', |
||||
type: 'number', |
||||
message: Lang.getString(STR.DB_PORT),// '👉 Enter database port number\t:',
|
||||
default(ans) { |
||||
return dbDefaults[ans.type].port |
||||
}, |
||||
transformer(color) { |
||||
try { |
||||
return color.green.bold; |
||||
} catch (e) { |
||||
return color |
||||
} |
||||
}, |
||||
validate(port, answers) { |
||||
const done = this.async(); |
||||
if (answers.host === 'host.docker.internal' || answers.host === 'docker.for.win.localhost') { |
||||
done(null, true) |
||||
} else { |
||||
TryMgr.isPortOpen(answers.host, port).then(isOpen => { |
||||
if (isOpen) { |
||||
done(null, true) |
||||
} else { |
||||
// done('Port is not open')
|
||||
console.log(`\n\n😩 ${answers.host}:${port} is not open please start the database if you haven't\n`.red.bold) |
||||
process.exit(0); |
||||
} |
||||
}).catch(done) |
||||
} |
||||
}, |
||||
when({type}) { |
||||
return type !== 'sqlite3' |
||||
} |
||||
}, |
||||
{ |
||||
name: 'username', |
||||
type: 'input', |
||||
message: Lang.getString(STR.DB_USER), // '👉 Enter database username\t:',
|
||||
default(ans) { |
||||
return dbDefaults[ans.type].username |
||||
}, |
||||
transformer(color) { |
||||
return chalkPipe(color)(color.green.bold); |
||||
}, |
||||
when({type}) { |
||||
return type !== 'sqlite3' |
||||
} |
||||
}, |
||||
{ |
||||
name: 'password', |
||||
type: 'input', |
||||
mask: true, |
||||
message: Lang.getString(STR.DB_PASSWORD), // '🙈 Enter database password\t:',
|
||||
transformer(color) { |
||||
return new Array(color.length).fill('*'.green.bold).join('') |
||||
}, |
||||
when({type}) { |
||||
return type !== 'sqlite3' |
||||
} |
||||
}, |
||||
{ |
||||
name: 'database', |
||||
type: 'input', |
||||
message: Lang.getString(STR.DB_SCHEMA), // '👉 Enter database/schema name\t:',
|
||||
transformer(color) { |
||||
return chalkPipe(color)(color.green.bold); |
||||
}, |
||||
when({type}) { |
||||
return type !== 'sqlite3' |
||||
} |
||||
}, |
||||
{ |
||||
name: 'apiType', |
||||
type: 'list', |
||||
message: Lang.getString(STR.DB_API), // '🚀 Enter API type to generate\t:',
|
||||
choices: ['REST APIs', 'GRAPHQL APIs', 'gRPC APIs'].map(t => ({ |
||||
name: t, |
||||
value: t, |
||||
short: t.green.bold |
||||
})), |
||||
transformer(color) { |
||||
return chalkPipe(color)(color.green.bold); |
||||
}, |
||||
when({type}) { |
||||
return type !== 'sqlite3' |
||||
} |
||||
}, |
||||
]) |
||||
|
||||
// console.log(finalAnswers);
|
||||
|
||||
if (finalAnswers.type === 'sqlite3') { |
||||
console.log('Please use desktop app to create Sqlite project'.green.bold) |
||||
process.exit(0); |
||||
} |
||||
|
||||
/* if not valid retry getting right input */ |
||||
/* if (!finalAnswers.database) { |
||||
console.log('\n\tWarning! Database name can NOT be empty. Retry.\n '.red.bold); |
||||
this.getNewProjectInput(args); |
||||
}*/ |
||||
//
|
||||
|
||||
|
||||
/* prepare the args */ |
||||
// const url = `${finalAnswers.type}://${finalAnswers.host}:${finalAnswers.port}?u=${finalAnswers.username}&p=${finalAnswers.password}&d=${finalAnswers.database}&api=${apiTypeMapping[finalAnswers.projectType]}`;
|
||||
// args._[0] = finalAnswers.projectType === 'REST APIs' ? 'gar' : 'gag';
|
||||
switch (finalAnswers.apiType) { |
||||
case 'REST APIs': |
||||
finalAnswers.apiType = 'rest'; |
||||
break; |
||||
case 'GRAPHQL APIs': |
||||
finalAnswers.apiType = 'graphql'; |
||||
break; |
||||
case 'gRPC APIs': |
||||
finalAnswers.apiType = 'grpc'; |
||||
break; |
||||
default: |
||||
finalAnswers.apiType = 'rest'; |
||||
break; |
||||
} |
||||
dbUrl = `${finalAnswers.type}://${finalAnswers.host}:${finalAnswers.port}?u=${finalAnswers.username}&p=${finalAnswers.password}&d=${finalAnswers.database}&api=${finalAnswers.apiType}` |
||||
} |
||||
// await Util.runCmd(`cd ${__dirname};echo "const {XcTry} = require('xc-instant'); process.env.NC_DB_URL='${finalAnswers.dbUrl}'; XcTry().then(() => console.log('App started'));" | node`);
|
||||
await Util.runCmd(`echo "const {XcTry} = require('xc-instant'); XcTry('${dbUrl}').then(() => console.log('App started'));" | NODE_PATH=${path.join(__dirname, '..', 'node_modules')} node`); |
||||
|
||||
} |
||||
|
||||
|
||||
public static async testConnection({url}) { |
||||
|
||||
for (const u of url) { |
||||
const parsedUrlData = URL.parse(u, true); |
||||
const queryParams = parsedUrlData.query; |
||||
const client = parsedUrlData.protocol.slice(0, -1); |
||||
const config = { |
||||
client, |
||||
connection: { |
||||
database: client === 'pg' ? 'postgres' : (client === 'mssql' ? undefined : null), |
||||
host: parsedUrlData.hostname, |
||||
password: queryParams.p, |
||||
port: +parsedUrlData.port, |
||||
user: queryParams.u, |
||||
} |
||||
}; |
||||
|
||||
try { |
||||
const knex = require('knex')(config) |
||||
await knex.raw("SELECT 1+1 as data"); |
||||
} catch (e) { |
||||
console.log(`\n😩 Test connection failed for : ${url}\n`.red.bold) |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
|
||||
public static async isPortOpen(host, port) { |
||||
try { |
||||
return await tcpPortUsed.check(+port, host) |
||||
} catch (e) { |
||||
console.log(e) |
||||
console.log(`\n😩 ${host}:${port} is not reachable please check\n`.red.bold) |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
export default TryMgr; |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* 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/>. |
||||
* |
||||
*/ |
@ -0,0 +1,170 @@
|
||||
import axios from 'axios'; |
||||
import download from 'download'; |
||||
import osInfo from 'linux-os-info'; |
||||
import open from 'open'; |
||||
import ora from 'ora'; |
||||
import os from 'os'; |
||||
import shell from 'shelljs'; |
||||
|
||||
import('colors'); |
||||
|
||||
const PROGRESS_WIDTH = 30; |
||||
|
||||
class AppMgr { |
||||
|
||||
public static async install(args) { |
||||
try { |
||||
const spinner = ora({text: 'Downloading Desktop App from Github..'.green.bold(), spinner: 'dots2', color: 'green'}).start(); |
||||
const {src, dest} = await AppMgr.getDownloadLink(args); |
||||
|
||||
console.log(`\t${src}`); |
||||
|
||||
await download(src, '.').on('downloadProgress', progress => { |
||||
// console.log(progress)
|
||||
// Report download progress
|
||||
const p = PROGRESS_WIDTH * progress.percent; |
||||
spinner.text = `Downloading Desktop App now..\n[${ |
||||
Array.from({length: PROGRESS_WIDTH}, (_, i) => i <= p ? '=' : ' ').join('') |
||||
}] ${(progress.transferred / (1024 * 1024)).toFixed(2)}MB/${(progress.total / (1024 * 1024)).toFixed(2)}MB\n`.green.bold()
|
||||
}); |
||||
// spinner.prefixText = '';
|
||||
spinner.succeed(`Installable downloaded successfully at ./${dest}`.green.bold()); |
||||
console.log(`\nInstallable will open automatically now.`.green.bold); |
||||
console.log(`If not, please install it manually.`.green.bold); |
||||
if (os.type() === 'Windows_NT') { |
||||
// open(dest, {wait: true, app: 'explorer.exe'})
|
||||
} else { |
||||
open(dest, {wait: true}); |
||||
} |
||||
|
||||
} catch (e) { |
||||
console.error(`Error in xc app.install`, e); |
||||
} |
||||
} |
||||
|
||||
public static async open(args) { |
||||
try { |
||||
const runCommand = AppMgr.getOpenCommand(args); |
||||
if (!runCommand) { return } |
||||
if (shell.exec(runCommand).code !== 0) { |
||||
shell.echo(`\n\nError running command internally`.red); |
||||
shell.echo(`\nExiting...`.red); |
||||
shell.exit(1); |
||||
} |
||||
} catch (e) { |
||||
console.error(`Error in xc app.open`, e); |
||||
} |
||||
} |
||||
|
||||
public static async getDownloadLink(args):Promise<any> { |
||||
try { |
||||
let src; |
||||
let dest; |
||||
const urls:any = {}; |
||||
|
||||
const res = await axios.get('https://api.github.com/repos/xgenecloud/xc-desktop-app/releases?page=1') |
||||
|
||||
let status = 0; |
||||
|
||||
for (let i = 0; i < res.data.length && status !== 15; i++) { |
||||
const assets = res.data[i].assets; |
||||
for (const {name, browser_download_url} of assets) { |
||||
switch (name.split('.').pop().toLowerCase()) { |
||||
case 'dmg': |
||||
urls.dmg = urls.dmg || browser_download_url; |
||||
status = status | 1; |
||||
break; |
||||
case 'deb': |
||||
urls.deb = urls.deb || browser_download_url; |
||||
status = status | 2; |
||||
break; |
||||
case 'rpm': |
||||
urls.rpm = urls.rpm || browser_download_url; |
||||
status = status | 4; |
||||
break; |
||||
case 'exe': |
||||
urls.exe = urls.exe || browser_download_url; |
||||
status = status | 8; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
switch (os.type()) { |
||||
case 'Linux': |
||||
const linuxInfo = osInfo({mode: 'sync'}); |
||||
if (args.debian) { src = urls.deb; } |
||||
else if (args.rpm) { src = urls.rpm; } |
||||
else { |
||||
switch (linuxInfo.id) { |
||||
case 'ubuntu': |
||||
case 'raspberry': |
||||
src = urls.deb |
||||
break; |
||||
case 'fedora': |
||||
src = urls.rpm |
||||
break; |
||||
default: |
||||
src = urls.rpm |
||||
} |
||||
} |
||||
break; |
||||
case 'Darwin': |
||||
src = urls.dmg; |
||||
break; |
||||
case 'Windows_NT': |
||||
src = urls.exe; |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
|
||||
dest = src.split('/').pop(); |
||||
return {src, dest} |
||||
} catch (e) { |
||||
console.log(e) |
||||
} |
||||
} |
||||
|
||||
public static getOpenCommand(_args):any { |
||||
switch (os.type()) { |
||||
case 'Linux': |
||||
return 'xgenecloud' |
||||
break; |
||||
case 'Darwin': |
||||
return 'open -a xgenecloud' |
||||
break; |
||||
case 'Windows_NT': |
||||
console.info('Open xgenecloud desktop app from Windows start menu') |
||||
|
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
export default AppMgr; |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* 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/>. |
||||
* |
||||
*/ |
@ -0,0 +1,9 @@
|
||||
{ |
||||
"prompt": false, |
||||
"dontPrompt": false, |
||||
"createdApis": false, |
||||
"firstTimeUse": true, |
||||
"promptForStar": false, |
||||
"dontPromptForStar": true, |
||||
"cliUsedCount": 0 |
||||
} |
@ -0,0 +1,136 @@
|
||||
/** |
||||
* Heirarchical conversation example |
||||
*/ |
||||
|
||||
'use strict'; |
||||
import inquirer from 'inquirer'; |
||||
|
||||
class RunOrDownload { |
||||
|
||||
public static async prepareCmd(ipc, args) { |
||||
|
||||
args._[0] = ''; |
||||
|
||||
const answers = await inquirer.prompt([ |
||||
{ |
||||
choices: [ |
||||
'gen', |
||||
'add', |
||||
'show', |
||||
'run', |
||||
], |
||||
message: 'What do you want to do?', |
||||
name: 'action', |
||||
type: 'rawlist' |
||||
}]); |
||||
|
||||
args._[0] = answers.action; |
||||
|
||||
switch (answers.action) { |
||||
|
||||
case 'gen': |
||||
await this.handleGen(ipc,args) |
||||
break; |
||||
|
||||
case 'add': |
||||
break; |
||||
|
||||
case 'show': |
||||
break; |
||||
|
||||
case 'run': |
||||
break; |
||||
|
||||
|
||||
} |
||||
} |
||||
|
||||
public static async handleGen(_ipc,args) { |
||||
|
||||
const a1 = await inquirer.prompt([ |
||||
{ |
||||
choices: [ |
||||
'apis', |
||||
'backend', |
||||
'block', |
||||
'controller', |
||||
'middleware', |
||||
'service', |
||||
'resolver', |
||||
'type', |
||||
'schema', |
||||
'apis', |
||||
], |
||||
message: 'What do you want to generate ?', |
||||
name: 'action', |
||||
pageSize: 9, |
||||
type: 'rawlist' |
||||
}]); |
||||
|
||||
args._[0] += `:${a1.action}`; |
||||
|
||||
switch (a1.action) { |
||||
|
||||
case 'apis': |
||||
|
||||
const a2 = await inquirer.prompt([ |
||||
{ |
||||
choices: [ |
||||
'rest', |
||||
'graphql' |
||||
], |
||||
message: 'Which apis you want to use?', |
||||
name: 'action', |
||||
type: 'rawlist' |
||||
}]); |
||||
|
||||
args._[0] += `:${a2.action}`; |
||||
|
||||
break; |
||||
|
||||
} |
||||
} |
||||
|
||||
// tslint:disable-next-line:no-empty
|
||||
public static async handleAdd() { |
||||
|
||||
} |
||||
|
||||
// tslint:disable-next-line:no-empty
|
||||
public static async handleShow() { |
||||
|
||||
} |
||||
|
||||
// tslint:disable-next-line:no-empty
|
||||
public static async handleRun() { |
||||
|
||||
} |
||||
|
||||
|
||||
} |
||||
|
||||
|
||||
export default RunOrDownload; |
||||
|
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* 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/>. |
||||
* |
||||
*/ |
@ -0,0 +1,63 @@
|
||||
/** |
||||
* Heirarchical conversation example |
||||
*/ |
||||
|
||||
'use strict'; |
||||
import inquirer from "inquirer"; |
||||
|
||||
class RunOrDownload { |
||||
|
||||
public static async handle(_args) { |
||||
|
||||
const answers = await inquirer.prompt([ |
||||
{ |
||||
choices: [ |
||||
'Open the app!', |
||||
'Download it for FREE' |
||||
], |
||||
message: 'Your XGENE desktop app is not open - do you want to ?', |
||||
name: 'action', |
||||
type: 'rawlist' |
||||
} |
||||
]); |
||||
|
||||
switch (answers.action) { |
||||
|
||||
case 'Open app!': |
||||
break; |
||||
|
||||
case 'Download it for FREE': |
||||
console.log('wget(https://xgene.cloud/download?latest=true) to Downloads'); |
||||
break; |
||||
|
||||
} |
||||
} |
||||
|
||||
|
||||
} |
||||
|
||||
|
||||
export default RunOrDownload; |
||||
|
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* 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/>. |
||||
* |
||||
*/ |
@ -0,0 +1,178 @@
|
||||
import osLocale from 'os-locale'; |
||||
import english from './english.json'; |
||||
import translated from './translated.json'; |
||||
|
||||
/* Converted from : https://smodin.me/translate-one-text-into-multiple-languages |
||||
* Enter database host name || Choose SQL Database type || Enter database username || Enter database password || Enter database port number || Enter database/schema name || Enter API type to generate || How do you want to run it |
||||
* */ |
||||
|
||||
const formattedTranslate: any = {}; |
||||
for (const {symbol, text} of [english, ...translated].sort((a, b) => a.symbol.localeCompare(b.symbol))) { |
||||
formattedTranslate [symbol] = text.split(/\s*\|\|\s*/); |
||||
} |
||||
|
||||
|
||||
const dummy: any = new Date(); |
||||
const offset: any = -dummy.getTimezoneOffset(); |
||||
const locale: string = offset === 330 ? 'en-IN' : osLocale.sync(); |
||||
|
||||
const SMILEY_PREFIX = ['👉', '🔥', '👉', '🙈', '👉', '👉', '🚀', '🚀']; |
||||
|
||||
enum STR { |
||||
DB_HOST, |
||||
DB_TYPE, |
||||
DB_USER, |
||||
DB_PASSWORD, |
||||
DB_PORT, |
||||
DB_SCHEMA, |
||||
DB_API, |
||||
PROJECT_TYPE |
||||
} |
||||
|
||||
|
||||
class Lang { |
||||
|
||||
// @ts-ignore
|
||||
public static getString(str: STR) { |
||||
|
||||
switch (locale) { |
||||
|
||||
case 'en': |
||||
case 'en-GB': |
||||
case 'en-AU': |
||||
case 'en-CA': |
||||
case 'en-IE': |
||||
case 'en-US': |
||||
default: |
||||
|
||||
return `${SMILEY_PREFIX[str]} ${formattedTranslate?.en?.[str]}\t:`; |
||||
case 'zh': |
||||
case 'zh-Hans': |
||||
case 'zh-Hant': |
||||
case 'zh-CN': |
||||
case 'zh-HK': |
||||
return `${SMILEY_PREFIX[str]} ${formattedTranslate?.['zh-cn']?.[str]}\t:`; |
||||
|
||||
case 'zh-TW': |
||||
return `${SMILEY_PREFIX[str]} ${formattedTranslate?.['zh-tw']?.[str]}\t:`; |
||||
|
||||
|
||||
case 'de': |
||||
case 'de-DE': |
||||
case 'de-CH': |
||||
case 'de-AT': |
||||
return `${SMILEY_PREFIX[str]} ${formattedTranslate?.de?.[str]}\t:`; |
||||
case 'el': |
||||
case 'el-GR': |
||||
return `${SMILEY_PREFIX[str]} ${formattedTranslate?.el?.[str]}\t:`; |
||||
|
||||
case 'es': |
||||
case 'es-AR': |
||||
case 'es-419': |
||||
case 'es-CL': |
||||
case 'es-CO': |
||||
case 'es-EC': |
||||
case 'es-ES': |
||||
case 'es-LA': |
||||
case 'es-NI': |
||||
case 'es-MX': |
||||
case 'es-US': |
||||
case 'es-VE': |
||||
return `${SMILEY_PREFIX[str]} ${formattedTranslate?.es?.[str]}\t:`; |
||||
case 'fi': |
||||
case 'fi-FI': |
||||
return `${SMILEY_PREFIX[str]} ${formattedTranslate?.fi?.[str]}\t:`; |
||||
|
||||
case 'fr': |
||||
case 'fr-CA': |
||||
case 'fr-FR': |
||||
case 'fr-BE': |
||||
case 'fr-CH': |
||||
return `${SMILEY_PREFIX[str]} ${formattedTranslate?.fr?.[str]}\t:`; |
||||
case 'it': |
||||
case 'it-IT': |
||||
return `${SMILEY_PREFIX[str]} ${formattedTranslate?.it?.[str]}\t:`; |
||||
|
||||
case 'ja': |
||||
case 'ja-JP': |
||||
return `${SMILEY_PREFIX[str]} ${formattedTranslate?.ja?.[str]}\t:`; |
||||
case 'ko': |
||||
case 'ko-KR': |
||||
return `${SMILEY_PREFIX[str]} ${formattedTranslate?.ko?.[str]}\t:`; |
||||
|
||||
case 'nl': |
||||
case 'nl-BE': |
||||
case 'nl-NL': |
||||
case 'nn-NO': |
||||
return `${SMILEY_PREFIX[str]} ${formattedTranslate?.nl?.[str]}\t:`; |
||||
|
||||
case 'pt': |
||||
case 'pt-BR': |
||||
case 'pt-PT': |
||||
|
||||
return `${SMILEY_PREFIX[str]} ${formattedTranslate?.pt?.[str]}\t:`; |
||||
|
||||
case 'ru': |
||||
case 'ru-RU': |
||||
return `${SMILEY_PREFIX[str]} ${formattedTranslate?.ru?.[str]}\t:`; |
||||
|
||||
|
||||
case 'sv': |
||||
case 'sv-SE': |
||||
return `${SMILEY_PREFIX[str]} ${formattedTranslate?.sv?.[str]}\t:`; |
||||
|
||||
|
||||
case 'th': |
||||
case 'th-TH': |
||||
return `${SMILEY_PREFIX[str]} ${formattedTranslate?.th?.[str]}\t:`; |
||||
|
||||
case 'tl': |
||||
case 'tl-PH': |
||||
return `${SMILEY_PREFIX[str]} ${formattedTranslate?.tl?.[str]}\t:`; |
||||
|
||||
|
||||
case 'tr': |
||||
case 'tr-TR': |
||||
return `${SMILEY_PREFIX[str]} ${formattedTranslate?.tr?.[str]}\t:`; |
||||
|
||||
|
||||
case 'uk': |
||||
case 'uk-UA': |
||||
return `${SMILEY_PREFIX[str]} ${formattedTranslate?.uk?.[str]}\t:`; |
||||
|
||||
case 'vi': |
||||
case 'vi-VN': |
||||
return `${SMILEY_PREFIX[str]} ${formattedTranslate?.vi?.[str]}\t:`; |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
} |
||||
|
||||
export default Lang; |
||||
export { |
||||
STR |
||||
}; |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* 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/>. |
||||
* |
||||
*/ |
@ -0,0 +1,931 @@
|
||||
import path from 'path'; |
||||
import shell from 'shelljs'; |
||||
|
||||
class Util { |
||||
|
||||
public static getShortVersion(args) { |
||||
|
||||
const shortVersion = JSON.parse(JSON.stringify(args)); |
||||
|
||||
if (shortVersion._.length) { |
||||
shortVersion._[0] = shortVersion._[0].split('.').map(a => a[0]).join('') |
||||
} |
||||
return shortVersion; |
||||
} |
||||
|
||||
public static isProjectGraphql() { |
||||
const currentProjectJson = require(path.join(process.cwd(), 'config.xc.json')) |
||||
return currentProjectJson.meta.projectType.toLowerCase() === 'graphql'; |
||||
} |
||||
|
||||
public static showHelp(_args) { |
||||
console.log(` |
||||
Usage: xc [commands] |
||||
|
||||
Man page : |
||||
man <command> Outputs a man page for the command |
||||
|
||||
Commands: |
||||
new Creates a new project by taking database credentials as input |
||||
|
||||
`);
|
||||
} |
||||
|
||||
public static manNew() { |
||||
return ` |
||||
${'COMMAND : '.bold} |
||||
${'xc new'.bold} - Creates new projects and creates APIs instantly for database inputted. |
||||
|
||||
${'SYNOPSIS : '.bold} |
||||
${'xc new'.bold} ${'<projectName>'.bold} |
||||
|
||||
${'ARGS :'.bold} |
||||
|
||||
The following are arguments available for new: |
||||
|
||||
${'projectName'.bold} new project name |
||||
|
||||
${'VARIATIONS :'.bold} |
||||
|
||||
None |
||||
|
||||
${'Examples with notes : '.bold} |
||||
${'xc new airbnb'.bold} |
||||
- Takes in database credentials and API type as input |
||||
- Database can be new or existing database |
||||
- Creates a new folder by name 'airbnb' |
||||
` |
||||
} |
||||
|
||||
public static manDocker() { |
||||
return ` |
||||
${'COMMAND : '.bold} |
||||
${'xc docker'.bold} - Accepts DB credentials and creates a Docker file. |
||||
|
||||
${'SYNOPSIS : '.bold} |
||||
${'xc docker'.bold} |
||||
|
||||
${'ARGS :'.bold} |
||||
None |
||||
|
||||
${'VARIATIONS :'.bold} |
||||
None |
||||
|
||||
${'Examples with notes : '.bold} |
||||
${'xc docker'.bold} |
||||
- Takes in database credentials and API type as input |
||||
- Database can be new or existing database |
||||
- Scaffolds REST/GraphQL/gRPC APIs for the database specified |
||||
- These database can be already existing or new databases. |
||||
` |
||||
} |
||||
|
||||
public static manGenApisRest() { |
||||
return ` |
||||
${'COMMAND : '.bold} |
||||
${'xc gen.apis.graphql'.bold} - generates REST APIs for an existing/new databases of a project. |
||||
|
||||
${'SYNOPSIS : '.bold} |
||||
${'xc gen.apis.rest'.bold} [${'-u'.bold} DatabaseURL(s)] |
||||
|
||||
${'VARIATIONS :'.bold} |
||||
|
||||
The following variations are available: |
||||
|
||||
${'<>'.bold} If ${'no'.bold} option is passed, this generates REST APIs for databases current project |
||||
that are within project file config.xc.json |
||||
|
||||
${'-u'.bold} ${'<Database URLs>'.bold} Creates a new project for an existing/new database |
||||
For mysql : mysql://localhost:3306?u=root&p=password&d=ecommerce
|
||||
For pg : pg://localhost:5432?u=postgres&p=password&d=ecommerce
|
||||
For mssql : mssql://localhost:1433?u=sa&p=Password123.&d=ecommerce
|
||||
|
||||
${'Examples with notes : '.bold} |
||||
${'xc gen.apis.rest -u mysql://localhost:3306?u=root&p=password&d=ecommerce'.bold} |
||||
- Generates REST apis for a mysql database 'ecommerce' |
||||
- Uses current directory as project name |
||||
- If database exists - REST APIs will be created for the tables within it. |
||||
- If database doesnt exist - it will be created. |
||||
|
||||
${'xc gen.apis.rest -u pg://localhost:5432?u=postgres&p=password&d=ecommerce'.bold} |
||||
- Generates REST apis for a postgres database 'ecommerce' |
||||
|
||||
${'xc gen.apis.rest -u mssql://localhost:1433?u=sa&p=Password123.&d=ecommerce'.bold} |
||||
- Generates REST apis for a mssql database 'ecommerce' |
||||
|
||||
${`xc gen.apis.rest -u mysql://localhost:3306?u=root&p=password&d=ecommerce
|
||||
-u pg://localhost:5432?u=postgres&p=password&d=crm`.bold}
|
||||
- Generates REST apis for multiple databases 'ecommerce' & 'crm'. |
||||
- These database can be already existing or new databases. |
||||
` |
||||
} |
||||
|
||||
public static manGenApisGraphql() { |
||||
return ` |
||||
${'COMMAND : '.bold} |
||||
${'xc gen.apis.graphql'.bold} - generates GraphQL APIs for an existing/new databases of a project. |
||||
|
||||
${'SYNOPSIS : '.bold} |
||||
${'xc gen.apis.graphql'.bold} [${'-u'.bold} DatabaseURL(s)] |
||||
|
||||
${'VARIATIONS :'.bold} |
||||
|
||||
The following variations are available: |
||||
|
||||
${'<>'.bold} If ${'no'.bold} option is passed, this generates GraphQL APIs for databases current project |
||||
that are within project file config.xc.json |
||||
|
||||
${'-u'.bold} ${'<Database URLs>'.bold} Creates a new project for an existing/new database |
||||
For mysql : mysql://localhost:3306?u=root&p=password&d=ecommerce
|
||||
For pg : pg://localhost:5432?u=postgres&p=password&d=ecommerce
|
||||
For mssql : mssql://localhost:1433?u=sa&p=Password123.&d=ecommerce
|
||||
|
||||
${'Examples with notes : '.bold} |
||||
${'xc gen.apis.graphql -u mysql://localhost:3306?u=root&p=password&d=ecommerce'.bold} |
||||
- Generates GraphQL apis for a mysql database 'ecommerce' |
||||
- Uses current directory as project name |
||||
- If database exists - GraphQL APIs will be created for the tables within it. |
||||
- If database doesnt exist - it will be created. |
||||
|
||||
${'xc gen.apis.graphql -u pg://localhost:5432?u=postgres&p=password&d=ecommerce'.bold} |
||||
- Generates GraphQL apis for a postgres database 'ecommerce' |
||||
|
||||
${'xc gen.apis.graphql -u mssql://localhost:1433?u=sa&p=Password123.&d=ecommerce'.bold} |
||||
- Generates GraphQL apis for a mssql database 'ecommerce' |
||||
|
||||
${`xc gen.apis.graphql -u mysql://localhost:3306?u=root&p=password&d=ecommerce
|
||||
-u pg://localhost:5432?u=postgres&p=password&d=crm`.bold}
|
||||
- Generates GraphQL apis for multiple databases 'ecommerce' & 'crm'. |
||||
- These database can be already existing or new databases. |
||||
` |
||||
} |
||||
|
||||
public static manGenModule() { |
||||
return ` |
||||
${'COMMAND : '.bold} |
||||
${'xc gen.module'.bold} - Creates a table and scaffolds the respective module files
|
||||
In REST project - (router, service, middleware, policy, model, meta) |
||||
In GraphQL project - (resolver, service, middleware, policy, model, meta) |
||||
|
||||
${'SYNOPSIS : '.bold} |
||||
${'xc gen.module'.bold} ${'module(s)'.bold} [--nomodel] [--router] [--service] [--dbAlias] |
||||
|
||||
${'OPTIONS :'.bold} |
||||
|
||||
The following options are available: |
||||
|
||||
${'--nomodel'.bold} Scaffolds module without creating table and model, meta files. |
||||
For mysql : mysql://localhost:3306?u=root&p=password&d=ecommerce
|
||||
For pg : pg://localhost:5432?u=postgres&p=password&d=ecommerce
|
||||
For mssql : mssql://localhost:1433?u=sa&p=Password123.&d=ecommerce
|
||||
|
||||
${'--dbAlias'.bold} Defaults to 'db'(primary) database if not provided to create the table |
||||
${'--router'.bold} Will create router with model - service file will be skipped //todo
|
||||
${'--service'.bold} Will create service with model - router file will be skipped //todo
|
||||
|
||||
${'VARIATIONS :'.bold} |
||||
|
||||
None |
||||
|
||||
${'Examples with notes : '.bold} |
||||
${'xc gen.module blog'.bold} |
||||
- Creates blog table in primary database and scaffolds files \n\t (router/resolver,middleware,policy,service,model,meta) files |
||||
- Uses current directory as project name |
||||
- If database exists - GraphQL APIs will be created for the tables within it. |
||||
- If database doesnt exist - it will be created. |
||||
|
||||
${'xc gen.module blog.comment'.bold} |
||||
- Multiple tables can be created with '.' seperated files |
||||
- Creates blog and comment table in primary database with all component files |
||||
|
||||
${'xc gen.module blog --dbAlias secondary'.bold} |
||||
- Creates table and module for secondary database |
||||
|
||||
${'xc gen.module blog --nomodel'.bold} |
||||
- Creates blog model without creating table |
||||
- All folder/files in respective components will be created in parent table of primary table |
||||
|
||||
|
||||
` |
||||
} |
||||
|
||||
public static manDbMigrateInit() { |
||||
return ` |
||||
${'COMMAND : '.bold} |
||||
${'xc db.migrate.init'.bold} - Initialises a database migration project freshly - only necessary files are created |
||||
- This is a legacy command and will not be required in developer flow |
||||
- Instead use ${'xc new'.bold} |
||||
- After running this command - edit config.xc.json and run xc db.migrate.sync |
||||
|
||||
${'SYNOPSIS : '.bold} |
||||
${'xc db.migrate.init'.bold} ${'--type mysql|pg|mssql|sqlite3'.bold} |
||||
|
||||
${'VARIATIONS :'.bold} |
||||
|
||||
None |
||||
|
||||
${'Examples with notes : '.bold} |
||||
${'xc db.migrate.init --type mysql'.bold} |
||||
- Creates migration project of type mysql |
||||
- xc db.migrate.sync has to be run after command |
||||
` |
||||
} |
||||
|
||||
public static manDbMigrateSync() { |
||||
return ` |
||||
${'COMMAND : '.bold} |
||||
${'xc db.migrate.sync'.bold} - Initialises migrations in the databases of project |
||||
- Creates _evolutions table for database if it doesn't exists |
||||
|
||||
${'SYNOPSIS : '.bold} |
||||
${'xc db.migrate.sync'.bold} [${'--env'.bold} <environment>] [${'--dbAlias'.bold} <databaseAlias>] |
||||
|
||||
${'OPTIONS :'.bold} |
||||
|
||||
The following options are available: |
||||
${'--env'.bold} Defaults to 'dev' environment if not provided |
||||
${'--dbAlias'.bold} If not provided all databases within environment are initialised |
||||
|
||||
${'VARIATIONS :'.bold} |
||||
|
||||
None |
||||
|
||||
${'Examples with notes : '.bold} |
||||
${'xc db.migrate.sync'.bold} |
||||
- Initialises migration for all databases within 'dev' environment |
||||
|
||||
${'xc db.migrate.sync --env production'.bold} |
||||
- Initialises migration for all databases within 'production' environment |
||||
|
||||
${'xc db.migrate.sync --env production --dbAlias secondary'.bold} |
||||
- Initialises migration for 'db2'(secondary) database within 'production' environment |
||||
` |
||||
} |
||||
|
||||
public static manDbMigrateList() { |
||||
return ` |
||||
${'COMMAND : '.bold} |
||||
${'xc db.migrate.list'.bold} - Lists all the migrations |
||||
|
||||
${'SYNOPSIS : '.bold} |
||||
${'xc db.migrate.list'.bold} [${'--env'.bold} <environment>] [${'--dbAlias'.bold} <databaseAlias>] |
||||
|
||||
${'OPTIONS :'.bold} |
||||
|
||||
The following options are available: |
||||
${'--env'.bold} Defaults to 'dev' environment if not provided |
||||
${'--dbAlias'.bold} Defaults to 'db'(primary) environment if not provided |
||||
${'--all'.bold} List all migrations instead of just pending //todo
|
||||
|
||||
${'VARIATIONS :'.bold} |
||||
|
||||
None |
||||
|
||||
${'Examples with notes : '.bold} |
||||
${'xc db.migrate.list'.bold} |
||||
- Lists all migrations within 'dev' environment and 'db'(primary) database |
||||
|
||||
${'xc db.migrate.list --env production'.bold} |
||||
- Lists all migrations within 'production' environment and 'db'(primary) database |
||||
|
||||
${'xc db.migrate.list --env production --dbAlias secondary'.bold} |
||||
- Lists all migrations within 'production' environment and 'db2'(secondary) database |
||||
` |
||||
} |
||||
|
||||
public static manDbMigrateCreate() { |
||||
return ` |
||||
${'COMMAND : '.bold} |
||||
${'xc db.migrate.create'.bold} - Creates an empty migration for environment and dbAlias |
||||
|
||||
${'SYNOPSIS : '.bold} |
||||
${'xc db.migrate.create'.bold} [${'--env'.bold} <environment>] [${'--dbAlias'.bold} <databaseAlias>] |
||||
|
||||
${'OPTIONS :'.bold} |
||||
|
||||
The following options are available: |
||||
${'--env'.bold} Defaults to 'dev' environment if not provided |
||||
${'--dbAlias'.bold} Defaults to 'db'(primary) environment if not provided |
||||
|
||||
${'VARIATIONS :'.bold} |
||||
|
||||
None |
||||
|
||||
${'Examples with notes : '.bold} |
||||
${'xc db.migrate.create'.bold} |
||||
- Creates a migration within 'dev' environment and 'db'(primary) database |
||||
|
||||
${'xc db.migrate.create --env production'.bold} |
||||
- Creates a migration within 'production' environment and 'db'(primary) database |
||||
|
||||
${'xc db.migrate.create --env production --dbAlias secondary'.bold} |
||||
- Creates a migration within 'production' environment and 'db2'(secondary) database |
||||
` |
||||
} |
||||
|
||||
public static manDbMigrateUp() { |
||||
return ` |
||||
${'COMMAND : '.bold} |
||||
${'xc db.migrate.up'.bold} - Applies all pending migrations |
||||
|
||||
${'SYNOPSIS : '.bold} |
||||
${'xc db.migrate.up'.bold} [${'--env'.bold} <environment>] [${'--dbAlias'.bold} <databaseAlias>] \n\t\t\t[${'--steps'.bold} <number>] [${'--file'.bold} <number>] |
||||
|
||||
${'OPTIONS :'.bold} |
||||
|
||||
The following options are available: |
||||
${'--steps'.bold} Number of pending migrations to apply |
||||
${'--file'.bold} Filename till migrations has to be applied |
||||
${'--env'.bold} Defaults to 'dev' environment if not provided |
||||
${'--dbAlias'.bold} Defaults to 'db'(primary) environment if not provided |
||||
${'--sqlContentMigrate'.bold} Defaults to 1 |
||||
On '0' doesn't apply SQL contents |
||||
but updates _evolutions table that migration has been applied |
||||
|
||||
${'VARIATIONS :'.bold} |
||||
|
||||
None |
||||
|
||||
${'Examples with notes : '.bold} |
||||
${'xc db.migrate.up'.bold} |
||||
- Applies all pending migrations |
||||
|
||||
${'xc db.migrate.up --env production --steps 1'.bold} |
||||
- Applies one pending migrations in 'production' environment |
||||
|
||||
${'xc db.migrate.up --env production --dbAlias secondary --steps 1'.bold} |
||||
- Applies one pending migrations in 'production' environment for 'db2'(secondary) database |
||||
` |
||||
} |
||||
|
||||
public static manDbMigrateDown() { |
||||
return ` |
||||
${'COMMAND : '.bold} |
||||
${'xc db.migrate.down'.bold} - Reverses all migrations or by steps or by till the filename |
||||
|
||||
${'SYNOPSIS : '.bold} |
||||
${'xc db.migrate.down'.bold} [${'--env'.bold} <environment>] [${'--dbAlias'.bold} <databaseAlias>] \n\t\t\t[${'--steps'.bold} <number>] [${'--file'.bold} <number>] |
||||
|
||||
${'OPTIONS :'.bold} |
||||
|
||||
The following options are available: |
||||
${'--steps'.bold} Number of applied migrations to apply |
||||
${'--file'.bold} Filename till migrations has to be applied |
||||
${'--env'.bold} Defaults to 'dev' environment if not provided |
||||
${'--dbAlias'.bold} Defaults to 'db'(primary) environment if not provided |
||||
${'--sqlContentMigrate'.bold} Defaults to 1 |
||||
On '0' doesn't apply SQL contents |
||||
but updates _evolutions table that migration has been applied |
||||
|
||||
${'VARIATIONS :'.bold} |
||||
|
||||
None |
||||
|
||||
${'Examples with notes : '.bold} |
||||
${'xc db.migrate.down'.bold} |
||||
- Reverses all applied migrations |
||||
|
||||
${'xc db.migrate.down --env production --steps 1'.bold} |
||||
- Reverses one applied migrations in 'production' environment |
||||
|
||||
${'xc db.migrate.down --env production --dbAlias secondary --steps 1'.bold} |
||||
- Reverses one applied migrations in 'production' environment for 'db2'(secondary) database |
||||
` |
||||
} |
||||
|
||||
|
||||
public static manDbMigrateTerm() { |
||||
return ` |
||||
${'COMMAND : '.bold} |
||||
${'xc db.migrate.term'.bold} - Deprecated. Terminates all databases in environment. |
||||
` |
||||
} |
||||
|
||||
public static manComponentAdd() { |
||||
return ` |
||||
${'COMMAND : '.bold} |
||||
${'xc component.add'.bold} - Adds a new component in server/components directory by taking user input |
||||
on where to place the component in initialisation order. |
||||
|
||||
${'SYNOPSIS : '.bold} |
||||
${'xc component.add'.bold} <componentName> |
||||
|
||||
${'ARGS :'.bold} |
||||
|
||||
The following are arguments to function: |
||||
${'componentName'.bold} Name of the new component |
||||
|
||||
${'OPTIONS :'.bold} |
||||
|
||||
The following options are available: |
||||
None |
||||
|
||||
${'VARIATIONS :'.bold} |
||||
|
||||
None |
||||
|
||||
${'Examples with notes : '.bold} |
||||
${'xc component.add cache'.bold} |
||||
- Adds a cache component |
||||
` |
||||
} |
||||
|
||||
public static manAppInstall() { |
||||
return ` |
||||
${'COMMAND : '.bold} |
||||
${'xc app.install'.bold} - Download and starts xgenecloud desktop application installation |
||||
|
||||
${'SYNOPSIS : '.bold} |
||||
${'xc app.install'.bold} |
||||
|
||||
${'OPTIONS :'.bold} |
||||
|
||||
The following options are available: |
||||
None |
||||
|
||||
${'VARIATIONS :'.bold} |
||||
|
||||
None |
||||
|
||||
${'Examples with notes : '.bold} |
||||
${'xc app.install'.bold} |
||||
` |
||||
} |
||||
|
||||
public static manAppOpen() { |
||||
return ` |
||||
${'COMMAND : '.bold} |
||||
${'xc app.open'.bold} - Opens the xgenecloud desktop application (mac and linux only) |
||||
|
||||
${'SYNOPSIS : '.bold} |
||||
${'xc app.open'.bold} |
||||
|
||||
${'OPTIONS :'.bold} |
||||
|
||||
The following options are available: |
||||
None |
||||
|
||||
${'VARIATIONS :'.bold} |
||||
|
||||
None |
||||
|
||||
${'Examples with notes : '.bold} |
||||
${'xc app.open'.bold} |
||||
` |
||||
} |
||||
|
||||
|
||||
public static manPermissionsSet() { |
||||
return ` |
||||
${'COMMAND : '.bold} |
||||
${'xc permissions.set'.bold} - Sets model(s) permission(s) for user role(s) |
||||
|
||||
${'SYNOPSIS : '.bold} |
||||
# For REST API project |
||||
${'xc permissions.set'.bold} [${'model(s)'.bold} | $] [${'userType(s)'.bold} | $] [${'read|write)'.bold} = 1 | 0] |
||||
|
||||
# For GraphQL API project |
||||
${'xc permissions.set'.bold} [${'model(s)'.bold} | $] [${'userType(s)'.bold} | $] [${'read|write|resolverFunction)'.bold} = 1 | 0] |
||||
|
||||
${'ARGS :'.bold} |
||||
|
||||
The following are arguments to function: |
||||
${'model(s)'.bold} Model name | Multiple models with dot seperated | $ - means all models |
||||
${'userType(s)'.bold}User role name | Multiple user role names with dot separated | $ - means all user models |
||||
${'permissions(s)'.bold}Permissions - read | write | get | post | put | delete | patch | resolverFunc | $ - means all permissions |
||||
${'VARIATIONS :'.bold} |
||||
|
||||
None |
||||
|
||||
${'Examples with notes : '.bold} |
||||
${'xc permissions.set blog guest read=1'.bold} |
||||
- For 'blog' model 'guest' user type SET 'read' permission |
||||
|
||||
${'xc permissions.set $ guest read=1'.bold} |
||||
- For 'all' models 'guest' user type SET 'read' permission |
||||
|
||||
${'xc permissions.set $ $ read=1'.bold} |
||||
- For 'all' models 'all' user types SET 'read' permission |
||||
|
||||
${'xc permissions.set $ $ read=1 write=1'.bold} |
||||
- For 'all' models 'all' user types SET 'read' and 'write' permission |
||||
` |
||||
} |
||||
|
||||
// todo
|
||||
public static manPermissionsGet() { |
||||
return ` |
||||
${'COMMAND : '.bold} |
||||
${'xc permissions.get'.bold} - Gets model permission(s) for all the user roles |
||||
|
||||
${'SYNOPSIS : '.bold} |
||||
# For REST API project |
||||
${'xc permissions.get'.bold} [${'model'.bold}] |
||||
|
||||
# For GraphQL API project |
||||
${'xc permissions.get'.bold} [${'model(s)'.bold} ] |
||||
|
||||
${'ARGS :'.bold} |
||||
|
||||
The following are arguments to function: |
||||
${'model'.bold} Model name |
||||
|
||||
${'VARIATIONS :'.bold} |
||||
|
||||
None |
||||
|
||||
${'Examples with notes : '.bold} |
||||
${'xc permissions.get blog'.bold} |
||||
- Get all permissions of blog model ` |
||||
} |
||||
|
||||
|
||||
public static manPermissionsUserAdd() { |
||||
return ` |
||||
${'COMMAND : '.bold} |
||||
${'xc permissions.role.add'.bold} - Add a new user role for all models |
||||
read is set default to 1 |
||||
write is set default to 1 |
||||
|
||||
${'SYNOPSIS : '.bold} |
||||
${'xc permissions.role.add'.bold} ${'userRole'.bold} <userRole> |
||||
|
||||
${'ARGS :'.bold} |
||||
|
||||
The following are the arguments : |
||||
|
||||
${'userRole'.bold} new user role |
||||
|
||||
${'VARIATIONS :'.bold} |
||||
|
||||
None |
||||
|
||||
${'Examples with notes : '.bold} |
||||
${'xc permissions.role.add manager'.bold} |
||||
- Adds user role 'manager' to all the models |
||||
|
||||
` |
||||
} |
||||
|
||||
|
||||
public static manPermissionsUserDelete() { |
||||
return ` |
||||
${'COMMAND : '.bold} |
||||
${'xc permissions.role.delete'.bold} - Delete user role from all models |
||||
|
||||
${'SYNOPSIS : '.bold} |
||||
${'xc permissions.role.delete'.bold} <userRole> |
||||
|
||||
${'ARGS :'.bold} |
||||
|
||||
The following are the arguments : |
||||
|
||||
${'userRole'.bold} User role to be deleted |
||||
|
||||
${'VARIATIONS :'.bold} |
||||
|
||||
None |
||||
|
||||
${'Examples with notes : '.bold} |
||||
${'xc permissions.role.delete manager'.bold} |
||||
- deletes user role 'manager' from all the models |
||||
|
||||
` |
||||
} |
||||
|
||||
|
||||
public static manPermissionsUserRename() { |
||||
return ` |
||||
${'COMMAND : '.bold} |
||||
${'xc permissions.role.rename'.bold} - renames an existing user role |
||||
|
||||
${'SYNOPSIS : '.bold} |
||||
${'xc permissions.role.rename'.bold} <oldUserRoleName> <newUserRoleName> |
||||
|
||||
${'ARGS :'.bold} |
||||
|
||||
The following are the arguments : |
||||
|
||||
${'oldUserRoleName'.bold} old user role |
||||
${'newUserRoleName'.bold} new user role |
||||
|
||||
${'VARIATIONS :'.bold} |
||||
|
||||
None |
||||
|
||||
${'Examples with notes : '.bold} |
||||
${'xc permissions.role.rename manager StaffManager'.bold} |
||||
- Renames user role from 'manager' to 'StaffManager' in all the models |
||||
|
||||
` |
||||
} |
||||
|
||||
|
||||
public static manProjectMetaExport() { |
||||
return ` |
||||
${'COMMAND : '.bold} |
||||
${'xc meta.export'.bold} - Exports all meta table data to project meta directory |
||||
|
||||
${'SYNOPSIS : '.bold} |
||||
${'xc meta.export'.bold} ${'--env'.bold} <environment> [${'--dbAlias'.bold} <databaseAlias>] |
||||
|
||||
${'OPTIONS :'.bold} |
||||
|
||||
The following options are available: |
||||
${'--env'.bold} Environment name to be exported
|
||||
${'--dbAlias'.bold} dbAlias of database connection. If not provided then it will export metadata for all connections in the environment |
||||
|
||||
${'VARIATIONS :'.bold} |
||||
|
||||
None |
||||
|
||||
${'Examples with notes : '.bold} |
||||
${'xc meta.export --env dev'.bold} |
||||
- Exports metadata for all databases in 'dev' environment |
||||
|
||||
${'xc meta.export --env production --dbAlias db2'.bold} |
||||
- Exports metadata for 'db2' database in 'production' environment |
||||
` |
||||
|
||||
} |
||||
|
||||
public static manProjectMetaReset() { |
||||
return ` |
||||
${'COMMAND : '.bold} |
||||
${'xc meta.reset'.bold} - Clears all meta table data |
||||
|
||||
${'SYNOPSIS : '.bold} |
||||
${'xc meta.reset'.bold} ${'--env'.bold} <environment> [${'--dbAlias'.bold} <databaseAlias>] |
||||
|
||||
${'OPTIONS :'.bold} |
||||
|
||||
The following options are available: |
||||
${'--env'.bold} Environment name to be reset
|
||||
${'--dbAlias'.bold} dbAlias of database connection. If not provided then it will clear metadata for all connections in the environment |
||||
|
||||
${'VARIATIONS :'.bold} |
||||
|
||||
None |
||||
|
||||
${'Examples with notes : '.bold} |
||||
${'xc meta.reset --env dev'.bold} |
||||
- Clears metadata for all databases in 'dev' environment |
||||
|
||||
${'xc meta.reset --env production --dbAlias db2'.bold} |
||||
- Clears metadata for 'db2' database in 'production' environment |
||||
` |
||||
|
||||
} |
||||
|
||||
public static manProjectMetaImport() { |
||||
return ` |
||||
${'COMMAND : '.bold} |
||||
${'xc meta.import'.bold} - Imports data from project meta folder to meta tables |
||||
|
||||
${'SYNOPSIS : '.bold} |
||||
${'xc meta.import'.bold} ${'--env'.bold} <environment> [${'--dbAlias'.bold} <databaseAlias>] |
||||
|
||||
${'OPTIONS :'.bold} |
||||
|
||||
The following options are available: |
||||
${'--env'.bold} Environment name to be imported
|
||||
${'--dbAlias'.bold} dbAlias of database connection. If not provided then it will import metadata to all connections in the environment |
||||
|
||||
${'VARIATIONS :'.bold} |
||||
|
||||
None |
||||
|
||||
${'Examples with notes : '.bold} |
||||
${'xc meta.import --env dev'.bold} |
||||
- Imports metadata to all databases in 'dev' environment |
||||
|
||||
${'xc meta.import --env production --dbAlias db2'.bold} |
||||
- Imports metadata for 'db2' database in 'production' environment |
||||
` |
||||
|
||||
} |
||||
|
||||
|
||||
public static showHelpForCommand(args) { |
||||
|
||||
|
||||
try { |
||||
switch (args._[1]) { |
||||
|
||||
case 'new' : |
||||
console.log(Util.manNew()); |
||||
break; |
||||
|
||||
// case 'ga':
|
||||
// case 'gar':
|
||||
// case 'gen.apis':
|
||||
// case 'gen.apis.rest':
|
||||
// console.log(Util.manGenApisRest());
|
||||
// break;
|
||||
//
|
||||
// case 'gag':
|
||||
// case 'gen.apis.graphql':
|
||||
// case 'gen.apis.gql':
|
||||
// console.log(Util.manGenApisGraphql());
|
||||
// break;
|
||||
//
|
||||
// case 'gm':
|
||||
// case 'gen.module':
|
||||
// console.log(Util.manGenModule());
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// /**************** START : permissions stuff ****************/
|
||||
// case 'permissions.set' :
|
||||
// case 'ps' :
|
||||
// console.log(Util.manPermissionsSet());
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// case 'permissions.get' :
|
||||
// case 'pg' :
|
||||
// console.log(Util.manPermissionsGet());
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// case 'permissions.role.add' :
|
||||
// case 'pra' :
|
||||
// console.log(Util.manPermissionsUserAdd());
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// case 'permissions.role.rename' :
|
||||
// case 'prr' :
|
||||
// console.log(Util.manPermissionsUserRename());
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// case 'permissions.role.delete' :
|
||||
// case 'prd' :
|
||||
// console.log(Util.manPermissionsUserDelete());
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// /**************** END : permissions stuff ****************/
|
||||
//
|
||||
// /**************** START : Migration stuff ****************/
|
||||
// case 'db.migrate.init' :
|
||||
// case 'dmi' :
|
||||
// console.log(Util.manDbMigrateInit());
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// case 'db.migrate.sync' :
|
||||
// console.log(Util.manDbMigrateSync());
|
||||
// break;
|
||||
// case 'dms' :
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// case 'db.migrate.list' :
|
||||
// case 'dml' :
|
||||
// console.log(Util.manDbMigrateList());
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// case 'db.migrate.create' :
|
||||
// case 'dmc' :
|
||||
// console.log(Util.manDbMigrateCreate());
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// case 'db.migrate.up' :
|
||||
// case 'dmu' :
|
||||
// console.log(Util.manDbMigrateUp());
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// case 'db.migrate.down' :
|
||||
// case 'dmd' :
|
||||
// console.log(Util.manDbMigrateDown());
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// case 'db.migrate.term' :
|
||||
// case 'dmt' :
|
||||
// console.log(Util.manDbMigrateTerm());
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// case 'db.migrate.sql.dump' :
|
||||
// case 'dmsd' :
|
||||
// break;
|
||||
// /**************** END : Migration stuff ****************/
|
||||
// /**************** START : Project Meta stuff ****************/
|
||||
//
|
||||
// case 'meta.export' :
|
||||
// case 'me' :
|
||||
// console.log(Util.manProjectMetaExport());
|
||||
// process.exit(0);
|
||||
// break;
|
||||
// case 'meta.import' :
|
||||
// case 'mi' :
|
||||
// console.log(Util.manProjectMetaImport());
|
||||
// process.exit(0);
|
||||
// break;
|
||||
// case 'meta.reset' :
|
||||
// case 'mr' :
|
||||
// console.log(Util.manProjectMetaReset());
|
||||
// process.exit(0);
|
||||
// break;
|
||||
// /**************** END : Project Meta stuff ****************/
|
||||
//
|
||||
//
|
||||
// /**************** START : app stuff ****************/
|
||||
// case 'app.install' :
|
||||
// case 'ai' :
|
||||
// console.log(Util.manDbMigrateTerm());
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// case 'app.open' :
|
||||
// case 'ao' :
|
||||
// console.log(Util.manDbMigrateTerm());
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// /**************** END : app stuff ****************/
|
||||
//
|
||||
// /**************** START : Component stuff ****************/
|
||||
// case 'component.add' :
|
||||
// case 'ca' :
|
||||
// console.log(Util.manComponentAdd());
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// /**************** END : Component stuff ****************/
|
||||
//
|
||||
//
|
||||
// /**************** START : Docker ****************/
|
||||
// case 'docker' :
|
||||
// case 'd' :
|
||||
// console.log(Util.manDocker());
|
||||
// process.exit(0);
|
||||
// break;
|
||||
//
|
||||
// /**************** END : Docker ****************/
|
||||
|
||||
|
||||
default: |
||||
break; |
||||
} |
||||
} catch (e) { |
||||
throw e; |
||||
} |
||||
|
||||
} |
||||
|
||||
public static async runCmd(str) { |
||||
shell.echo(`\nNow, executing command : ${str}\n\n`.blue); |
||||
if (shell.exec(str).code !== 0) { |
||||
shell.echo(`\n\nError running command internally\n\n\t"${str}"`.red); |
||||
shell.echo(`\nExiting...`.red); |
||||
shell.exit(1); |
||||
} |
||||
} |
||||
|
||||
public async play(sound) { |
||||
|
||||
switch (sound) { |
||||
|
||||
case -1: |
||||
break; |
||||
|
||||
case 'fun': |
||||
break; |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
export default Util; |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* 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/>. |
||||
* |
||||
*/ |
@ -0,0 +1,25 @@
|
||||
export default { |
||||
port: 23917, |
||||
} |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* 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/>. |
||||
* |
||||
*/ |
@ -0,0 +1,5 @@
|
||||
{ |
||||
"language": "English", |
||||
"symbol": "en", |
||||
"text": "Enter database host name || Choose SQL Database type || Enter database username || Enter database password || Enter database port number || Enter database/schema name || Enter API type to generate || How do you want to run it" |
||||
} |
@ -0,0 +1,46 @@
|
||||
{ |
||||
"compilerOptions": { |
||||
"target": "es2017", |
||||
"outDir": "build/main", |
||||
"rootDir": "src", |
||||
"moduleResolution": "node", |
||||
"module": "commonjs", |
||||
"declaration": false, |
||||
"inlineSourceMap": true, |
||||
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, |
||||
|
||||
// "strict": true /* Enable all strict type-checking options. */, |
||||
|
||||
/* Strict Type-Checking Options */ |
||||
// "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, |
||||
// "strictNullChecks": true /* Enable strict null checks. */, |
||||
// "strictFunctionTypes": true /* Enable strict checking of function types. */, |
||||
// "strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */, |
||||
// "noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */, |
||||
// "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */, |
||||
|
||||
/* Additional Checks */ |
||||
"noUnusedLocals": true /* Report errors on unused locals. */, |
||||
"noUnusedParameters": true /* Report errors on unused parameters. */, |
||||
"noImplicitReturns": true /* Report error when not all code paths in function return a value. */, |
||||
"noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, |
||||
|
||||
/* Debugging Options */ |
||||
"traceResolution": false /* Report module resolution log messages. */, |
||||
"listEmittedFiles": false /* Print names of generated files part of the compilation. */, |
||||
"listFiles": false /* Print names of files part of the compilation. */, |
||||
"pretty": true /* Stylize errors and messages using color and context. */, |
||||
"resolveJsonModule": true, |
||||
/* Experimental Options */ |
||||
// "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */, |
||||
// "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */, |
||||
|
||||
"lib": ["es2017","es2015"], |
||||
"types": ["node"], |
||||
"typeRoots": ["node_modules/@types", "src/types"] |
||||
}, |
||||
|
||||
"include": ["src/**/*.ts"], |
||||
"exclude": ["node_modules/**"], |
||||
"compileOnSave": false |
||||
} |
@ -0,0 +1,11 @@
|
||||
{ |
||||
"extends": "./tsconfig", |
||||
"compilerOptions": { |
||||
"target": "esnext", |
||||
"outDir": "build/module", |
||||
"module": "esnext" |
||||
}, |
||||
"exclude": [ |
||||
"node_modules/**" |
||||
] |
||||
} |
@ -0,0 +1,47 @@
|
||||
{ |
||||
"extends": [ |
||||
"tslint:latest", |
||||
"tslint-config-prettier", |
||||
"tslint-immutable" |
||||
], |
||||
"rules": { |
||||
"interface-name": [ |
||||
true, |
||||
"never-prefix" |
||||
], |
||||
// TODO: allow devDependencies only in **/*.spec.ts files: |
||||
// waiting on https://github.com/palantir/tslint/pull/3708 |
||||
"no-implicit-dependencies": [ |
||||
true, |
||||
"dev" |
||||
], |
||||
/* tslint-immutable rules */ |
||||
// Recommended built-in rules |
||||
"no-var-keyword": true, |
||||
"no-parameter-reassignment": true, |
||||
// "typedef": [ |
||||
// true, |
||||
// "call-signature" |
||||
// ], |
||||
// Immutability rules |
||||
// "readonly-keyword": true, |
||||
// "readonly-array": true, |
||||
// "no-let": true, |
||||
// "no-object-mutation": true, |
||||
// "no-delete": true, |
||||
"no-method-signature": true, |
||||
"no-console": false, |
||||
// Functional style rules |
||||
// "no-this": true, |
||||
// "no-class": true, |
||||
"no-mixed-interface": true, |
||||
"no-bitwise": false, |
||||
"variable-name": false |
||||
// "no-expression-statement": [ |
||||
// true, |
||||
// { "ignore-prefix": ["console.", "process.exit"] } |
||||
// ], |
||||
// "no-if-statement": true |
||||
/* end tslint-immutable rules */ |
||||
} |
||||
} |
@ -0,0 +1,70 @@
|
||||
const nodeExternals = require('webpack-node-externals'); |
||||
// const CopyPlugin = require('copy-webpack-plugin');
|
||||
//
|
||||
// const TerserPlugin = require('terser-webpack-plugin');
|
||||
const webpack = require('webpack'); |
||||
const JavaScriptObfuscator = require('webpack-obfuscator'); |
||||
const path = require('path'); |
||||
module.exports = { |
||||
entry: './src/index.ts', |
||||
module: { |
||||
rules: [ |
||||
{ |
||||
test: /\.tsx?$/, |
||||
exclude: /node_modules/, |
||||
use: { |
||||
loader: 'ts-loader', |
||||
options: { |
||||
transpileOnly: true |
||||
} |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
|
||||
optimization: { |
||||
minimize: false, //Update this to true or false
|
||||
// minimizer: [new TerserPlugin()],
|
||||
nodeEnv: false |
||||
}, |
||||
externals: [nodeExternals()], |
||||
resolve: { |
||||
extensions: ['.tsx', '.ts', '.js', '.json'], |
||||
}, |
||||
output: { |
||||
filename: 'bundle.js', |
||||
path: path.resolve(__dirname, 'dist'), |
||||
library: 'libs', |
||||
libraryTarget: 'umd', |
||||
globalObject: "typeof self !== 'undefined' ? self : this", |
||||
}, |
||||
// node: {
|
||||
// fs: 'empty'
|
||||
// },
|
||||
plugins: [ |
||||
|
||||
new JavaScriptObfuscator({ |
||||
rotateStringArray: true, |
||||
splitStrings: true, |
||||
splitStringsChunkLength: 6 |
||||
}, []), |
||||
|
||||
new webpack.BannerPlugin({banner: "#! /usr/bin/env node", raw: true}), |
||||
// new CopyPlugin({
|
||||
// patterns: [
|
||||
// // {
|
||||
// // from: "src/sample-db/chinook.sqlite",
|
||||
// // to: 'chinook.sqlite',
|
||||
// // // flatten: true,
|
||||
// // },
|
||||
// {
|
||||
// from: "src/sample-db/sakila.db",
|
||||
// to: 'sakila.db',
|
||||
// // flatten: true,
|
||||
// },
|
||||
// ],
|
||||
// })
|
||||
], |
||||
|
||||
target: 'node', |
||||
}; |
@ -0,0 +1,64 @@
|
||||
# https://circleci.com/docs/2.0/language-javascript/ |
||||
version: 2 |
||||
jobs: |
||||
'node-10': |
||||
docker: |
||||
- image: circleci/node:10 |
||||
steps: |
||||
- checkout |
||||
# Download and cache dependencies |
||||
- restore_cache: |
||||
keys: |
||||
- v1-dependencies-{{ checksum "package.json" }} |
||||
# fallback to using the latest cache if no exact match is found |
||||
- v1-dependencies- |
||||
- run: npm install |
||||
- save_cache: |
||||
paths: |
||||
- node_modules |
||||
key: v1-dependencies-{{ checksum "package.json" }} |
||||
- run: npm test |
||||
- run: npm run cov:send |
||||
- run: npm run cov:check |
||||
'node-12': |
||||
docker: |
||||
- image: circleci/node:12 |
||||
steps: |
||||
- checkout |
||||
- restore_cache: |
||||
keys: |
||||
- v1-dependencies-{{ checksum "package.json" }} |
||||
- v1-dependencies- |
||||
- run: npm install |
||||
- save_cache: |
||||
paths: |
||||
- node_modules |
||||
key: v1-dependencies-{{ checksum "package.json" }} |
||||
- run: npm test |
||||
- run: npm run cov:send |
||||
- run: npm run cov:check |
||||
'node-latest': |
||||
docker: |
||||
- image: circleci/node:latest |
||||
steps: |
||||
- checkout |
||||
- restore_cache: |
||||
keys: |
||||
- v1-dependencies-{{ checksum "package.json" }} |
||||
- v1-dependencies- |
||||
- run: npm install |
||||
- save_cache: |
||||
paths: |
||||
- node_modules |
||||
key: v1-dependencies-{{ checksum "package.json" }} |
||||
- run: npm test |
||||
- run: npm run cov:send |
||||
- run: npm run cov:check |
||||
|
||||
workflows: |
||||
version: 2 |
||||
build: |
||||
jobs: |
||||
- 'node-10' |
||||
- 'node-12' |
||||
- 'node-latest' |
@ -0,0 +1,34 @@
|
||||
{ |
||||
"version": "0.1", |
||||
"$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/master/cspell.schema.json", |
||||
"language": "en", |
||||
"words": [ |
||||
"bitjson", |
||||
"bitauth", |
||||
"cimg", |
||||
"circleci", |
||||
"codecov", |
||||
"commitlint", |
||||
"dependabot", |
||||
"editorconfig", |
||||
"esnext", |
||||
"execa", |
||||
"exponentiate", |
||||
"globby", |
||||
"libauth", |
||||
"mkdir", |
||||
"prettierignore", |
||||
"sandboxed", |
||||
"transpiled", |
||||
"typedoc", |
||||
"untracked" |
||||
], |
||||
"flagWords": [], |
||||
"ignorePaths": [ |
||||
"package.json", |
||||
"package-lock.json", |
||||
"yarn.lock", |
||||
"tsconfig.json", |
||||
"node_modules/**" |
||||
] |
||||
} |
@ -0,0 +1,15 @@
|
||||
# http://editorconfig.org |
||||
root = true |
||||
|
||||
[*] |
||||
charset = utf-8 |
||||
end_of_line = lf |
||||
indent_size = 2 |
||||
indent_style = space |
||||
insert_final_newline = true |
||||
max_line_length = 80 |
||||
trim_trailing_whitespace = true |
||||
|
||||
[*.md] |
||||
max_line_length = 0 |
||||
trim_trailing_whitespace = false |