From 5fd50792a0a1b601d66f0c0bde70908e02eaa306 Mon Sep 17 00:00:00 2001 From: Cheney Date: Thu, 7 Mar 2024 15:50:13 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20kit=20=E5=AD=90=E7=9B=AE?= =?UTF-8?q?=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kit/LICENSE | 661 +++++++++++++++++++++++++++++++++ kit/package-lock.json | 555 +++++++++++++++++++++++++++ kit/package.json | 19 + kit/src/config/SystemConfig.js | 94 +++++ kit/src/config/UserConfig.js | 52 +++ kit/src/config/index.js | 14 + kit/src/context/index.js | 33 ++ kit/src/crypto.js | 18 + kit/src/index.js | 320 ++++++++++++++++ kit/src/init.js | 101 +++++ kit/src/linux/local.js | 5 + kit/src/linux/net.js | 33 ++ kit/src/net.js | 84 +++++ kit/src/remote/index.js | 228 ++++++++++++ kit/src/remote/install.js | 53 +++ kit/src/remote/publish.js | 79 ++++ kit/src/shell/index.js | 27 ++ kit/src/util/convutil.js | 551 +++++++++++++++++++++++++++ kit/src/util/objutil.js | 6 + kit/src/util/osutil.js | 51 +++ kit/src/windows/local.js | 38 ++ kit/src/windows/net.js | 40 ++ kit/test/net_test.js | 6 + kit/test/webdav_test.js | 75 ++++ 24 files changed, 3143 insertions(+) create mode 100644 kit/LICENSE create mode 100644 kit/package-lock.json create mode 100644 kit/package.json create mode 100644 kit/src/config/SystemConfig.js create mode 100644 kit/src/config/UserConfig.js create mode 100644 kit/src/config/index.js create mode 100644 kit/src/context/index.js create mode 100644 kit/src/crypto.js create mode 100644 kit/src/index.js create mode 100644 kit/src/init.js create mode 100644 kit/src/linux/local.js create mode 100644 kit/src/linux/net.js create mode 100644 kit/src/net.js create mode 100644 kit/src/remote/index.js create mode 100644 kit/src/remote/install.js create mode 100644 kit/src/remote/publish.js create mode 100644 kit/src/shell/index.js create mode 100644 kit/src/util/convutil.js create mode 100644 kit/src/util/objutil.js create mode 100644 kit/src/util/osutil.js create mode 100644 kit/src/windows/local.js create mode 100644 kit/src/windows/net.js create mode 100644 kit/test/net_test.js create mode 100644 kit/test/webdav_test.js diff --git a/kit/LICENSE b/kit/LICENSE new file mode 100644 index 0000000..dbbe355 --- /dev/null +++ b/kit/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. + + + Copyright (C) + + 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 . + +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 +. diff --git a/kit/package-lock.json b/kit/package-lock.json new file mode 100644 index 0000000..8bebcde --- /dev/null +++ b/kit/package-lock.json @@ -0,0 +1,555 @@ +{ + "name": "kit", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "kit", + "version": "1.0.0", + "dependencies": { + "cross-port-killer": "^1.4.0", + "extract-zip": "^2.0.1", + "get-port": "^7.0.0", + "json5": "^2.2.3", + "log4js": "^6.9.1", + "sm-crypto": "^0.3.13", + "webdav": "^5.3.1" + }, + "bin": { + "kit": "src/index.js" + } + }, + "node_modules/@buttercup/fetch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@buttercup/fetch/-/fetch-0.1.2.tgz", + "integrity": "sha512-mDBtsysQ0Gnrp4FamlRJGpu7HUHwbyLC4uUav1I7QAqThFAa/4d1cdZCxrV5gKvh6zO1fu95bILNJi4Y2hALhQ==", + "optionalDependencies": { + "node-fetch": "^3.3.0" + } + }, + "node_modules/@types/node": { + "version": "20.10.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.8.tgz", + "integrity": "sha512-f8nQs3cLxbAFc00vEU59yf9UyGUftkPaLGfvbVOIDdx2i1b8epBqj2aNGyP19fiyXWvlmZ7qC1XLjAzw/OKIeA==", + "optional": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base-64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", + "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==" + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "engines": { + "node": "*" + } + }, + "node_modules/byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/byte-length/-/byte-length-1.0.2.tgz", + "integrity": "sha512-ovBpjmsgd/teRmgcPh23d4gJvxDoXtAzEL9xTfMU8Yc2kqCDb7L9jAG0XHl1nzuGl+h3ebCIF1i62UFyA9V/2Q==" + }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "engines": { + "node": "*" + } + }, + "node_modules/cross-port-killer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cross-port-killer/-/cross-port-killer-1.4.0.tgz", + "integrity": "sha512-ujqfftKsSeorFMVI6JP25xMBixHEaDWVK+NarRZAGnJjR5AhebRQU+g+k/Lj8OHwM6f+wrrs8u5kkCdI7RLtxQ==", + "bin": { + "kill-port": "source/cli.js" + } + }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "engines": { + "node": "*" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/date-format": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", + "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/fast-xml-parser": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.3.3.tgz", + "integrity": "sha512-coV/D1MhrShMvU6D0I+VAK3umz6hUaxxhL0yp/9RjfiYUfAv14rDhGQL+PLForhMdr0wq3PiV07WtkkNjJjNHg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "optional": true, + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==" + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "optional": true, + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/get-port": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.0.0.tgz", + "integrity": "sha512-mDHFgApoQd+azgMdwylJrv2DX47ywGq1i5VFJE7fZ0dttNq3iQMfsU4IvEgBHojA3KqEudyu7Vq+oN8kNaNkWw==", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hot-patcher": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hot-patcher/-/hot-patcher-2.0.1.tgz", + "integrity": "sha512-ECg1JFG0YzehicQaogenlcs2qg6WsXQsxtnbr1i696u5tLUjtJdQAh0u2g0Q5YV45f263Ta1GnUJsc8WIfJf4Q==" + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/layerr": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/layerr/-/layerr-2.0.1.tgz", + "integrity": "sha512-z0730CwG/JO24evdORnyDkwG1Q7b7mF2Tp1qRQ0YvrMMARbt1DFG694SOv439Gm7hYKolyZyaB49YIrYIfZBdg==" + }, + "node_modules/log4js": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", + "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "flatted": "^3.2.7", + "rfdc": "^1.3.0", + "streamroller": "^3.1.5" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, + "node_modules/minimatch": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", + "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/nested-property": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/nested-property/-/nested-property-4.0.0.tgz", + "integrity": "sha512-yFehXNWRs4cM0+dz7QxCd06hTbWbSkV0ISsqBfkntU6TOY4Qm3Q88fRRLOddkGh2Qq6dZvnKVAahfhjcUvLnyA==" + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "optional": true, + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "optional": true, + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/path-posix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/path-posix/-/path-posix-1.0.0.tgz", + "integrity": "sha512-1gJ0WpNIiYcQydgg3Ed8KzvIqTsDpNwq+cjBCssvBtuTWjEqY1AW+i+OepiEMqDCzyro9B2sLAe4RBPajMYFiA==" + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "node_modules/rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" + }, + "node_modules/sm-crypto": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/sm-crypto/-/sm-crypto-0.3.13.tgz", + "integrity": "sha512-ztNF+pZq6viCPMA1A6KKu3bgpkmYti5avykRHbcFIdSipFdkVmfUw2CnpM2kBJyppIalqvczLNM3wR8OQ0pT5w==", + "dependencies": { + "jsbn": "^1.1.0" + } + }, + "node_modules/streamroller": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", + "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "optional": true + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/url-join": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz", + "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.2.tgz", + "integrity": "sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ==", + "optional": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/webdav": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/webdav/-/webdav-5.3.1.tgz", + "integrity": "sha512-wzZdTHtMuSIXqHGBznc8FM2L94Mc/17Tbn9ppoMybRO0bjWOSIeScdVXWX5qqHsg00EjfiOcwMqGFx6ghIhccQ==", + "dependencies": { + "@buttercup/fetch": "^0.1.1", + "base-64": "^1.0.0", + "byte-length": "^1.0.2", + "fast-xml-parser": "^4.2.4", + "he": "^1.2.0", + "hot-patcher": "^2.0.0", + "layerr": "^2.0.1", + "md5": "^2.3.0", + "minimatch": "^7.4.6", + "nested-property": "^4.0.0", + "path-posix": "^1.0.0", + "url-join": "^5.0.0", + "url-parse": "^1.5.10" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } +} diff --git a/kit/package.json b/kit/package.json new file mode 100644 index 0000000..8472ae6 --- /dev/null +++ b/kit/package.json @@ -0,0 +1,19 @@ +{ + "name": "kit", + "version": "0.1", + "bin": "src/index.js", + "scripts": { + "linux": "pkg --compress GZip . -t linux -o ./dist/linux/kit", + "windows": "pkg --compress GZip . -t win -o ./dist/windows/kit.exe", + "alpine" : "pkg --compress GZip . -t node14-alpine-arm64 -o ./dist/linux/kit" + }, + "dependencies": { + "cross-port-killer": "^1.4.0", + "extract-zip": "^2.0.1", + "get-port": "^7.0.0", + "json5": "^2.2.3", + "log4js": "^6.9.1", + "sm-crypto": "^0.3.13", + "webdav": "^5.3.1" + } +} diff --git a/kit/src/config/SystemConfig.js b/kit/src/config/SystemConfig.js new file mode 100644 index 0000000..068b157 --- /dev/null +++ b/kit/src/config/SystemConfig.js @@ -0,0 +1,94 @@ +/** + * 系统配置 + */ +const os = require('os'); +const process = require('process'); +const path = require("path"); +const fs = require('fs'); + +module.exports = class SystemConfig { + + constructor() { + } + + static isWindows(){ + return os.platform() === 'win32' + } + + static isLinux(){ + return os.platform() === 'linux' + } + + static getShellEncode(){ + if ( this.isWindows() ) { + return "GBK" + } else { + return "UTF-8" + } + } + + static getLocalKitPath(){ + let path = "./" + if ( this.isWindows() ) { + path = require('path').dirname(require.main.filename) + } else { + path = process.execPath; + } + this.affirmDir(path) + $logger.debug(`LocalKitPath=${path}`) + return path; + } + + static getLocalStorePath(){ + const path = require("path") + let dir = path.resolve(this.getLocalKitPath(), ".kit") + this.affirmDir(dir) + return dir; + } + + static getUserPath() { + let userDirectory = '~'; + if ( this.isWindows() ) { + userDirectory = process.env.USERPROFILE; + } + return userDirectory; + } + + static getUserConfigPath() { + const path = require("path") + const dir = path.resolve( this.getUserPath() , ".kit"); + this.affirmDir(dir) + return dir; + } + + static getUserConfigFilePath() { + const path = require("path") + return path.resolve( this.getUserPath() , ".kit", "config.json"); + } + + static getLogPath() { + const path = require("path") + let logs = path.resolve( this.getUserPath() , ".kit", "logs"); + this.affirmDir(logs) + return logs + } + + /** + * 确保目录存在 + * @param path + */ + static affirmDir(path){ + // 检查文件夹是否存在 + const exists = fs.existsSync(path); + + if (! exists) { + // 文件夹不存在 + try { + fs.mkdirSync(path, { recursive: true }); + } catch (err) { + // 处理错误 + log.error(`affirmDir ${path} error ${ err }`); + } + } + } +} \ No newline at end of file diff --git a/kit/src/config/UserConfig.js b/kit/src/config/UserConfig.js new file mode 100644 index 0000000..fb938f0 --- /dev/null +++ b/kit/src/config/UserConfig.js @@ -0,0 +1,52 @@ +/** + * 读写用户配置 + * 实时维护状态 + */ +const fs = require("fs") +const JSON5 = require('json5') +module.exports = class UserConfig { + constructor(path) { + this.path = path + this.init() + } + + init(){ + let txt = "{}"; + try { + txt = fs.readFileSync(this.path, 'utf8') + } catch ( err ) { + if (err.code === 'ENOENT') { + // File does not exist. + txt = '{}'; + } else { + // Some other error occurred. Throw it. + throw err; + } + } + + + this.configs = JSON5.parse( txt ) + this.setDefault("man", "https://flowus.cn/fullstack/share/acd5b26f-b3d1-4d41-a58e-e4f23daf9db8") + + } + + get(key) { + return this.configs[key] + } + + set(key, value) { + this.configs[key] = value + this.write() + } + + setDefault(key, value){ + if ( ! this.configs[key] ) { + this.configs[key] = value + } + } + + write(){ + fs.writeFileSync(this.path, JSON5.stringify(this.configs, undefined, 4)) + } + +} \ No newline at end of file diff --git a/kit/src/config/index.js b/kit/src/config/index.js new file mode 100644 index 0000000..61f8ef0 --- /dev/null +++ b/kit/src/config/index.js @@ -0,0 +1,14 @@ +/** + * config 模块处理配置相关事务 + */ +const SystemConfig = require("./SystemConfig") +const UserConfig = require("./UserConfig") + + +module.exports = { + SystemConfig, UserConfig +} + + + + diff --git a/kit/src/context/index.js b/kit/src/context/index.js new file mode 100644 index 0000000..5ab2731 --- /dev/null +++ b/kit/src/context/index.js @@ -0,0 +1,33 @@ +/** + * 对象管理 + * 懒加载对象 + */ +const config = require("../config"); +module.exports = class Context { + constructor() { + this.beans = {} + this.standalones = {} + } + standalone(name, factory){ + if ( this.standalones[name] ){ + throw `重定义 ${name}` + } + + if ( ! factory ) { + throw `定义 ${name} 时未提供必须参数 factory` + } + + this.standalones[name] = factory + } + async get(name){ + if ( this.beans[name] ) { + return this.beans[name] + } + if ( this.standalones[name] ){ + this.beans[name] = await this.standalones[name]() + return this.beans[name] + } else { + return undefined + } + } +} \ No newline at end of file diff --git a/kit/src/crypto.js b/kit/src/crypto.js new file mode 100644 index 0000000..296c9a0 --- /dev/null +++ b/kit/src/crypto.js @@ -0,0 +1,18 @@ +const sm4 = require('sm-crypto').sm4 +const sm3 = require('sm-crypto').sm3 + + +module.exports.sm4en3 = function (key, data){ + let sm3key = sm3(key) + sm3key = sm3key.substring(0, 32) + let encryptData = sm4.encrypt(data, sm3key) + return encryptData +} + + +module.exports.sm4de3 = function (key, data){ + let sm3key = sm3(key) + sm3key = sm3key.substring(0, 32) + let decryptData = sm4.decrypt(data, sm3key) + return decryptData +} \ No newline at end of file diff --git a/kit/src/index.js b/kit/src/index.js new file mode 100644 index 0000000..60ebc52 --- /dev/null +++ b/kit/src/index.js @@ -0,0 +1,320 @@ +/** + * kit cli 注册和调用执行 + */ +require("./init") +const { CliCmdParser } = require("../../argv") +const parser = new CliCmdParser("kit V" + $version); +const net = require("./net") +const shell = require("./shell") +const {sm4en3, sm4de3} = require("./crypto"); +const convutil = require("./util/convutil") +const path = require("path"); +const fs = require("fs"); +const {sm3} = require("sm-crypto"); +const zlib = require("zlib"); + +parser.addCmdLine("man", "打开在线帮助;", async function (){ + const uConfig = await $context.get("uConfig") + shell.execSync("start " + uConfig.get("man")) +}); +parser.addCmdLine("manu", "人工维护配置;", function (){ + shell.execSync("start " + $sConfig.getUserConfigPath()) +}); + +parser.addCmdLine("run [--file] [func]", "运行项目脚本;", function (cli) { + $logger.info( "kit 当前执行目录 " + process.cwd()); + const fs = require("fs"); + + let step = cli.getParamValue("func"); + if ( ! step ) { + step = "main" + } + + let file = cli.getParamValue("file"); + if ( ! file ) { + file = "kit" + } + + const stat = fs.existsSync(`./${file}.js`) + if ( ! stat ) { + if ( ! cli.getParamValue("file") ) { + fs.writeFileSync("./kit.js", + ` +async function main(){ + console.log("hello kit!") +} +`); + } else { + $logger.info( `文件 ${file} 不存在` ); + return + } + } + + + const projectScript = fs.readFileSync("./kit.js", "utf-8"); + const vm = require('node:vm'); + const script = new vm.Script(projectScript + ";" + step + "()"); + const context = { + animal: 'cat', + count: 2, + $logger + }; + + script.runInNewContext(context, { + timeout : 10 * 60 * 1000, + breakOnSigint : true, + }); + $logger.info( "暂未实现" ); +}); + +parser.addCmdLine("config [value]", "查询/设置配置;", async function (cli){ + let key = cli.getParamValue("key"); + let value = cli.getParamValue("value"); + const uConfig = await $context.get("uConfig") + if (null != key && null == value) { + let v = uConfig.get(key); + $logger.info("{} : {}", key, v); + } else if (null != key && null != value) { + uConfig.set(key, value); + $logger.info("set {} : {}", key, value); + } else { + $logger.error("参数异常"); + } +}); + +parser.addCmdLine("remote-check", "验证远程仓库;", async function (){ + let remote = await $context.get("remote") + if (! ( await remote.check()) ) { + $logger.error("远程仓库不可用") + return + } else { + $logger.info("远程仓库可用") + } +}); + +parser.addCmdLine("publish", "发布到远程仓库;", async function (){ + let remote = await $context.get("remote") + if (! ( await remote.check()) ) { + $logger.error("远程仓库不可用") + return + } else { + $logger.info("远程仓库可用") + } + remote.publish() +}); +parser.addCmdLine("install [module]", "安装/更新模块;", async function (cli){ + let remote = await $context.get("remote") + if (! ( await remote.check()) ) { + $logger.error("远程仓库不可用") + return + } else { + $logger.info("远程仓库可用") + } + remote.install(cli.getParamValue("module")) +}); + +parser.addCmdLine("killport ", "释放端口", function (cli) { + let port = cli.getParamValue("port") + net.killport( parseInt(port, 10) ); +}); + + +parser.addCmdLine("getport [--port]", "获取一个可用端口;或查询指定端口状态;", async function (cli) { + const getPort = await import('get-port').then(psl => psl.default); + let port = cli.getParamValue("port") + if( ! port ) { + let ret = await getPort() + $logger.info(`端口 ${ret} 未被使用`) + } else { + await net.getport( port ) + + } +}); + +parser.addCmdLine("checkip", "获取自己的公网 ip;", async function (cli) { + let ret = await net.checkip() + if( "ok" === ret.ret ) { + $logger.info( "ip : " + ret.data.ip ) + $logger.info( "location : " + ret.data.location.join(" ") ) + $logger.info("\n" + ret.data.ip) + } else { + $logger.error("获取失败") + } +}); + + +parser.addCmdLine("telnet [--timeout] [port]", "验证远程 TCP 端口是否可用;", async function (cli) { + let ip = cli.getParamValue("ip-port") + let port = 80; + + // 允许冒号形式指定端口 + const re = /^(?.*):(?[0-9]+)$/ + const match = ip.match( re ); + if ( match ) { + ip = match.groups.ip; + port = match.groups.port; + } else { + // 如果指定了 port 参数,那么使用 port 参数 + let dport = cli.getParamValue("port") + if ( dport ) { + port = dport; + } + } + + // 支持不填写 ip,默认访问本地 + if( "" === ip.trim() ) { + ip = "127.0.0.1"; + } + + try { + await net.telnet(port, ip, cli.getParamValue("timeout")) + $logger.info("连接成功") + }catch (e) { + $logger.error( e ) + } + +}); + +parser.addCmdLine("checklist ", "生成/验证文件 hash", async function (cli) { + let checkListFile = cli.getParamValue("checkListFile") + if ( !fs.existsSync( checkListFile ) ) { + $logger.error(`file ${checkListFile} 不存在`) + process.exit(-2) + } + const sm3 = require('sm-crypto').sm3 + const list = fs.readFileSync(checkListFile, "UTF-8") + const files = list.split('\n').filter(line => line.trim() !== ''); + for ( let file of files ) { + const infos = file.split(/\s+/) + const info = {} + if ( infos.length > 2 ) { + info.file = infos[0] + info.hash = infos[1] + info.alg = infos[2] + } else { + info.file = infos[0] + info.hash = infos[1] + info.alg = "md5" + } + if ( "sm3" === info.alg ) { + if ( !fs.existsSync( info.file ) ) { + $logger.error(`file ${info.file} 不存在`) + process.exit(-2) + } + + const data = fs.readFileSync(info.file); + const arrayBuffer = Buffer.from(data); + const uint8Array = new Uint8Array(arrayBuffer); + let hashData = sm3( uint8Array ) // 杂凑 + if ( hashData === info.hash ) { + $logger.info(`file ${file} 验证通过`) + } else { + $logger.error(`file ${file} 验证不通过`) + process.exit(-400) + } + + } else { + $logger.error(`不支持 file ${file} `) + process.exit(-2) + } + } + + $logger.info(`${checkListFile} 中的文件全部验证通过`) + +}) + +parser.addCmdLine("sm3 [--file] [--text] [--hash]", "生成/验证 SM3 值;", async function (cli) { + let filePath = cli.getParamValue("file") + let text = cli.getParamValue("text") + if( !filePath && !text ) { + $logger.error(`file 和 text 入参不能同时有`) + process.exit(-1) + } + const sm3 = require('sm-crypto').sm3 + if ( text ) { + let hashData = sm3( text ) // 杂凑 + $logger.info( hashData ) + } + if ( filePath ) { + if ( !fs.existsSync( filePath ) ) { + $logger.error(`file ${filePath} 不存在`) + process.exit(-2) + } + const data = fs.readFileSync(filePath); + const arrayBuffer = Buffer.from(data); + const uint8Array = new Uint8Array(arrayBuffer); + let hashData = sm3( uint8Array ) // 杂凑 + $logger.info( hashData ) + } +}); + +parser.addCmdLine("sm4en3 ", "sm4 加密,密钥通过 sm3 制备;", function (cli) { + const {sm4en3} = require("./crypto"); + let key = cli.getParamValue("key") + let data = cli.getParamValue("data") + let out = sm4en3( key, data ) + $logger.info( out ); +}); + +parser.addCmdLine("sm4de3 ", "sm4 解密,密钥通过 sm3 制备;", function (cli) { + const {sm4de3} = require("./crypto"); + let key = cli.getParamValue("key") + let data = cli.getParamValue("data") + let out = sm4de3( key, data ) + $logger.info( out ); +}); + +// parser.addCmdLine("base64 <-en/-de> ", "base64 编解码", function (cli) { +parser.addCmdLine("base64 [-en] [-de] [-hex] ", "base64 编解码;", function (cli) { + let data = cli.getParamValue("data") + if ( cli.hasFlag("de") ) { + if ( cli.hasFlag("hex") ) { + $logger.info( "执行 base64 decode to hex: " ); + $logger.info( convutil.base64ToHex( data ) ); + } else { + $logger.info( "执行 base64 decode to string: " ); + $logger.info( convutil.base64ToString( data ) ); + } + } else { + if ( cli.hasFlag("hex") ) { + $logger.info( "执行 hex encode to base64: " ); + $logger.info( convutil.hexToBase64( data ) ); + } else { + $logger.info( "执行 string encode to base64: " ); + $logger.info( convutil.stringToBase64( data ) ); + } + } +}); + +parser.addCmdLine("replace [-d] ", "文件内容正则替换;", function (cli) { + $logger.info( "暂未实现" ); +}); + +parser.addCmdLine("crlf [-d] [-r] [--path] [--filter-name] [--filter-path] ", "回车一致化", function (cli) { + $logger.info( "暂未实现" ); +}); + +parser.addCmdLine("tcpc [-d] [-x] [--host] [--ip] [--port] [--headlen] [--timeout] [--filepath]", "tcp 报文发收", function (cli) { + $logger.info( "暂未实现" ); +}); + +async function main(){ + + try { + let cmd = parser.parse(process.argv.slice(2)) + if ( ! cmd ) { + console.log( parser.help() ) + } else { + $logger.info(`执行命令 ${cmd.cmdline.cmd}`) + if ( cmd.canEval() ){ + await cmd.eval() + } else { + console.log( parser.help() ) + } + } + } catch ( e ) { + console.error( e ) + } +} + +main() diff --git a/kit/src/init.js b/kit/src/init.js new file mode 100644 index 0000000..f56b8d8 --- /dev/null +++ b/kit/src/init.js @@ -0,0 +1,101 @@ +const config = require('./config') +const Context = require("./context") +const log4js = require("log4js"); +// const layout = log4js.layouts.patternLayout('%m') +if ( ! global.$inited ) { + init() +} else { + // 不再初始化 +} + + +function init(){ + global.$inited = true + + const package = require("../package.json") + global.$version = package.version + + // 上下文模块 + global.$context = new Context(); + + // 载入日志 + const log4js = require('log4js'); + log4js.configure({ + appenders: { + console: { type: 'stdout' , level: "info", layout: { + type: "pattern", + pattern: "%m", + },}, + }, + categories: { + default: { appenders: ['console'], level: 'debug' }, + } + }); + global.$logger = log4js.getLogger(); + + + // 载入配置 + global.$sConfig = config.SystemConfig + // 重载版本号 + try { + global.$version = fs.readFileSync($sConfig.getLocalKitPath() + "/version", "UTF-8") + }catch ( e ) {} + + let logPath = global.$sConfig.getLogPath(); + global.$context.standalone("uConfig", function (){ + return new config.UserConfig( $sConfig.getUserConfigFilePath() ) + }) + log4js.addLayout('customLayout', function(config) { + return function(logEvent) { + const now = new Date(logEvent.startTime) + let time = now.toLocaleTimeString(); + time += "." + now.getMilliseconds().toString().padStart(3, '0'); + const level = "" + logEvent.level; + const categoryName = logEvent.categoryName; + const message = logEvent.data[0]; + if ( message && message.startsWith && message.startsWith("\r") ) { + return message; + } + + let logMessage = `${time} ${level} - ${message}`; + + // Add color to log message based on log level + switch (level) { + case 'INFO': + logMessage = `\x1b[32m${logMessage}\x1b[0m`; // Green color for INFO log level + break; + case 'WARN': + logMessage = `\x1b[33m${logMessage}\x1b[0m`; // Yellow color for WARN log level + break; + case 'ERROR': + logMessage = `\x1b[31m${logMessage}\x1b[0m`; // Red color for ERROR log level + break; + default: + break; + } + + return logMessage; + }; + }); + // 重置日志配置 + log4js.configure({ + appenders: { + console: { type: 'console' , level: "info", layout: { + type: 'customLayout' + }}, + file: { type: 'dateFile', filename: logPath + '/kit.log', "pattern": "yyyy-MM-dd", "compress": true , alwaysIncludePattern : true, keepFileExt : true, numBackups : 7} + }, + categories: { + default: { appenders: ['console', 'file'], level: 'debug' }, + } + }); + + // 注册:远程仓库 + global.$context.standalone("remote", async function (){ + const Remote = require("./remote") + const remote = new Remote(); + await remote.init() + return remote + }) + +} \ No newline at end of file diff --git a/kit/src/linux/local.js b/kit/src/linux/local.js new file mode 100644 index 0000000..00410c8 --- /dev/null +++ b/kit/src/linux/local.js @@ -0,0 +1,5 @@ +function getModuleInfo(){ + +} + +module.exports = { getModuleInfo } \ No newline at end of file diff --git a/kit/src/linux/net.js b/kit/src/linux/net.js new file mode 100644 index 0000000..2810c1b --- /dev/null +++ b/kit/src/linux/net.js @@ -0,0 +1,33 @@ + + +async function killport(port) { + $logger.info(`kill process on port ${port}`); + const util = require('util'); + const exec = util.promisify(require('child_process').exec); + + try { + // 查 PID + let { stdout : stdout1 } = exec(`lsof -t -sTCP:LISTEN -i:${port}`) + let pid = stdout1.trim(); + $logger.debug(`绑定端口号 ${port} 的进程 PID=${pid}`) + + // 查进程信息 + let { stdout : stdout2 } = exec(`ps -p ${pid} -o comm=`) + let processName = stdout2.trim(); + $logger.info(`绑定端口号=${port}; 进程 PID=${pid}; 程序=${processName};`); + + // Kill 进程 + exec(`kill -9 ${pid}`) + + } catch (e) { + if ( 1 === e.code ) { + $logger.error("此端口未被使用,或清理进程失败。") + return + } + } +} + + +module.exports = { + killport +} \ No newline at end of file diff --git a/kit/src/net.js b/kit/src/net.js new file mode 100644 index 0000000..7db8bee --- /dev/null +++ b/kit/src/net.js @@ -0,0 +1,84 @@ +require("./init") +const osutil = require("./util/osutil") +const killer = require('cross-port-killer'); +const net = require('node:net'); + + +/** + * 模拟 telnet ,检测端口是否开放 + * @param port + * @param host + * @param timeout + * @returns {Promise} + */ +async function telnet(port, host, timeout=3000){ + $logger.debug(`port=${port} host=${host} timeout=${timeout}`) + return new Promise(function (resolve, reject){ + const socket = new net.Socket(); + + socket.setTimeout(timeout, function() { + socket.end(); + reject("链接超时") + }); + + socket.connect(port, host); + + socket.on('connect', function() { + resolve('连接成功'); + socket.end(); + }); + + socket.on('error', function(err) { + reject('连接失败'); + }); + }) + +} + +async function killport(port) { + return killer.kill( port ); + // let func = osutil.choise([ + // require("./windows/net").killport, + // require("./linux/net").killport, + // ]) + // return func && func(port); +} + +/** + * 获取端口信息 + * 因为命令输出缓存问题,有缺陷。 + * @param port + * @returns {Promise} + */ +async function getport(port) { + $logger.info( "暂未实现" ); +} + + +async function checkip(){ + const url = "https://myip.ipip.net/json" + const http = url.startsWith("https:") ? require("node:https") : require('node:http'); + return new Promise((resolve, reject) => { + http.get(url, (res) => { + let data = ''; + + // A chunk of data has been recieved. + res.on('data', (chunk) => { + data += chunk; + }); + + // The whole response has been received. Resolve the promise with result + res.on('end', () => { + resolve(JSON.parse(data)); + }); + + }).on("error", (err) => { + // In case of any error reject the promise + reject(err); + }); + }); +} + +module.exports = { + killport, getport, telnet, checkip +} \ No newline at end of file diff --git a/kit/src/remote/index.js b/kit/src/remote/index.js new file mode 100644 index 0000000..88ae0a4 --- /dev/null +++ b/kit/src/remote/index.js @@ -0,0 +1,228 @@ +/** + * remote 模块用来处理远程仓库相关事务 + */ +const path = require("path") +const fs = require("fs") +const JSON5 = require('json5') +const { pipeline, Transform} = require("node:stream/promises"); + +module.exports = class Remote { + + constructor() { + this.init() + this.func_publish = require("./publish") + this.func_install = require("./install") + } + + + + async init() { + this.webdav = await import("webdav"); + const uConfig = await $context.get("uConfig"); + if ( uConfig.get("remoteServer") ){ + let server = uConfig.get("remoteServer"); + if ( server.startsWith("webdav") ) { + server = "http" + server.substring(6) + } + + if ( uConfig.get("remoteAuth") ) { + let up = uConfig.get("remoteAuth"); + up = up.split(":", 2) + this.client = this.webdav.createClient( + server, + { + username: up[0], + password: up[1] + } + ); + } else { + this.client = this.webdav.createClient( + server + ); + } + } + } + + async check() { + + if ( ! this.webdav ) { + return false + } + + if ( ! this.client ) { + return false + } + + try { + + // Get directory contents + const directoryItems = await this.client.getDirectoryContents("/"); + // 是否包含 index.json ? + // 不包含尝试创建 + // 包含了尝试下载 + const indexJsonFile = this.getIndexJson( directoryItems ); + if ( indexJsonFile ) { + // 包含了尝试下载 + // 下载有缓存 + this.download(indexJsonFile) + + } else { + // 不包含尝试创建 + await this.affireRemoteStore() + } + + + return true + }catch ( e ) { + $logger.error("remote check 过程中出错", e) + return false + } + } + + /** + * 先对比时间和长度,有不同才会真正下载,否则使用本地缓存。 + * 本地缓存路径即本地仓库路径 + * @param rFile + */ + download(rFile){ + const lFilePath = path.resolve($sConfig.getLocalStorePath(), rFile.filename); + if ( fs.existsSync( lFilePath ) ) { + const lFileInfo = fs.statSync(lFilePath); + if ( lFileInfo.size === rFile.length + && lFileInfo.mtime === rFile.lastmod + ) { + // 使用缓存 + $logger.debug("use cache") + return true + } + } + + // 下载并覆盖 + // 不知道是同步还是异步 + this.wDownload( rFile.filename, lFilePath ); + + + } + + async affireRemoteStore(){ + // Get directory contents + const directoryItems = await this.client.getDirectoryContents("/"); + // 是否包含 index.json ? + // 不包含尝试创建 + // 包含了尝试下载 + const indexJsonFile = this.getIndexJson( directoryItems ); + if ( ! indexJsonFile ) { + // 创建 index + await this.client.putFileContents("/index.json", JSON5.stringify({ + modules : [], + tmpls : [], + lists : [] + }, null, 4) , { overwrite: false }); + } + + const modulesFolder = this.getFileInfoByName(directoryItems, "modules") + if ( ! modulesFolder ) { + // 创建文件夹 + await this.client.createDirectory("/modules"); + } + + const tmplsFolder = this.getFileInfoByName(directoryItems, "tmpls") + if ( ! tmplsFolder ) { + // 创建文件夹 + await this.client.createDirectory("/tmpls"); + } + + const listsFolder = this.getFileInfoByName(directoryItems, "lists") + if ( ! listsFolder ) { + // 创建文件夹 + await this.client.createDirectory("/lists"); + } + + } + + getIndexJson(items){ + return this.getFileInfoByName( items, "index.json" ); + } + getFileInfoByName(items, name){ + if ( ! items || items.length < 1 ) { + return false; + } + for ( let item of items ) { + if ( item.basename === name ) { + return item; + } + } + return false; + } + + async publish(){ + return this.func_publish( this ) + } + + async install(id){ + return this.func_install( this, id ) + } + + async wUpload(from, to){ + const totalSize = fs.statSync(from).size; + const rs = fs.createReadStream( from ); + let wSize = 0; + let rSize = 0; + rs.on("data", (r)=>{ + rSize += r.length; + // console.log("r", rSize * 100 / totalSize) + }) + const ws = this.client.createWriteStream(to) + ws.on("data", (r)=>{ + wSize += r.length; + // console.log("w", wSize * 100 / totalSize) + }) + return pipeline( rs, ws); + } + + async wDownload(from, to){ + $logger.info("download", from, to ) + const stat = await this.client.stat(from); + const totalSize = stat.size; + let wSize = 0; + let rSize = 0; + + const rs = this.client.createReadStream(from); + rs.on("data", (r)=>{ + rSize += r.length; + // console.log("r", rSize * 100 / totalSize) + }) + + const ws = fs.createWriteStream(to); + ws.on("data", (r)=>{ + wSize += r.length; + // console.log("w", wSize * 100 / totalSize) + }) + return pipeline( + rs, ws + ) + } + + async wMkdir(folder){ + + const folderfather = path.dirname( folder ); + if ( ! await this.client.exists( folderfather ) ) { + // await remote.client.createDirectory( folderfather ) + await this.wMkdir( folderfather ) + } + + if ( await this.client.exists( folder ) ) { + return + } else { + await this.client.createDirectory( folder ) + } + } + + async wGetModuleInfo(id){ + let meta = await this.client.getFileContents("/modules/" + id + "/meta.json", { format: "text" }); + let metaJson = JSON5.parse( meta ); + return metaJson + } + +} + diff --git a/kit/src/remote/install.js b/kit/src/remote/install.js new file mode 100644 index 0000000..6f69cf1 --- /dev/null +++ b/kit/src/remote/install.js @@ -0,0 +1,53 @@ +const os = require('os'); +const fs = require("fs"); +const osutil = require("../util/osutil") + +function matchDistName(cur, dists){ + const list = [] + for (let dist of dists) { + if ( cur.curOS === dist.os && cur.curPlatform === dist.platform) { + list.push( dist ) + } + else if ( cur.curOS === dist.os && "cross" === dist.platform) { + list.unshift( dist ) + } + else if ( "cross" === dist.os && cur.curPlatform === dist.platform) { + list.unshift( dist ) + } + } + return list +} + + +module.exports = async function install(remote, id){ + const cur = osutil.cur(); + + if ( ! id ) { + id = "kit" + } + + // 获取本地版本 + const localInfo = osutil.choise([ + require("../windows/local").getModuleInfo, + require("../linux/local").getModuleInfo, + ])( id ) + + $logger.info("local version", localInfo.version) + + // 确认远程版本文件是否存在 + const remoteInfo = await remote.wGetModuleInfo(id) + $logger.info("remote latest version", remoteInfo.latest.version) + + const dists = matchDistName(cur, remoteInfo.latest.dist) + const dist = dists.pop() + + await remote.wDownload( + "/modules/" + id + "/" + remoteInfo.latest.version + "/" + cur.curOS + "/" + cur.curPlatform + "/" + dist.filename, + localInfo.homeDir + "/" + dist.filename, + ) + + fs.writeFileSync( localInfo.homeDir + "/version", remoteInfo.latest.version ); + + $logger.info("当前版本", remoteInfo.latest.version ) + +} \ No newline at end of file diff --git a/kit/src/remote/publish.js b/kit/src/remote/publish.js new file mode 100644 index 0000000..5c0c75d --- /dev/null +++ b/kit/src/remote/publish.js @@ -0,0 +1,79 @@ +const path = require("path") +const fs = require("fs") +const JSON5 = require('json5') +const { pipeline } = require('node:stream/promises'); +const objutil = require("../util/objutil") +module.exports = async function publish(remote, meta){ + + let curPath = process.cwd(); + const metaFilePath = path.resolve( curPath, "meta.json" ); + if ( ! fs.existsSync( metaFilePath ) ) { + $logger.error("当前路径不包含 meta.json") + return + } + + const metaFile = fs.readFileSync(metaFilePath); + + const localMeta = JSON5.parse( metaFile ); + + // 更新版本 + const date = new Date(); + localMeta.version = `${localMeta.version}.${date.getFullYear()}${(date.getMonth() + 1).toString().padStart(2, '0')}${date.getDate().toString().padStart(2, '0')}${date.getHours().toString().padStart(2, '0')}${date.getMinutes().toString().padStart(2, '0')}${date.getSeconds().toString().padStart(2, '0')}`; + + + // 确认目录 + // const directoryItems = await remote.getDirectoryContents("/modules"); + // const modulesFolder = remote.getFileInfoByName(directoryItems, localMeta.id) + if ( ! await remote.client.exists("/modules/" + localMeta.id) ) { + // 创建文件夹 + await remote.client.createDirectory("/modules/" + localMeta.id); + } + + // 先更新文件 + for (let dist of localMeta.dist ) { + + const folder = "/modules/" + localMeta.id + "/" + localMeta.version + "/" + dist.os + "/" + dist.platform + await remote.wMkdir( folder ); + + $logger.info("upload dist", dist.path, fs.existsSync(dist.path)) + + await remote.wUpload( dist.path , folder + "/" + dist.filename ); + + } + + // remote meta + let remoteMeta = { + "versions" : [] + } + try { + const remoteMetaStr = await remote.client.getFileContents("/modules/" + localMeta.id + "/meta.json", { format: "text" }); + remoteMeta = JSON5.parse( remoteMetaStr ) + } catch (e) { + + } + + remoteMeta.id = localMeta.id; + remoteMeta.name = localMeta.name; + remoteMeta.desc = localMeta.desc; + remoteMeta.type = localMeta.type; + remoteMeta.vcs = localMeta.vcs; + // 废弃 + // remoteMeta.platform = "cross" === localMeta.platform ? ["X86_64", "ARM64"] : localMeta.platform; + // remoteMeta.os = "cross" === localMeta.os ? ["Windows", "Linux", "Alpine"] : localMeta.os; + const dist = objutil.copy(localMeta.dist) + for (let d of dist) { + delete dist.path + } + remoteMeta.latest = { + version : localMeta.version, + dist + } + remoteMeta.versions.push(remoteMeta.latest) + if ( remoteMeta.versions.length >= 20 ) { + remoteMeta.versions = remoteMeta.versions.slice(0, 20); + } + + await remote.client.putFileContents("/modules/" + localMeta.id + "/meta.json", JSON5.stringify(remoteMeta, null, 4) , { overwrite: true }); + + $logger.info("发布成功") +} \ No newline at end of file diff --git a/kit/src/shell/index.js b/kit/src/shell/index.js new file mode 100644 index 0000000..4e7bdfd --- /dev/null +++ b/kit/src/shell/index.js @@ -0,0 +1,27 @@ +/** + * + * child_process + * - exec 适合短时间命令,获取完整输出 + * - execFile 适合实时交互 + * - spawn 适合长时间命令 + * + * shelljs - https://www.npmjs.com/package/shelljs + * execa - https://www.npmjs.com/package/execa + * ssh2 - https://www.npmjs.com/package/ssh2 + * + */ + + + +const { execSync } = require('child_process'); + + + + +module.exports = { + execSync(cmd){ + $logger.debug(`execSync ${cmd}`) + const output = execSync(cmd); + return output.toString() + } +} \ No newline at end of file diff --git a/kit/src/util/convutil.js b/kit/src/util/convutil.js new file mode 100644 index 0000000..891b9d6 --- /dev/null +++ b/kit/src/util/convutil.js @@ -0,0 +1,551 @@ +var codePoint2utf8 = function (code) { + var bin = parseInt(code).toString(2) + var ln = 0; + var mx = 0; + if (bin.length <= 7) { + ln = 1; + mx = 7; + } else if (bin.length > 7 && bin.length <= 11) { + ln = 2; + mx = 11; + } else if (bin.length > 11 && bin.length <= 16) { + ln = 3; + mx = 16; + } else if (bin.length > 16 && bin.length <= 21) { + ln = 4; + mx = 21; + } else if (bin.length > 21 && bin.length <= 26) { + ln = 5; + mx = 26; + } else if (bin.length > 26 && bin.length <= 31) { + ln = 6; + mx = 31; + }; + var longbin = ("0".repeat(mx) + bin).slice(-mx) + var result = ''; + for (var i = 0; i < ln; i++) { + if (ln != 1) { + if (i == 0) { + result += parseInt("1".repeat(ln) + "0" + longbin.slice(0,7-ln),2).toString(16); + longbin = longbin.slice(7-ln); + } else { + result += parseInt("10" + longbin.slice(0,6),2).toString(16); + longbin = longbin.slice(6); + }; + } else { + result += ("00" + parseInt(longbin,2).toString(16)).slice(-2); + }; + }; + return result; +}; + +var stringToHex = function (inputstring) { + var result = ''; + for (var i = 0; i < inputstring.length; i++) { + result += codePoint2utf8(inputstring.codePointAt(i)) + }; + return result; +}; + +var hexToString = function (inputstring) { + try { + var result = decodeURIComponent(inputstring.toLowerCase().replace(/\s+/g, '').replace(/[0-9a-f]{2}/g, '%$&')); + } catch(e) { + throw "Not valid UTF-8 hex code!"; + }; + return result; +}; + + +function stringToByte(str) { + var bytes = new Array(); + var len, c; + len = str.length; + for(var i = 0; i < len; i++) { + c = str.charCodeAt(i); + if(c >= 0x010000 && c <= 0x10FFFF) { + bytes.push(((c >> 18) & 0x07) | 0xF0); + bytes.push(((c >> 12) & 0x3F) | 0x80); + bytes.push(((c >> 6) & 0x3F) | 0x80); + bytes.push((c & 0x3F) | 0x80); + } else if(c >= 0x000800 && c <= 0x00FFFF) { + bytes.push(((c >> 12) & 0x0F) | 0xE0); + bytes.push(((c >> 6) & 0x3F) | 0x80); + bytes.push((c & 0x3F) | 0x80); + } else if(c >= 0x000080 && c <= 0x0007FF) { + bytes.push(((c >> 6) & 0x1F) | 0xC0); + bytes.push((c & 0x3F) | 0x80); + } else { + bytes.push(c & 0xFF); + } + } + return bytes; + + +} +// utf8 +function byteToString(arr) { + if(typeof arr === 'string') { + return arr; + } + var str = '', + _arr = arr; + for(var i = 0; i < _arr.length; i++) { + var one = _arr[i].toString(2), + v = one.match(/^1+?(?=0)/); + if(v && one.length == 8) { + var bytesLength = v[0].length; + var store = _arr[i].toString(2).slice(7 - bytesLength); + for(var st = 1; st < bytesLength; st++) { + store += _arr[st + i].toString(2).slice(2); + } + str += String.fromCharCode(parseInt(store, 2)); + i += bytesLength - 1; + } else { + str += String.fromCharCode(_arr[i]); + } + } + return str; +} +/** + * Convert the given string to byte array + * + * @param {string} str the string to be converted to byte array + * @returns {number[]} + */ +var str2bytes = function (str) { + var i = 0; + var ch = null; + var hex = null; + var encoded = encodeURIComponent(str); + var length = encoded.length; + var bytes = []; + + while (i < length) { + ch = encoded.charAt(i++); + if (ch === '%') { + hex = encoded.charAt(i++) + hex += encoded.charAt(i++); + bytes.push(parseInt(hex, 16)); + } else { + bytes.push(ch.charCodeAt(0)); + } + } + + return bytes; +}; + +/** + * Convert the given byte array to string + * + * @param {number[]} bytes The byte array to be converted to string + * @returns {string} + */ +var bytes2str = function (bytes) { + var i = 0; + var hex = null; + var byte = 0; + var hexArray = []; + var length = bytes.length; + + while (i < length) { + byte = bytes[i++]; + hex = byte.toString(16); + hex = hex.length < 2 ? ('%0' + hex) : ('%' + hex); + hexArray.push(hex); + } + + return decodeURIComponent(hexArray.join('')); +}; +function base64ArrayBuffer(arrayBuffer) { + var base64 = '' + var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' + + var bytes = new Uint8Array(arrayBuffer) + var byteLength = bytes.byteLength + var byteRemainder = byteLength % 3 + var mainLength = byteLength - byteRemainder + + var a, b, c, d + var chunk + + // Main loop deals with bytes in chunks of 3 + for (var i = 0; i < mainLength; i = i + 3) { + // Combine the three bytes into a single integer + chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2] + + // Use bitmasks to extract 6-bit segments from the triplet + a = (chunk & 16515072) >> 18 // 16515072 = (2^6 - 1) << 18 + b = (chunk & 258048) >> 12 // 258048 = (2^6 - 1) << 12 + c = (chunk & 4032) >> 6 // 4032 = (2^6 - 1) << 6 + d = chunk & 63 // 63 = 2^6 - 1 + + // Convert the raw binary segments to the appropriate ASCII encoding + base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d] + } + + // Deal with the remaining bytes and padding + if (byteRemainder == 1) { + chunk = bytes[mainLength] + + a = (chunk & 252) >> 2 // 252 = (2^6 - 1) << 2 + + // Set the 4 least significant bits to zero + b = (chunk & 3) << 4 // 3 = 2^2 - 1 + + base64 += encodings[a] + encodings[b] + '==' + } else if (byteRemainder == 2) { + chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1] + + a = (chunk & 64512) >> 10 // 64512 = (2^6 - 1) << 10 + b = (chunk & 1008) >> 4 // 1008 = (2^6 - 1) << 4 + + // Set the 2 least significant bits to zero + c = (chunk & 15) << 2 // 15 = 2^4 - 1 + + base64 += encodings[a] + encodings[b] + encodings[c] + '=' + } + + return base64 +} + +(function() { + var base64EncodeChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', + base64DecodeChars = new Array(( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), 62, ( - 1), ( - 1), ( - 1), 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), ( - 1), 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, ( - 1), ( - 1), ( - 1), ( - 1), ( - 1)); + this.base64encode = function(e) { + var r, a, c, h, o, t; + for (c = e.length, a = 0, r = ''; a < c;) { + if (h = 255 & e.charCodeAt(a++), a == c) { + r += base64EncodeChars.charAt(h >> 2), + r += base64EncodeChars.charAt((3 & h) << 4), + r += '=='; + break + } + if (o = e.charCodeAt(a++), a == c) { + r += base64EncodeChars.charAt(h >> 2), + r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4), + r += base64EncodeChars.charAt((15 & o) << 2), + r += '='; + break + } + t = e.charCodeAt(a++), + r += base64EncodeChars.charAt(h >> 2), + r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4), + r += base64EncodeChars.charAt((15 & o) << 2 | (192 & t) >> 6), + r += base64EncodeChars.charAt(63 & t) + } + return r + } + this.base64decode = function(e) { + var r, a, c, h, o, t, d; + for (t = e.length, o = 0, d = ''; o < t;) { + do r = base64DecodeChars[255 & e.charCodeAt(o++)]; + while (o < t && r == -1); + if (r == -1) break; + do a = base64DecodeChars[255 & e.charCodeAt(o++)]; + while (o < t && a == -1); + if (a == -1) break; + d += String.fromCharCode(r << 2 | (48 & a) >> 4); + do { + if (c = 255 & e.charCodeAt(o++), 61 == c) return d; + c = base64DecodeChars[c] + } while ( o < t && c == - 1 ); + if (c == -1) break; + d += String.fromCharCode((15 & a) << 4 | (60 & c) >> 2); + do { + if (h = 255 & e.charCodeAt(o++), 61 == h) return d; + h = base64DecodeChars[h] + } while ( o < t && h == - 1 ); + if (h == -1) break; + d += String.fromCharCode((3 & c) << 6 | h) + } + return d + } + this.HexToBase64 = function(str) { + return base64encode(String.fromCharCode.apply(null, str.replace(/\r|\n/g, "").replace(/([\da-fA-F]{2}) ?/g, "0x$1 ").replace(/ +$/, "").split(" "))); + } + this.Base64Tohex = function(str) { + for (var i = 0, + bin = base64decode(str.replace(/[ \r\n]+$/, "")), hex = []; i < bin.length; ++i) { + var tmp = bin.charCodeAt(i).toString(16); + if (tmp.length === 1) tmp = "0" + tmp; + hex[hex.length] = tmp; + } + return hex.join(""); + }, + this.toBytes = function(str) { + for (var bytes = [], c = 0; c < str.length; c += 2) + bytes.push(parseInt(str.substr(c, 2), 16)); + return bytes; + } + +}) (); +//hexToBase64 Base64Tohex base64decode base64encode + +var bytesToString = function(bytes){ + return hexToString(bytesToHex(bytes)); +} + + +var bytesToBase64 = function(bytes){ + return base64ArrayBuffer(bytes); +} + +// Convert a byte array to a hex string +var bytesToHex = function(bytes) { + for (var hex = [], i = 0; i < bytes.length; i++) { + hex.push((bytes[i] >>> 4).toString(16)); + hex.push((bytes[i] & 0xF).toString(16)); + } + return hex.join(""); +} + + +var stringToBase64 = function(str){ + return base64encode(str); +} + +var stringToBytes = function(str){ + return hexToBytes(stringToHex(str)); +} + +// Convert a ASCII string to a hex string +var stringToHex = function(str) { + return str.split("").map(function(c) { + return ("0" + c.charCodeAt(0).toString(16)).slice(-2); + }).join(""); +} + +var hexToBytes= function(hex) { + for (var bytes = [], c = 0; c < hex.length; c += 2) + bytes.push(parseInt(hex.substr(c, 2), 16)); + return bytes; +} + +// Convert a hex string to a ASCII string +var hexToString = function(hexStr) { + var hex = hexStr.toString();//force conversion + var str = ''; + for (var i = 0; i < hex.length; i += 2) + str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); + return str; +} + +var hexToBase64 = function(hexStr){ + return HexToBase64(hexStr); +} + +var base64ToString = function(base64str){ + return base64decode(base64str); +} + + +const Base64 = { + _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", + encode: function(e) { + var t = ""; + var n, r, i, s, o, u, a; + var f = 0; + e = Base64._utf8_encode(e); + while (f < e.length) { + n = e.charCodeAt(f++); + r = e.charCodeAt(f++); + i = e.charCodeAt(f++); + s = n >> 2; + o = (n & 3) << 4 | r >> 4; + u = (r & 15) << 2 | i >> 6; + a = i & 63; + if (isNaN(r)) { + u = a = 64 + } else if (isNaN(i)) { + a = 64 + } + t = t + this._keyStr.charAt(s) + this._keyStr.charAt(o) + this._keyStr.charAt(u) + this._keyStr.charAt(a) + } + return t + }, + decode: function(e) { + var t = ""; + var n, r, i; + var s, o, u, a; + var f = 0; + e = e.replace(/[^A-Za-z0-9+/=]/g, ""); + while (f < e.length) { + s = this._keyStr.indexOf(e.charAt(f++)); + o = this._keyStr.indexOf(e.charAt(f++)); + u = this._keyStr.indexOf(e.charAt(f++)); + a = this._keyStr.indexOf(e.charAt(f++)); + n = s << 2 | o >> 4; + r = (o & 15) << 4 | u >> 2; + i = (u & 3) << 6 | a; + t = t + String.fromCharCode(n); + if (u != 64) { + t = t + String.fromCharCode(r) + } + if (a != 64) { + t = t + String.fromCharCode(i) + } + } + t = Base64._utf8_decode(t); + return t + }, + _utf8_encode: function(e) { + e = e.replace(/rn/g, "n"); + var t = ""; + for (var n = 0; n < e.length; n++) { + var r = e.charCodeAt(n); + if (r < 128) { + t += String.fromCharCode(r) + } else if (r > 127 && r < 2048) { + t += String.fromCharCode(r >> 6 | 192); + t += String.fromCharCode(r & 63 | 128) + } else { + t += String.fromCharCode(r >> 12 | 224); + t += String.fromCharCode(r >> 6 & 63 | 128); + t += String.fromCharCode(r & 63 | 128) + } + } + return t + }, + _utf8_decode: function(e) { + var t = ""; + var n = 0; + var r = 0; + var c1 =0; + var c2 =0; + var c3 =0; + while (n < e.length) { + r = e.charCodeAt(n); + if (r < 128) { + t += String.fromCharCode(r); + n++ + } else if (r > 191 && r < 224) { + c2 = e.charCodeAt(n + 1); + t += String.fromCharCode((r & 31) << 6 | c2 & 63); + n += 2 + } else { + c2 = e.charCodeAt(n + 1); + c3 = e.charCodeAt(n + 2); + t += String.fromCharCode((r & 15) << 12 | (c2 & 63) << 6 | c3 & 63); + n += 3 + } + } + return t + } +} + +function encodeUtf8(text) { + const code = encodeURIComponent(text); + const bytes = []; + for (var i = 0; i < code.length; i++) { + const c = code.charAt(i); + if (c === '%') { + const hex = code.charAt(i + 1) + code.charAt(i + 2); + const hexVal = parseInt(hex, 16); + bytes.push(hexVal); + i += 2; + } else bytes.push(c.charCodeAt(0)); + } + return bytes; +} + +function decodeUtf8(bytes) { + var encoded = ""; + for (var i = 0; i < bytes.length; i++) { + encoded += '%' + bytes[i].toString(16); + } + return decodeURIComponent(encoded); +} +function strToBytesUTF8(str) { + let utf8 = []; + for (let i = 0; i < str.length; i++) { + let charcode = str.charCodeAt(i); + if (charcode < 0x80) utf8.push(charcode); + else if (charcode < 0x800) { + utf8.push(0xc0 | (charcode >> 6), + 0x80 | (charcode & 0x3f)); + } + else if (charcode < 0xd800 || charcode >= 0xe000) { + utf8.push(0xe0 | (charcode >> 12), + 0x80 | ((charcode>>6) & 0x3f), + 0x80 | (charcode & 0x3f)); + } + // surrogate pair + else { + i++; + // UTF-16 encodes 0x10000-0x10FFFF by + // subtracting 0x10000 and splitting the + // 20 bits of 0x0-0xFFFFF into two halves + charcode = 0x10000 + (((charcode & 0x3ff)<<10) + | (str.charCodeAt(i) & 0x3ff)); + utf8.push(0xf0 | (charcode >>18), + 0x80 | ((charcode>>12) & 0x3f), + 0x80 | ((charcode>>6) & 0x3f), + 0x80 | (charcode & 0x3f)); + } + } + return utf8; +} + +function utf8ToBase64(str){ + var ret11 = strToBytesUTF8( str ) + return bytesToBase64( ret11 ) +} + +function base64ToUtf8( str ){ + var hex = base64ToHex( str ); + var ret = hexToBytes( hex ) + // var ret = base64ToBytes( str ) + return decodeUtf8(ret); +} + +var base64ToHex = function(base64str){ + return Base64Tohex(base64str); +} + +var base64ToBytes = function(base64str){ + return toBytes(base64str); + //null +} +var utf8ToHex = function (str) { + var ret11 = strToBytesUTF8( str ) + return bytesToHex( ret11 ) +} + +var hexToUtf8 = function (hex) { + var ret = hexToBytes( hex ) + // var ret = base64ToBytes( str ) + return decodeUtf8(ret); +} + +if ( module ) { + + module.exports = { + + stringToByte, + byteToString, + str2bytes, + bytes2str, + base64ArrayBuffer, + bytesToString, + bytesToBase64, + bytesToHex, + stringToBase64, + stringToBytes, + stringToHex, + hexToBytes, + hexToString, + hexToBase64, + base64ToString, + base64ToHex, + base64ToBytes, + Base64, + encodeUtf8, + decodeUtf8, + strToBytesUTF8, + utf8ToBase64, + base64ToUtf8, + utf8ToHex, + hexToUtf8 + } +} diff --git a/kit/src/util/objutil.js b/kit/src/util/objutil.js new file mode 100644 index 0000000..ab4b74a --- /dev/null +++ b/kit/src/util/objutil.js @@ -0,0 +1,6 @@ +const JSON5 = require('json5') +module.exports = { + copy(obj) { + return JSON5.parse( JSON5.stringify( obj ) ) + } +} \ No newline at end of file diff --git a/kit/src/util/osutil.js b/kit/src/util/osutil.js new file mode 100644 index 0000000..c6fe006 --- /dev/null +++ b/kit/src/util/osutil.js @@ -0,0 +1,51 @@ +const os = require('os'); +const fs = require("fs"); +const Windows = 0; +const Linux = 1; + +module.exports = { + + Windows, Linux, + + /** + * 根据 os 不同,载入不同的方法 + */ + choise : function ( cbs ) { + if (os.type() == 'Windows_NT') { + // windows平台 + return cbs && cbs[ Windows ] + } + else if (os.type() == 'Linux') { + // Linux平台 + return cbs && cbs[ Linux ] + } + else { + throw { message : "不支持的 OS" } + } + }, + + cur : function () { + let isLinux = false; + // 操作系统不同,安装方式不同 + let curOS = os.type() + if ( curOS === "Windows_NT" ) { + curOS = "Windows" + } + else if ( curOS === "Linux" ) { + isLinux = true; + const fs = require('fs'); + if( fs.existsSync('/etc/alpine-release') ){ + curOS = "Alpine" + } + } + + let curPlatform = os.arch() + if ( curPlatform === "x64" ) { + curPlatform = "X86_64" + } else if ( curPlatform.startsWith("arm") ) { + curPlatform = "ARM64" + } + + return { isLinux, curOS, curPlatform } + } +} \ No newline at end of file diff --git a/kit/src/windows/local.js b/kit/src/windows/local.js new file mode 100644 index 0000000..5f522fa --- /dev/null +++ b/kit/src/windows/local.js @@ -0,0 +1,38 @@ + +const fs = require("fs") +const os = require("os") +const path = require("path"); + +// 废弃,权限问题难以处理 +// const KIT_HOME = "C:\\Program Files\\kit" +// const HOME = "C:\\Program Files\\kit\\.kit" +const KIT_HOME = path.resolve(os.homedir(), "AppData", "Local", "kit") +const HOME = path.resolve(os.homedir(), "AppData", "Local", "kit", ".kit") + +function getModuleInfo(id){ + let homeDir = path.resolve(HOME, "modules", id) + if ( "kit" === id ) { + homeDir = KIT_HOME; + } + + if ( ! fs.existsSync( homeDir ) ) { + fs.mkdirSync( homeDir, { recursive : true } ) + return { + HOME, KIT_HOME, exist : false, homeDir + } + } + + let versionFile = path.resolve(homeDir, "version") + if ( ! fs.existsSync( versionFile ) ) { + return { + HOME, KIT_HOME, exist : true, homeDir + } + } + + return { + HOME, KIT_HOME, exist : true, version : fs.readFileSync(versionFile, "UTF-8"), homeDir + } + +} + +module.exports = { getModuleInfo } \ No newline at end of file diff --git a/kit/src/windows/net.js b/kit/src/windows/net.js new file mode 100644 index 0000000..0f51017 --- /dev/null +++ b/kit/src/windows/net.js @@ -0,0 +1,40 @@ + + +async function killport(port) { + $logger.info(`kill process on port ${port}`); + const util = require('util'); + const exec = util.promisify(require('child_process').exec); + + try { + // 查 PID + let { stdout : stdout1 } = await exec(`netstat -ano | findstr "LISTENING" | findstr /C:":${port} "`) + + const output = stdout1.split('\n'); + const outputLines = output[0].trim().split(' ') + const pid = outputLines[outputLines.length - 1]; + $logger.debug(`绑定端口号 ${port} 的进程 PID=${pid}`) + + + // 查进程信息 + let { stdout : stdout2 } = await exec(`tasklist /FI "PID eq ${pid}" /V /FO CSV`); + const tasklistOutput = stdout2.trim(); + const processInfo = tasklistOutput.split('\n')[1].split('","'); + const processName = processInfo[0].slice(1) ; + + $logger.info(`绑定端口号=${port}; 进程 PID=${pid}; 程序=${processName};`); + + // Kill 进程 + await exec(`taskkill /PID ${pid} /F`) + + } catch (e) { + if ( 1 === e.code ) { + $logger.error("此端口未被使用,或清理进程失败。") + return + } + } +} + + +module.exports = { + killport +} \ No newline at end of file diff --git a/kit/test/net_test.js b/kit/test/net_test.js new file mode 100644 index 0000000..40705a3 --- /dev/null +++ b/kit/test/net_test.js @@ -0,0 +1,6 @@ +const net = require(__dirname + "/../src/net") + + + +net.killport(6667); + diff --git a/kit/test/webdav_test.js b/kit/test/webdav_test.js new file mode 100644 index 0000000..26670a5 --- /dev/null +++ b/kit/test/webdav_test.js @@ -0,0 +1,75 @@ +const fs = require("fs"); +const { pipeline } = require('node:stream/promises'); + + +async function wUpload(client , from, to){ + const totalSize = fs.statSync(from).size; + const rs = fs.createReadStream( from ); + let wSize = 0; + let rSize = 0; + rs.on("data", (r)=>{ + rSize += r.length; + console.log("r", rSize * 100 / totalSize) + }) + const ws = client.createWriteStream(to) + ws.on("data", (r)=>{ + wSize += r.length; + console.log("w", wSize * 100 / totalSize) + }) + ws.on("finish", ()=>{ + console.log("finish") + }) + return pipeline( rs, ws ); +} + +async function main(){ + const webdav = await import("webdav"); + const client = webdav.createClient( + "http://baishe.fullstack.club:5244/dav", + { + username: "cheney", + password: "722V587" + } + ); + // const client = webdav.createClient( + // "http://172.16.17.230:5244/dav", + // { + // username: "kit", + // password: "kitkit" + // } + // ); + + // Get directory contents + const directoryItems = await client.getDirectoryContents("/"); + console.log(directoryItems) + + // await pipeline( + // client.createReadStream( + // "/index.json" + // ), + // fs.createWriteStream("./xxx.json") + // ); + + // let rs = fs.createReadStream("D:\\fullstack\\TDevOps\\kit\\dist\\windows\\kit.exe") + // rs.on("data", (r)=>{ + // console.log("r", r.length) + // }) + + // let stat = await client.stat("/kitw.exe"); + // console.log("stat", stat) + + // let ws = client.createWriteStream( + // "/kit.exe" + // ); + // ws.on("data", (r)=>{ + // console.log("w", r.length) + // }) + // await pipeline(rs , ws ); + // console.log("end") + + await wUpload( client, "./kit", "/kit" ) + // await wUpload( client, "../dist/windows/kit.exe", "/modules/kit/0.1.20240227094429/Windows/X86_64/kit.exe" ) + console.log("over") +} + +main() \ No newline at end of file