diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7eb421a --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out +auxpi +auxpi.tar.gz +.json +.conf +.lock +.tmp +.idea +.idea/* +.vscode +.vscode/* \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7a3b7c2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 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 General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is 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. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + 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. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + 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 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. Use with the GNU Affero General Public License. + + 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 Affero 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 special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 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 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 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 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + 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 GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..f293fd7 --- /dev/null +++ b/README.md @@ -0,0 +1,315 @@ +
+ +## AUXPI + +**基于 API 的简单图床** + +[![GitHub issues](https://img.shields.io/github/issues/aimerforreimu/AUXPI.svg)](https://github.com/aimerforreimu/AUXPI/issues) +[![GitHub forks](https://img.shields.io/github/forks/aimerforreimu/AUXPI.svg)](https://github.com/aimerforreimu/AUXPI/network) +[![GitHub stars](https://img.shields.io/github/stars/aimerforreimu/AUXPI.svg)](https://github.com/aimerforreimu/AUXPI/stargazers) +[![GitHub license](https://img.shields.io/github/license/aimerforreimu/AUXPI.svg)](https://github.com/aimerforreimu/AUXPI) +
+
+ +## 项目截图 + +![Snipaste_2018-11-05_21-09-45.png](https://ws4.sinaimg.cn/large/006A66c0ly1fwxhzushiuj31hc0m5q4v.jpg) + +![Snipaste_2018-11-05_21-10-02.png](https://ws3.sinaimg.cn/large/006A66c0ly1fwxhzuskkhj31h50o0myt.jpg) + +![Snipaste_2018-11-05_21-10-11.png](https://ws1.sinaimg.cn/large/006A66c0ly1fwxhzup2k6j31ha0o8my3.jpg) + +![Snipaste_2018-11-05_21-17-01.png](https://ws1.sinaimg.cn/large/006A66c0ly1fwxhzve3syj313j0lojup.jpg) + +![Snipaste_2018-11-05_21-11-31.png](https://ws2.sinaimg.cn/large/006A66c0ly1fwxhzuvyx9j31gw0ny0wv.jpg) + + + + +## 部署使用 + +使用该图床有两种选择,一种选择是使用 Release 中已经编译好的文件,另外一种选择是自己编译代码。 + +### 从 Release 中获取编译完成的文件 +首先需要到 [Release](https://github.com/aimerforreimu/AUXPI/releases) 中下载符合您服务器需要的最新版本的压缩包,然后在您的服务器端进行解压,解压完成以后的目录为 (以 Linux/Mac 系统为例): + +```text +├── LICENSE +├── README.md +├── conf #配置文件目录 +├── static #静态文件存放目录 +├── auxpi # 编译以后的可执行程序 +└── views # 视图文件 +``` + +**基础配置** + +您可以在 `conf/app.conf` 下面进行基础站点的配置,配置示例如下 + + +```text +appname = auxpi # 程序的名称 +httpport = 2333 # 程序所运行的端口 +runmode = dev # 程序的运行环境 dev 为开发模式 pro 为正常模式 +enablexsrf = true # 是否开启 CSRF 攻击防御(必须开启) +xsrfkey = ads093jmfas93j*3sd-212df923 #CSRF key 这里请随意填写字符串 +xsrfexpire = 3600 # CSRF token 过期时间 +``` +请注意,`dev` 模式 需要设置 $GOPATH 所以如果没有 Go 环境,请直接使用 pro 模式,调试请使用源代码进行调试 + +设置完成以后,请执行 如下命令赋予文件执行权限 + +```bash +# 赋予运行权限 +chmod u+x auxpi + +#运行程序 +./auxpi + +#后台运行程序 +nohup ./auxpi +``` + + +在程序第一次运行的时候会在 `conf/` 目录下生成 `install.lock` 和 `siteConfig.json` 如果删除 `install.lock` 的话 `siteConfig.json` 就会被初始化为最初的值,所以请不要轻易的删除 `install.lock` + +**站点配置** + +接下来您可以在 `siteConfig.json` 配置您的站点设置 + +```text + +{ + "site_name": "BusterApi 图床", #站点名称 + "site_footer": "你好世界", #footer 输出的内容 + "site_url": "/", # 站点 url + "site_upload_max_number": 10,#一次性最多可以上传多少张图片 + "site_up_load_max_size": 5,#最大允许上传的图片大小,单位 MB + "open_api_up_load": true,# 是否开启 API 上传 + "api_token": "",# API token 空为不设置 + "api_default": "SouGou", # API 默认上传上去的图床 + "cache_config": true, #是否对配置进行缓存(建议开启) + "site_upload_way": { + "local_store": false, # 是否开启本地储存(此功能等待开发) + "open_sina_pic_store": false, #是否启用新浪图床 + "sina_account": { + "user_name": "", #若开启微博图床,请填写您的微博登录用户名 + "pass_word": "", #填写您的 微博登录密码 + "reset_sina_cookie_time": 3600, # 微博 cookie 缓存时间 s + "defult_pic_size": "large" # 默认返回的微博图片的大小 + } + } +} + +``` + +其中需要说明的是 + +`reset_sina_cookie_time` 这一项最好不要更改,更改的话不要让其大于 3600s + +`defult_pic_size` 可选的参数为 + +```text +square +thumb150 +orj360 +orj480 +mw690 +mw1024 +mw2048 +small +bmiddle +large +``` + +请根据自己的需要进行配置,**配置以后需要重新启动程序才能生效** + +您现在可以通过访问 http://yourip:yourport 进行访问 + +(yourport 为 conf 中设置的端口) + + +**绑定域名** + +现在程序已经在您的后台中跑了起来,如果您想要绑定域名可以通过 Nginx 进行反代,Nginx 的配置如下 + +```conf +server +{ + listen 80; + server_name 你的域名; + error_page 404 /404.html; + error_page 502 /502.html; + + location / + { + proxy_pass http://yourip:yourport; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header REMOTE-HOST $remote_addr; + add_header X-Cache $upstream_cache_status; + expires 12h; + } + + location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md) + { + return 404; + } + +} +``` + +替换 `你的域名` 和 `http://yourip:yourport` 为你实际的值即可,保存以后,需要检查 nginx 的配置,然后重启 ,访问您的域名即可看到您的网站 + + +### 从源码中构建 + +#### 使用 Bee 工具运行程序 +```bash +cd $GOPATH/src +git clone git@github.com:aimerforreimu/AUXPI.git +cd AUXPI/ +bee run auxpi +``` + +#### 交叉编译 + +```bash +#Mac/Windows环境编译成 Linux 程序 +GOOS=linux GOARCH=amd64 bee pack +#Mac/Linux 环境编译 Windows 程序 +GOOS=windows GOARCH=amd64 bee pack +#Windows/Linux 编译 Mac 程序 +GOOS=darwin GOARCH=amd64 bee pack +``` +更多交叉编译请参考 [Go 交叉编译](https://www.jianshu.com/p/4b345a9e768e) + +## API 上传 + +其实当时写这个程序的时候没有想要写前端的页面,是看到了另一位大佬的图床,感觉这个前端页面很好看才写网页版上传,本来想直接写个 API 服务. + +### API 上传实例 + +**图片上传 V1 接口** + +| 功能 | 图片上传接口 | +| --- | --- | +| HTTP 请求方式 |  POST | +| URL  | http://yourname/api/v1/upload | + +**请求参数** + +| 参数名称 | 类型 | 是否必须|描述| +| --- | --- |---| --- | --- | +| image | File | 是 | 表单名称,上传图片| +| token  | String | 是 | 认证所必须的 token ,如果站在没有开启则留空即可 | +| apiSelect  | String | 是 | 所选择的 API 类型 | + +**apiSelect可选参数** + +| apiSelect 可选参数 | 参数说明 +| --- | ---| +| SouGou| 搜狗图床| +|Sina|新浪图床| +|Smms|SMMS 图床| + + +**成功上传返回** + +```json +{ + "code": 200, + "msg": "上传成功", + "data": { + "name": "Snipaste_2018-08-28_01-17-58.png", + "url": "https://img04.sogoucdn.com/app/a/100520146/0dcb98aadb59c6b29dc0832eb7cc094a" + } +} + +``` + +```json +{ + "code": 200, + "msg": "上传成功", + "data": { + "name": "Snipaste_2018-08-28_01-17-58.png", + "url": "https://i.loli.net/2018/11/05/5be038b1b4af6.png" + } +} +``` + +**失败返回值** + +上传出错返回值 + +```json +{ + "code": 500, + "msg": "上传失败" +} +``` + +API 未开启返回值 + +```json +{ + "code": 405, + "msg": "Method not allowed" +} +``` + Token 验证失败返回值 + +```json +{ + "code": 403, + "msg": "Forbidden" +} +``` + +选择文件为空返回值 + +```json + +{ + "code": 500, + "msg": "No files were uploaded." +} + +``` + +文件太大返回值 + +```json + +{ + "code": 500, + "msg": "File is too large." +} + +``` + +##TODO + +[x] API 上传 + +[] API 自动文档 + +[] API v2 版本分发上传,返回所有图床储存链接 + +[] 用户系统 + +[] 前后端分离,Vue 驱动前端 + +[] 后台控制 + +[] 本地上传,各大平台对接储存 + +[] 使用 MySQL 而不是 JSON + +## 致敬 + +[wisp-x](https://github.com/wisp-x) + +[astaxie](https://github.com/astaxie) \ No newline at end of file diff --git a/auxpiAll/all.go b/auxpiAll/all.go new file mode 100644 index 0000000..8826cfe --- /dev/null +++ b/auxpiAll/all.go @@ -0,0 +1,115 @@ +package auxpi + + +//Config 配置 +type SiteConfig struct { + //站点名称 + SiteName string `json:"site_name"` + //底部信息 + SiteFooter string `json:"site_footer"` + //网站链接 + SiteUrl string `json:"site_url"` + //最大上传的图片个数 + SiteUploadMaxNumber int `json:"site_upload_max_number"` + //最大图片规格 MB + SiteUpLoadMaxSize int64 `json:"site_up_load_max_size"` + //是否开启 API + OpenApiUpLoad bool `json:"open_api_up_load"` + //Api token 空为 不设置token + ApiToken string `json:"api_token"` + //Api 默认上传图床 默认为搜狗 可选 SM 图床 + ApiDefault string `json:"api_default"` + //是否对配置进行缓存 + CacheConfig bool `json:"cache_config"` + //图床储存的一些配置 + SiteUploadWay UploadConfig `json:"site_upload_way"` + +} + +type UploadConfig struct { + //TODO:是否开启本地上传 + LocalStore bool `json:"local_store"` + //是否开启微博图床 + OpenSinaPicStore bool `json:"open_sina_pic_store"` + //Sina Account + SinaAccount Account `json:"sina_account"` + // +} + +type Account struct { + //用户名 + UserName string `json:"user_name"` + //密码 + PassWord string `json:"pass_word"` + //新浪 Cookie 更新的频率,默认为3600s ,单位 s + ResetSinaCookieTime int `json:"reset_sina_cookie_time"` + //新浪图床默认使用的尺寸大小 square,thumb150,orj360,orj480,mw690,mw1024,mw2048,small,bmiddle,large 、默认为large + DefultPicSize string `json:"defult_pic_size"` +} + + //SM 图床 json +type SmResponse struct { + Code string `json:"code"` + Data SmData `json:"data"` +} + +type SmData struct { + Width int `json:"width"` + Height int `json:"height"` + Filename string `json:"filename"` + Storename string `json:"storename"` + Size int `json:"size"` + Path string `json:"path"` + Hash string `json:"hash"` + Timestamp int `json:"timestamp"` + Ip string `json:"ip"` + Url string `json:"url"` + Delete string `json:"delete"` +} + + //Sina 图床 json +type SinaMsg struct { + Code string `json:"code"` + Data SinaData `json:"data"` +} + + +type SinaData struct { + Count int `json:"count"` + Data string `json:"data"` + Pics SinaPics `json:"pics"` +} + + +type SinaPics struct { + Pic_1 picInfo `json:"pic_1"` +} + + +type picInfo struct { + Width int `json:"width"` + Size int `json:"size"` + Ret int `json:"ret"` + Height int `json:"height"` + Name string `json:"name"` + Pid string `json:"pid"` +} + + //Api & upload Json +type ResultJson struct { + Code int `json:"code"` + Msg string `json:"msg"` + Data fileData `json:"data"` +} + + +type fileData struct { + Name string `json:"name"` + Url string `json:"url"` +} + + +type ErrorJson struct { + Code int `json:"code"` + Msg string `json:"msg"` +} diff --git a/auxpiAll/all_easyjson.go b/auxpiAll/all_easyjson.go new file mode 100644 index 0000000..f48aa64 --- /dev/null +++ b/auxpiAll/all_easyjson.go @@ -0,0 +1,1315 @@ +// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. + +package auxpi + +import ( + json "encoding/json" + easyjson "github.com/mailru/easyjson" + jlexer "github.com/mailru/easyjson/jlexer" + jwriter "github.com/mailru/easyjson/jwriter" +) + +// suppress unused package warning +var ( + _ *json.RawMessage + _ *jlexer.Lexer + _ *jwriter.Writer + _ easyjson.Marshaler +) + +func easyjsonB926644bDecodeAuxpiAuxpiAll(in *jlexer.Lexer, out *picInfo) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeString() + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "width": + out.Width = int(in.Int()) + case "size": + out.Size = int(in.Int()) + case "ret": + out.Ret = int(in.Int()) + case "height": + out.Height = int(in.Int()) + case "name": + out.Name = string(in.String()) + case "pid": + out.Pid = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonB926644bEncodeAuxpiAuxpiAll(out *jwriter.Writer, in picInfo) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"width\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int(int(in.Width)) + } + { + const prefix string = ",\"size\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int(int(in.Size)) + } + { + const prefix string = ",\"ret\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int(int(in.Ret)) + } + { + const prefix string = ",\"height\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int(int(in.Height)) + } + { + const prefix string = ",\"name\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Name)) + } + { + const prefix string = ",\"pid\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Pid)) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v picInfo) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonB926644bEncodeAuxpiAuxpiAll(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v picInfo) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonB926644bEncodeAuxpiAuxpiAll(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *picInfo) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonB926644bDecodeAuxpiAuxpiAll(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *picInfo) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonB926644bDecodeAuxpiAuxpiAll(l, v) +} +func easyjsonB926644bDecodeAuxpiAuxpiAll1(in *jlexer.Lexer, out *fileData) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeString() + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "name": + out.Name = string(in.String()) + case "url": + out.Url = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonB926644bEncodeAuxpiAuxpiAll1(out *jwriter.Writer, in fileData) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"name\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Name)) + } + { + const prefix string = ",\"url\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Url)) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v fileData) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonB926644bEncodeAuxpiAuxpiAll1(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v fileData) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonB926644bEncodeAuxpiAuxpiAll1(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *fileData) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonB926644bDecodeAuxpiAuxpiAll1(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *fileData) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonB926644bDecodeAuxpiAuxpiAll1(l, v) +} +func easyjsonB926644bDecodeAuxpiAuxpiAll2(in *jlexer.Lexer, out *UploadConfig) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeString() + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "local_store": + out.LocalStore = bool(in.Bool()) + case "open_sina_pic_store": + out.OpenSinaPicStore = bool(in.Bool()) + case "sina_account": + (out.SinaAccount).UnmarshalEasyJSON(in) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonB926644bEncodeAuxpiAuxpiAll2(out *jwriter.Writer, in UploadConfig) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"local_store\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Bool(bool(in.LocalStore)) + } + { + const prefix string = ",\"open_sina_pic_store\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Bool(bool(in.OpenSinaPicStore)) + } + { + const prefix string = ",\"sina_account\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (in.SinaAccount).MarshalEasyJSON(out) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v UploadConfig) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonB926644bEncodeAuxpiAuxpiAll2(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v UploadConfig) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonB926644bEncodeAuxpiAuxpiAll2(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *UploadConfig) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonB926644bDecodeAuxpiAuxpiAll2(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *UploadConfig) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonB926644bDecodeAuxpiAuxpiAll2(l, v) +} +func easyjsonB926644bDecodeAuxpiAuxpiAll3(in *jlexer.Lexer, out *SmResponse) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeString() + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "code": + out.Code = string(in.String()) + case "data": + (out.Data).UnmarshalEasyJSON(in) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonB926644bEncodeAuxpiAuxpiAll3(out *jwriter.Writer, in SmResponse) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"code\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Code)) + } + { + const prefix string = ",\"data\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (in.Data).MarshalEasyJSON(out) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v SmResponse) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonB926644bEncodeAuxpiAuxpiAll3(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v SmResponse) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonB926644bEncodeAuxpiAuxpiAll3(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *SmResponse) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonB926644bDecodeAuxpiAuxpiAll3(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *SmResponse) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonB926644bDecodeAuxpiAuxpiAll3(l, v) +} +func easyjsonB926644bDecodeAuxpiAuxpiAll4(in *jlexer.Lexer, out *SmData) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeString() + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "width": + out.Width = int(in.Int()) + case "height": + out.Height = int(in.Int()) + case "filename": + out.Filename = string(in.String()) + case "storename": + out.Storename = string(in.String()) + case "size": + out.Size = int(in.Int()) + case "path": + out.Path = string(in.String()) + case "hash": + out.Hash = string(in.String()) + case "timestamp": + out.Timestamp = int(in.Int()) + case "ip": + out.Ip = string(in.String()) + case "url": + out.Url = string(in.String()) + case "delete": + out.Delete = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonB926644bEncodeAuxpiAuxpiAll4(out *jwriter.Writer, in SmData) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"width\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int(int(in.Width)) + } + { + const prefix string = ",\"height\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int(int(in.Height)) + } + { + const prefix string = ",\"filename\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Filename)) + } + { + const prefix string = ",\"storename\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Storename)) + } + { + const prefix string = ",\"size\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int(int(in.Size)) + } + { + const prefix string = ",\"path\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Path)) + } + { + const prefix string = ",\"hash\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Hash)) + } + { + const prefix string = ",\"timestamp\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int(int(in.Timestamp)) + } + { + const prefix string = ",\"ip\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Ip)) + } + { + const prefix string = ",\"url\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Url)) + } + { + const prefix string = ",\"delete\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Delete)) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v SmData) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonB926644bEncodeAuxpiAuxpiAll4(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v SmData) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonB926644bEncodeAuxpiAuxpiAll4(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *SmData) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonB926644bDecodeAuxpiAuxpiAll4(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *SmData) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonB926644bDecodeAuxpiAuxpiAll4(l, v) +} +func easyjsonB926644bDecodeAuxpiAuxpiAll5(in *jlexer.Lexer, out *SiteConfig) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeString() + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "site_name": + out.SiteName = string(in.String()) + case "site_footer": + out.SiteFooter = string(in.String()) + case "site_url": + out.SiteUrl = string(in.String()) + case "site_upload_max_number": + out.SiteUploadMaxNumber = int(in.Int()) + case "site_up_load_max_size": + out.SiteUpLoadMaxSize = int64(in.Int64()) + case "open_api_up_load": + out.OpenApiUpLoad = bool(in.Bool()) + case "api_token": + out.ApiToken = string(in.String()) + case "api_default": + out.ApiDefault = string(in.String()) + case "cache_config": + out.CacheConfig = bool(in.Bool()) + case "site_upload_way": + (out.SiteUploadWay).UnmarshalEasyJSON(in) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonB926644bEncodeAuxpiAuxpiAll5(out *jwriter.Writer, in SiteConfig) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"site_name\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.SiteName)) + } + { + const prefix string = ",\"site_footer\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.SiteFooter)) + } + { + const prefix string = ",\"site_url\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.SiteUrl)) + } + { + const prefix string = ",\"site_upload_max_number\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int(int(in.SiteUploadMaxNumber)) + } + { + const prefix string = ",\"site_up_load_max_size\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int64(int64(in.SiteUpLoadMaxSize)) + } + { + const prefix string = ",\"open_api_up_load\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Bool(bool(in.OpenApiUpLoad)) + } + { + const prefix string = ",\"api_token\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.ApiToken)) + } + { + const prefix string = ",\"api_default\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.ApiDefault)) + } + { + const prefix string = ",\"cache_config\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Bool(bool(in.CacheConfig)) + } + { + const prefix string = ",\"site_upload_way\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (in.SiteUploadWay).MarshalEasyJSON(out) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v SiteConfig) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonB926644bEncodeAuxpiAuxpiAll5(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v SiteConfig) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonB926644bEncodeAuxpiAuxpiAll5(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *SiteConfig) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonB926644bDecodeAuxpiAuxpiAll5(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *SiteConfig) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonB926644bDecodeAuxpiAuxpiAll5(l, v) +} +func easyjsonB926644bDecodeAuxpiAuxpiAll6(in *jlexer.Lexer, out *SinaPics) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeString() + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "pic_1": + (out.Pic_1).UnmarshalEasyJSON(in) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonB926644bEncodeAuxpiAuxpiAll6(out *jwriter.Writer, in SinaPics) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"pic_1\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (in.Pic_1).MarshalEasyJSON(out) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v SinaPics) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonB926644bEncodeAuxpiAuxpiAll6(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v SinaPics) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonB926644bEncodeAuxpiAuxpiAll6(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *SinaPics) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonB926644bDecodeAuxpiAuxpiAll6(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *SinaPics) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonB926644bDecodeAuxpiAuxpiAll6(l, v) +} +func easyjsonB926644bDecodeAuxpiAuxpiAll7(in *jlexer.Lexer, out *SinaMsg) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeString() + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "code": + out.Code = string(in.String()) + case "data": + (out.Data).UnmarshalEasyJSON(in) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonB926644bEncodeAuxpiAuxpiAll7(out *jwriter.Writer, in SinaMsg) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"code\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Code)) + } + { + const prefix string = ",\"data\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (in.Data).MarshalEasyJSON(out) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v SinaMsg) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonB926644bEncodeAuxpiAuxpiAll7(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v SinaMsg) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonB926644bEncodeAuxpiAuxpiAll7(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *SinaMsg) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonB926644bDecodeAuxpiAuxpiAll7(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *SinaMsg) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonB926644bDecodeAuxpiAuxpiAll7(l, v) +} +func easyjsonB926644bDecodeAuxpiAuxpiAll8(in *jlexer.Lexer, out *SinaData) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeString() + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "count": + out.Count = int(in.Int()) + case "data": + out.Data = string(in.String()) + case "pics": + (out.Pics).UnmarshalEasyJSON(in) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonB926644bEncodeAuxpiAuxpiAll8(out *jwriter.Writer, in SinaData) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"count\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int(int(in.Count)) + } + { + const prefix string = ",\"data\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Data)) + } + { + const prefix string = ",\"pics\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (in.Pics).MarshalEasyJSON(out) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v SinaData) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonB926644bEncodeAuxpiAuxpiAll8(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v SinaData) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonB926644bEncodeAuxpiAuxpiAll8(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *SinaData) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonB926644bDecodeAuxpiAuxpiAll8(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *SinaData) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonB926644bDecodeAuxpiAuxpiAll8(l, v) +} +func easyjsonB926644bDecodeAuxpiAuxpiAll9(in *jlexer.Lexer, out *ResultJson) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeString() + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "code": + out.Code = int(in.Int()) + case "msg": + out.Msg = string(in.String()) + case "data": + (out.Data).UnmarshalEasyJSON(in) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonB926644bEncodeAuxpiAuxpiAll9(out *jwriter.Writer, in ResultJson) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"code\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int(int(in.Code)) + } + { + const prefix string = ",\"msg\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Msg)) + } + { + const prefix string = ",\"data\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (in.Data).MarshalEasyJSON(out) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v ResultJson) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonB926644bEncodeAuxpiAuxpiAll9(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v ResultJson) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonB926644bEncodeAuxpiAuxpiAll9(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *ResultJson) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonB926644bDecodeAuxpiAuxpiAll9(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *ResultJson) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonB926644bDecodeAuxpiAuxpiAll9(l, v) +} +func easyjsonB926644bDecodeAuxpiAuxpiAll10(in *jlexer.Lexer, out *ErrorJson) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeString() + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "code": + out.Code = int(in.Int()) + case "msg": + out.Msg = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonB926644bEncodeAuxpiAuxpiAll10(out *jwriter.Writer, in ErrorJson) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"code\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int(int(in.Code)) + } + { + const prefix string = ",\"msg\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Msg)) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v ErrorJson) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonB926644bEncodeAuxpiAuxpiAll10(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v ErrorJson) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonB926644bEncodeAuxpiAuxpiAll10(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *ErrorJson) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonB926644bDecodeAuxpiAuxpiAll10(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *ErrorJson) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonB926644bDecodeAuxpiAuxpiAll10(l, v) +} +func easyjsonB926644bDecodeAuxpiAuxpiAll11(in *jlexer.Lexer, out *Account) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeString() + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "user_name": + out.UserName = string(in.String()) + case "pass_word": + out.PassWord = string(in.String()) + case "reset_sina_cookie_time": + out.ResetSinaCookieTime = int(in.Int()) + case "defult_pic_size": + out.DefultPicSize = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonB926644bEncodeAuxpiAuxpiAll11(out *jwriter.Writer, in Account) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"user_name\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.UserName)) + } + { + const prefix string = ",\"pass_word\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.PassWord)) + } + { + const prefix string = ",\"reset_sina_cookie_time\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int(int(in.ResetSinaCookieTime)) + } + { + const prefix string = ",\"defult_pic_size\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.DefultPicSize)) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v Account) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonB926644bEncodeAuxpiAuxpiAll11(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v Account) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonB926644bEncodeAuxpiAuxpiAll11(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *Account) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonB926644bDecodeAuxpiAuxpiAll11(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *Account) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonB926644bDecodeAuxpiAuxpiAll11(l, v) +} diff --git a/bootstrap/Config.go b/bootstrap/Config.go index b74a353..fcff970 100644 --- a/bootstrap/Config.go +++ b/bootstrap/Config.go @@ -1,6 +1,7 @@ package bootstrap import ( + "auxpi/auxpiAll" "bufio" "encoding/json" "github.com/astaxie/beego/cache" @@ -10,41 +11,6 @@ import ( ) type AuxpiConfig struct { - -} -type SiteConfig struct { - //站点名称 - SiteName string `json:"site_name"` - //底部信息 - SiteFooter string `json:"site_footer"` - //网站链接 - SiteUrl string `json:"site_url"` - //最大上传的图片个数 - SiteUploadMaxNumber int `json:"site_upload_max_number"` - //最大图片规格 MB - SiteUpLoadMaxSize int `json:"site_up_load_max_size"` - //图床储存的一些配置 - SiteUploadWay UploadConfig `json:"site_upload_way"` -} - -type UploadConfig struct { - //是否开启本地上传 - LocalStore bool `json:"local_store"` - //是否开启微博图床 - OpenSinaPicStore bool `json:"open_sina_pic_store"` - //Sina Account - SinaAccount Account `json:"sina_account"` - // -} -type Account struct { - //用户名 - UserName string `json:"user_name"` - //密码 - PassWord string `json:"pass_word"` - //新浪 Cookie 更新的频率,默认为3600s ,单位 s - ResetSinaCookieTime int `json:"reset_sina_cookie_time"` - //新浪图床默认使用的尺寸大小 square,thumb150,orj360,orj480,mw690,mw1024,mw2048,small,bmiddle,large 、默认为large - DefultPicSize string `json:"defult_pic_size"` } var cCache, _ = cache.NewCache("memory", `{"interval":3600}`) @@ -69,27 +35,25 @@ func (jst *JsonStruct) Load(filename string, v interface{}) { } //返回 Config 里面的 数据 -func Config() *SiteConfig { - //尝试从缓存中检索 +func Config() *auxpi.SiteConfig { + //如果开启了 Config 缓存则尝试从缓存中检索 cacheConfig := cCache.Get("SiteConfig") if cacheConfig != nil { - config, _ := cacheConfig.(*SiteConfig) + config, _ := cacheConfig.(*auxpi.SiteConfig) return config } reader := &JsonStruct{} - config := &SiteConfig{} + config := &auxpi.SiteConfig{} configDir := GetPath() + "/conf/siteConfig.json" reader.Load(configDir, config) //缓存到内存 - cCache.Put("SiteConfig", config, time.Second*3600) + if config.CacheConfig { + cCache.Put("SiteConfig", config, time.Second*3600) + } return config } -//func (this *AuxpiConfig)GetConfig(key string) { -// config :=Config() -//} //初始化的时候检测是否进行安装,生成对应的 lock 文件,生成配置的 json - func init() { baseDir := GetPath() + "/conf/" lockDir := baseDir + "install.lock" @@ -99,18 +63,23 @@ func init() { } if os.IsNotExist(err) { var f *os.File - siteconfig := SiteConfig{} - siteconfig.SiteName = "BusterApi 图床" + siteconfig := auxpi.SiteConfig{} + siteconfig.SiteName = "AuXpI API 图床" siteconfig.SiteUrl = "/" - siteconfig.SiteFooter = "你好世界" + siteconfig.SiteFooter = "" siteconfig.SiteUpLoadMaxSize = 5 siteconfig.SiteUploadMaxNumber = 10 + siteconfig.OpenApiUpLoad = true + siteconfig.ApiToken = "" + siteconfig.ApiDefault = "SouGou" + siteconfig.CacheConfig = false siteconfig.SiteUploadWay.OpenSinaPicStore = false siteconfig.SiteUploadWay.LocalStore = false siteconfig.SiteUploadWay.SinaAccount.UserName = "" siteconfig.SiteUploadWay.SinaAccount.PassWord = "" siteconfig.SiteUploadWay.SinaAccount.ResetSinaCookieTime = 3600 siteconfig.SiteUploadWay.SinaAccount.DefultPicSize = "large" + configJson, err := siteconfig.MarshalJSON() if err != nil { panic(err) diff --git a/bootstrap/Config_easyjson.go b/bootstrap/Config_easyjson.go deleted file mode 100644 index df98930..0000000 --- a/bootstrap/Config_easyjson.go +++ /dev/null @@ -1,411 +0,0 @@ -// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. - -package bootstrap - -import ( - json "encoding/json" - easyjson "github.com/mailru/easyjson" - jlexer "github.com/mailru/easyjson/jlexer" - jwriter "github.com/mailru/easyjson/jwriter" -) - -// suppress unused package warning -var ( - _ *json.RawMessage - _ *jlexer.Lexer - _ *jwriter.Writer - _ easyjson.Marshaler -) - -func easyjsonDc4bdceDecodeAuxpiBootstrap(in *jlexer.Lexer, out *UploadConfig) { - isTopLevel := in.IsStart() - if in.IsNull() { - if isTopLevel { - in.Consumed() - } - in.Skip() - return - } - in.Delim('{') - for !in.IsDelim('}') { - key := in.UnsafeString() - in.WantColon() - if in.IsNull() { - in.Skip() - in.WantComma() - continue - } - switch key { - case "local_store": - out.LocalStore = bool(in.Bool()) - case "open_sina_pic_store": - out.OpenSinaPicStore = bool(in.Bool()) - case "sina_account": - (out.SinaAccount).UnmarshalEasyJSON(in) - default: - in.SkipRecursive() - } - in.WantComma() - } - in.Delim('}') - if isTopLevel { - in.Consumed() - } -} -func easyjsonDc4bdceEncodeAuxpiBootstrap(out *jwriter.Writer, in UploadConfig) { - out.RawByte('{') - first := true - _ = first - { - const prefix string = ",\"local_store\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - out.Bool(bool(in.LocalStore)) - } - { - const prefix string = ",\"open_sina_pic_store\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - out.Bool(bool(in.OpenSinaPicStore)) - } - { - const prefix string = ",\"sina_account\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - (in.SinaAccount).MarshalEasyJSON(out) - } - out.RawByte('}') -} - -// MarshalJSON supports json.Marshaler interface -func (v UploadConfig) MarshalJSON() ([]byte, error) { - w := jwriter.Writer{} - easyjsonDc4bdceEncodeAuxpiBootstrap(&w, v) - return w.Buffer.BuildBytes(), w.Error -} - -// MarshalEasyJSON supports easyjson.Marshaler interface -func (v UploadConfig) MarshalEasyJSON(w *jwriter.Writer) { - easyjsonDc4bdceEncodeAuxpiBootstrap(w, v) -} - -// UnmarshalJSON supports json.Unmarshaler interface -func (v *UploadConfig) UnmarshalJSON(data []byte) error { - r := jlexer.Lexer{Data: data} - easyjsonDc4bdceDecodeAuxpiBootstrap(&r, v) - return r.Error() -} - -// UnmarshalEasyJSON supports easyjson.Unmarshaler interface -func (v *UploadConfig) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjsonDc4bdceDecodeAuxpiBootstrap(l, v) -} -func easyjsonDc4bdceDecodeAuxpiBootstrap1(in *jlexer.Lexer, out *SiteConfig) { - isTopLevel := in.IsStart() - if in.IsNull() { - if isTopLevel { - in.Consumed() - } - in.Skip() - return - } - in.Delim('{') - for !in.IsDelim('}') { - key := in.UnsafeString() - in.WantColon() - if in.IsNull() { - in.Skip() - in.WantComma() - continue - } - switch key { - case "site_name": - out.SiteName = string(in.String()) - case "site_footer": - out.SiteFooter = string(in.String()) - case "site_url": - out.SiteUrl = string(in.String()) - case "site_upload_max_number": - out.SiteUploadMaxNumber = int(in.Int()) - case "site_up_load_max_size": - out.SiteUpLoadMaxSize = int(in.Int()) - case "site_upload_way": - (out.SiteUploadWay).UnmarshalEasyJSON(in) - default: - in.SkipRecursive() - } - in.WantComma() - } - in.Delim('}') - if isTopLevel { - in.Consumed() - } -} -func easyjsonDc4bdceEncodeAuxpiBootstrap1(out *jwriter.Writer, in SiteConfig) { - out.RawByte('{') - first := true - _ = first - { - const prefix string = ",\"site_name\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - out.String(string(in.SiteName)) - } - { - const prefix string = ",\"site_footer\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - out.String(string(in.SiteFooter)) - } - { - const prefix string = ",\"site_url\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - out.String(string(in.SiteUrl)) - } - { - const prefix string = ",\"site_upload_max_number\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - out.Int(int(in.SiteUploadMaxNumber)) - } - { - const prefix string = ",\"site_up_load_max_size\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - out.Int(int(in.SiteUpLoadMaxSize)) - } - { - const prefix string = ",\"site_upload_way\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - (in.SiteUploadWay).MarshalEasyJSON(out) - } - out.RawByte('}') -} - -// MarshalJSON supports json.Marshaler interface -func (v SiteConfig) MarshalJSON() ([]byte, error) { - w := jwriter.Writer{} - easyjsonDc4bdceEncodeAuxpiBootstrap1(&w, v) - return w.Buffer.BuildBytes(), w.Error -} - -// MarshalEasyJSON supports easyjson.Marshaler interface -func (v SiteConfig) MarshalEasyJSON(w *jwriter.Writer) { - easyjsonDc4bdceEncodeAuxpiBootstrap1(w, v) -} - -// UnmarshalJSON supports json.Unmarshaler interface -func (v *SiteConfig) UnmarshalJSON(data []byte) error { - r := jlexer.Lexer{Data: data} - easyjsonDc4bdceDecodeAuxpiBootstrap1(&r, v) - return r.Error() -} - -// UnmarshalEasyJSON supports easyjson.Unmarshaler interface -func (v *SiteConfig) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjsonDc4bdceDecodeAuxpiBootstrap1(l, v) -} -func easyjsonDc4bdceDecodeAuxpiBootstrap2(in *jlexer.Lexer, out *JsonStruct) { - isTopLevel := in.IsStart() - if in.IsNull() { - if isTopLevel { - in.Consumed() - } - in.Skip() - return - } - in.Delim('{') - for !in.IsDelim('}') { - key := in.UnsafeString() - in.WantColon() - if in.IsNull() { - in.Skip() - in.WantComma() - continue - } - switch key { - default: - in.SkipRecursive() - } - in.WantComma() - } - in.Delim('}') - if isTopLevel { - in.Consumed() - } -} -func easyjsonDc4bdceEncodeAuxpiBootstrap2(out *jwriter.Writer, in JsonStruct) { - out.RawByte('{') - first := true - _ = first - out.RawByte('}') -} - -// MarshalJSON supports json.Marshaler interface -func (v JsonStruct) MarshalJSON() ([]byte, error) { - w := jwriter.Writer{} - easyjsonDc4bdceEncodeAuxpiBootstrap2(&w, v) - return w.Buffer.BuildBytes(), w.Error -} - -// MarshalEasyJSON supports easyjson.Marshaler interface -func (v JsonStruct) MarshalEasyJSON(w *jwriter.Writer) { - easyjsonDc4bdceEncodeAuxpiBootstrap2(w, v) -} - -// UnmarshalJSON supports json.Unmarshaler interface -func (v *JsonStruct) UnmarshalJSON(data []byte) error { - r := jlexer.Lexer{Data: data} - easyjsonDc4bdceDecodeAuxpiBootstrap2(&r, v) - return r.Error() -} - -// UnmarshalEasyJSON supports easyjson.Unmarshaler interface -func (v *JsonStruct) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjsonDc4bdceDecodeAuxpiBootstrap2(l, v) -} -func easyjsonDc4bdceDecodeAuxpiBootstrap3(in *jlexer.Lexer, out *Account) { - isTopLevel := in.IsStart() - if in.IsNull() { - if isTopLevel { - in.Consumed() - } - in.Skip() - return - } - in.Delim('{') - for !in.IsDelim('}') { - key := in.UnsafeString() - in.WantColon() - if in.IsNull() { - in.Skip() - in.WantComma() - continue - } - switch key { - case "user_name": - out.UserName = string(in.String()) - case "pass_word": - out.PassWord = string(in.String()) - case "reset_sina_cookie_time": - out.ResetSinaCookieTime = int(in.Int()) - case "defult_pic_size": - out.DefultPicSize = string(in.String()) - default: - in.SkipRecursive() - } - in.WantComma() - } - in.Delim('}') - if isTopLevel { - in.Consumed() - } -} -func easyjsonDc4bdceEncodeAuxpiBootstrap3(out *jwriter.Writer, in Account) { - out.RawByte('{') - first := true - _ = first - { - const prefix string = ",\"user_name\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - out.String(string(in.UserName)) - } - { - const prefix string = ",\"pass_word\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - out.String(string(in.PassWord)) - } - { - const prefix string = ",\"reset_sina_cookie_time\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - out.Int(int(in.ResetSinaCookieTime)) - } - { - const prefix string = ",\"defult_pic_size\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - out.String(string(in.DefultPicSize)) - } - out.RawByte('}') -} - -// MarshalJSON supports json.Marshaler interface -func (v Account) MarshalJSON() ([]byte, error) { - w := jwriter.Writer{} - easyjsonDc4bdceEncodeAuxpiBootstrap3(&w, v) - return w.Buffer.BuildBytes(), w.Error -} - -// MarshalEasyJSON supports easyjson.Marshaler interface -func (v Account) MarshalEasyJSON(w *jwriter.Writer) { - easyjsonDc4bdceEncodeAuxpiBootstrap3(w, v) -} - -// UnmarshalJSON supports json.Unmarshaler interface -func (v *Account) UnmarshalJSON(data []byte) error { - r := jlexer.Lexer{Data: data} - easyjsonDc4bdceDecodeAuxpiBootstrap3(&r, v) - return r.Error() -} - -// UnmarshalEasyJSON supports easyjson.Unmarshaler interface -func (v *Account) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjsonDc4bdceDecodeAuxpiBootstrap3(l, v) -} diff --git a/controllers/api/upload.go b/controllers/api/upload.go new file mode 100644 index 0000000..23a1597 --- /dev/null +++ b/controllers/api/upload.go @@ -0,0 +1,110 @@ +package api + +import ( + "auxpi/auxpiAll" + "auxpi/bootstrap" + "auxpi/utils" + "github.com/astaxie/beego" + "log" + "strings" +) + +type ApiUpLoadController struct { + beego.Controller + utils.UpLoadTools +} + +//需要关闭 xsrf +func (this *ApiUpLoadController) Prepare() { + this.EnableXSRF = false +} + +var picType = []string{"png", "jpg", "jpeg", "gif", "bmp"} +var siteConfig = bootstrap.Config() + +func (this *ApiUpLoadController) ApiUpLoadHandle() { + //检测是否开启 token 认证 + if siteConfig.ApiToken != "" { + //需要进行验证 + apiToken := this.GetString("token") + if apiToken != siteConfig.ApiToken { + this.errorResp(403, "Forbidden") + return + } + } + //获取上传类型 + apiSelect := this.GetString("apiSelect") + f, h, err := this.GetFile("image") + if f == nil { + this.errorResp(500, "No files were uploaded.") + } + if h.Size > siteConfig.SiteUpLoadMaxSize<<20 { + this.errorResp(500, "File is too large.") + } + defer f.Close() + if err != nil { + log.Fatal("File Upload Err", err) + } + //验证 + validate := this.validate(h.Header.Get("Content-Type"), h.Filename) + if validate { + url := this.HandleUrl(apiSelect, f, h) + + //如果有返回值 + if strings.HasPrefix(url, "http") { + this.succResp(200, "上传成功", url, h.Filename) + return + } + } + //返回失败 json + this.errorResp(500, "上传失败") + return +} + +func (this *ApiUpLoadController) ErrorCapture() { + this.errorResp(405, "Method not allowed") +} + +//验证文件后缀&文件MIME +func (this *ApiUpLoadController) validate(contentType string, fileName string) bool { + //首先检测文件的后缀 + isSuffix := false + for _, pType := range picType { + if strings.HasSuffix(fileName, pType) { + isSuffix = true + break + } + } + //然后检测 MIME 类型 + //beego.Alert(contentType) + if strings.HasPrefix(contentType, "image") && isSuffix { + for _, pType := range picType { + if strings.HasSuffix(contentType, pType) { + return true + } + } + + } + return false +} + +//错误resp +func (this *ApiUpLoadController) errorResp(code int, msg string) { + result := &auxpi.ErrorJson{} + result.Code = code + result.Msg = msg + this.Data["json"] = result + this.ServeJSON() +} + +//成功 resp +func (this *ApiUpLoadController) succResp(code int, msg string, url string, name string) { + result := &auxpi.ResultJson{} + result.Code = code + result.Msg = msg + result.Data.Url = url + result.Data.Name = name + //beego.Alert(result) + this.Data["json"] = result + this.ServeJSON() +} diff --git a/controllers/index.go b/controllers/index.go index 201a27a..5942a2e 100644 --- a/controllers/index.go +++ b/controllers/index.go @@ -14,9 +14,9 @@ var siteConfig = bootstrap.Config() func (c *PagesController) URLMapping() { c.Mapping("Show", c.IndexShow) - c.Mapping("AboutShow", c.AboutShow) c.Mapping("SinaShow", c.SinaShow) - c.Mapping("TestShow", c.TestShow) + c.Mapping("SmmsShow", c.SmmsShow) + c.Mapping("AboutShow", c.AboutShow) } // @router / [get] @@ -29,8 +29,8 @@ func (this *PagesController) IndexShow() { //单位为Mb 5mb ==> 5*1024 kb this.Data["maxPicSize"] = siteConfig.SiteUpLoadMaxSize << 10 this.Data["apiSelect"] = "SouGou" - this.Data["iconStyle"] ="sougou" - this.Data["iconColor"] ="orange" + this.Data["iconStyle"] = "sougou" + this.Data["iconColor"] = "orange" this.Data["xsrf_token"] = this.XSRFToken() this.LayoutSections = make(map[string]string) this.LayoutSections["Scripts"] = "upload/uploadScript.tpl" @@ -46,15 +46,15 @@ func (this *PagesController) SinaShow() { this.Data["siteName"] = siteConfig.SiteName this.Data["siteUrl"] = siteConfig.SiteUrl this.Data["siteFooterText"] = siteConfig.SiteFooter - this.Data["apiUrl"] = "/api/v1/auth/upload" + this.Data["apiUrl"] = "/api/v1/auth/upload/" this.Data["maxNumber"] = siteConfig.SiteUploadMaxNumber //单位为Mb 5mb ==> 5*1024 kb - this.Data["maxPicSize"] = siteConfig.SiteUpLoadMaxSize << 10 + this.Data[" maxPicSize"] = siteConfig.SiteUpLoadMaxSize << 10 this.Data["xsrf_token"] = this.XSRFToken() this.LayoutSections = make(map[string]string) this.Data["apiSelect"] = "Sina" - this.Data["iconStyle"] ="xinlang" - this.Data["iconColor"] ="red" + this.Data["iconStyle"] = "xinlang" + this.Data["iconColor"] = "red" this.LayoutSections["Scripts"] = "upload/uploadScript.tpl" this.LayoutSections["Header"] = "layouts/header.tpl" this.LayoutSections["Footer"] = "layouts/footer.tpl" @@ -68,48 +68,40 @@ func (this *PagesController) SinaShow() { this.TplName = "upload/ban.tpl" } -// @router /about/ [get] -func (this *PagesController) AboutShow() { +// @router /Smms [get] +func (this *PagesController) SmmsShow() { this.Data["siteName"] = siteConfig.SiteName this.Data["siteUrl"] = siteConfig.SiteUrl this.Data["siteFooterText"] = siteConfig.SiteFooter + this.Data["apiUrl"] = "/api/v1/auth/upload/" + this.Data["maxNumber"] = siteConfig.SiteUploadMaxNumber + //单位为Mb 5mb ==> 5*1024 kb + this.Data["maxPicSize"] = siteConfig.SiteUpLoadMaxSize << 10 + this.Data["xsrf_token"] = this.XSRFToken() this.LayoutSections = make(map[string]string) + this.Data["apiSelect"] = "Smms" + this.Data["iconStyle"] = "sm" + this.Data["iconColor"] = "blue" this.LayoutSections["Scripts"] = "upload/uploadScript.tpl" this.LayoutSections["Header"] = "layouts/header.tpl" this.LayoutSections["Footer"] = "layouts/footer.tpl" this.LayoutSections["Left"] = "layouts/left.tpl" - this.Data["title"] = "关于 Buster API 图床" this.Layout = "layouts/app.tpl" - this.TplName = "about/about-me.tpl" -} + this.TplName = "upload/box.tpl" -// @router /app [get] -func (this *PagesController) InstallShow() { - this.Data["siteName"] = siteConfig.SiteName - this.Data["siteUrl"] = siteConfig.SiteUrl - this.Data["siteFooterText"] = siteConfig.SiteFooter - this.TplName = "about-me.tpl" } -// @router /test [get] -func (this *PagesController) TestShow() { +// @router /about/ [get] +func (this *PagesController) AboutShow() { this.Data["siteName"] = siteConfig.SiteName this.Data["siteUrl"] = siteConfig.SiteUrl this.Data["siteFooterText"] = siteConfig.SiteFooter - this.Data["apiUrl"] = "/api/v1/auth/upload" - this.Data["maxNumber"] = siteConfig.SiteUploadMaxNumber - //单位为Mb 5mb ==> 5*1024 kb - this.Data["maxPicSize"] = siteConfig.SiteUpLoadMaxSize << 10 - this.Data["xsrf_token"] = this.XSRFToken() this.LayoutSections = make(map[string]string) - this.Data["apiSelect"] = "Sina" - this.Data["iconStyle"] ="xinlang" - this.Data["iconColor"] ="red" - //this.LayoutSections["Scripts"] = "upload/uploadScript.tpl" + this.LayoutSections["Scripts"] = "upload/uploadScript.tpl" this.LayoutSections["Header"] = "layouts/header.tpl" this.LayoutSections["Footer"] = "layouts/footer.tpl" this.LayoutSections["Left"] = "layouts/left.tpl" + this.Data["title"] = "关于 Buster API 图床" this.Layout = "layouts/app.tpl" - this.TplName = "upload/ban.tpl" - + this.TplName = "about/about-me.tpl" } diff --git a/controllers/upload/authUpLoad.go b/controllers/upload/authUpLoad.go index d77cc5a..96c46bf 100644 --- a/controllers/upload/authUpLoad.go +++ b/controllers/upload/authUpLoad.go @@ -1,40 +1,21 @@ package controllers import ( - "auxpi/server" - "encoding/base64" + "auxpi/auxpiAll" + "auxpi/bootstrap" + "auxpi/utils" "github.com/astaxie/beego" "log" "strings" ) -//easyjson:json type UpLoadController struct { beego.Controller - server.Sina - server.SouGou -} - -//easyjson:json -type ResultJson struct { - Code int `json:"code"` - Msg string `json:"msg"` - Data fileData `json:"data"` -} - -//easyjson:json -type fileData struct { - Name string `json:"name"` - Url string `json:"url"` -} - -//easyjson:json -type ErrorJson struct { - Code int `json:"code"` - Msg string `json:"msg"` + utils.UpLoadTools } var picType = []string{"png", "jpg", "jpeg", "gif", "bmp"} +var siteConfig = bootstrap.Config() func (c *UpLoadController) URLMapping() { c.Mapping("UpLoad", c.AuthUpLoadHandle) @@ -49,75 +30,46 @@ func (this *UpLoadController) AuthUpLoadHandle() { if err != nil { log.Fatal("File Upload Err", err) } - imgMime := h.Header.Get("Content-Type") + //是否为空文件 + if f == nil { + this.errorResp(500, "No files were uploaded.") + } + //检测是否超出大小限制 + if h.Size > siteConfig.SiteUpLoadMaxSize<<20 { + this.errorResp(500, "File is too large.") + } //验证 - validate := this.validate(imgMime, h.Filename) + validate := this.Validate(h.Header.Get("Content-Type"), h.Filename) if validate { - //读取文件 - size := h.Size - fileContent := make([]byte, size) - f.Read(fileContent) - url := "" - switch apiSelect { - case "SouGou": - url = this.UpLoadToSouGou(fileContent) - case "Sina": - url = this.UpLoadToSina(fileContent, imgMime) - default: - url = "" - } + url := this.HandleUrl(apiSelect, f, h) //如果有返回值 if strings.HasPrefix(url, "http") { - //配置 json - result := &ResultJson{} - result.Code = 200 - result.Msg = "上传成功" - result.Data.Url = url - result.Data.Name = h.Filename - //beego.Alert(result) - this.Data["json"] = result - this.ServeJSON() - return + this.succResp(200, "上传成功", url, h.Filename) } } //返回失败 json - result := &ErrorJson{} - result.Code = 500 - result.Msg = "上传失败" - this.Data["json"] = result - this.ServeJSON() + this.errorResp(500, "上传失败") return } -//验证文件后缀&文件MIME -func (this *UpLoadController) validate(contentType string, fileName string) bool { - //首先检测文件的后缀 - isSuffix := false - for _, pType := range picType { - if strings.HasSuffix(fileName, pType) { - isSuffix = true - break - } - } - //然后检测 MIME 类型 - //beego.Alert(contentType) - if strings.HasPrefix(contentType, "image") && isSuffix { - for _, pType := range picType { - if strings.HasSuffix(contentType, pType) { - return true - } - } - - } - return false +//错误resp +func (this *UpLoadController) errorResp(code int, msg string) { + result := &auxpi.ErrorJson{} + result.Code = code + result.Msg = msg + this.Data["json"] = result + this.ServeJSON() } -//tools--- -func Decode(enc *base64.Encoding, str string) string { - data, err := enc.DecodeString(str) - if err != nil { - panic(err) - } - return string(data) +//成功 resp +func (this *UpLoadController) succResp(code int, msg string, url string, name string) { + result := &auxpi.ResultJson{} + result.Code = code + result.Msg = msg + result.Data.Url = url + result.Data.Name = name + //beego.Alert(result) + this.Data["json"] = result + this.ServeJSON() } diff --git a/controllers/api/api.go b/controllers/uploadComm/handle.go similarity index 53% rename from controllers/api/api.go rename to controllers/uploadComm/handle.go index 9befbf4..4ae237f 100644 --- a/controllers/api/api.go +++ b/controllers/uploadComm/handle.go @@ -1,53 +1,54 @@ -package api +package uploadComm import ( - "auxpi/bootstrap" + "auxpi/auxpiAll" "auxpi/server" "github.com/astaxie/beego" "log" "strings" ) -//easyjson:json -type ApiUpLoadController struct { + +type HandleController struct { beego.Controller server.Sina server.SouGou } -//easyjson:json -type ResultJson struct { - Code int `json:"code"` - Msg string `json:"msg"` - Data fileData `json:"data"` -} - -//easyjson:json -type fileData struct { - Name string `json:"name"` - Url string `json:"url"` -} +var picType = []string{"png", "jpg", "jpeg", "gif", "bmp"} -//easyjson:json -type ErrorJson struct { - Code int `json:"code"` - Msg string `json:"msg"` -} +//验证文件后缀&文件MIME +func (this *HandleController) Validate(contentType string, fileName string) bool { + //首先检测文件的后缀 + isSuffix := false + for _, pType := range picType { + if strings.HasSuffix(fileName, pType) { + isSuffix = true + break + } + } + //然后检测 MIME 类型 + //beego.Alert(contentType) + if strings.HasPrefix(contentType, "image") && isSuffix { + for _, pType := range picType { + if strings.HasSuffix(contentType, pType) { + return true + } + } -//需要关闭 xsrf -func (this *ApiUpLoadController) Prepare() { - this.EnableXSRF = false + } + return false } -var picType = []string{"png", "jpg", "jpeg", "gif", "bmp"} -var siteConfig = bootstrap.Config() - -func (c *ApiUpLoadController) URLMapping() { - c.Mapping("UpLoad", c.AuthUpLoadHandle) +func (this *HandleController) ErrorResponse(code int, msg string) { + result := &auxpi.ErrorJson{} + result.Code = code + result.Msg = msg + this.Data["json"] = result + this.ServeJSON() } -// @router /api/v1/upload/ [post] -func (this *ApiUpLoadController) AuthUpLoadHandle() { +func (this *HandleController) Check() { //获取上传类型 apiSelect := this.GetString("apiSelect") f, h, err := this.GetFile("image") @@ -57,35 +58,30 @@ func (this *ApiUpLoadController) AuthUpLoadHandle() { } imgMime := h.Header.Get("Content-Type") //验证 - validate := this.validate(imgMime, h.Filename) + validate := this.Validate(imgMime, h.Filename) if validate { //读取文件 size := h.Size fileContent := make([]byte, size) f.Read(fileContent) url := "" - if siteConfig.SiteUploadWay.OpenSinaPicStore == false { + switch apiSelect { + case "SouGou": url = this.UpLoadToSouGou(fileContent) - } else { - switch apiSelect { - case "SouGou": - url = this.UpLoadToSouGou(fileContent) - case "Sina": - url = this.UpLoadToSina(fileContent, imgMime) - default: - url = "" - } + case "Sina": + url = this.UpLoadToSina(fileContent, imgMime) + default: + url = "" } - //url := this.UpLoadToSina(fileContent,imgMime) //如果有返回值 if strings.HasPrefix(url, "http") { //配置 json - result := &ResultJson{} + result := &auxpi.ResultJson{} result.Code = 200 result.Msg = "上传成功" result.Data.Url = url result.Data.Name = h.Filename - beego.Alert(result) + //beego.Alert(result) this.Data["json"] = result this.ServeJSON() return @@ -93,33 +89,10 @@ func (this *ApiUpLoadController) AuthUpLoadHandle() { } //返回失败 json - result := &ErrorJson{} + result := &auxpi.ErrorJson{} result.Code = 500 result.Msg = "上传失败" this.Data["json"] = result this.ServeJSON() return -} - -//验证文件后缀&文件MIME -func (this *ApiUpLoadController) validate(contentType string, fileName string) bool { - //首先检测文件的后缀 - isSuffix := false - for _, pType := range picType { - if strings.HasSuffix(fileName, pType) { - isSuffix = true - break - } - } - //然后检测 MIME 类型 - //beego.Alert(contentType) - if strings.HasPrefix(contentType, "image") && isSuffix { - for _, pType := range picType { - if strings.HasSuffix(contentType, pType) { - return true - } - } - - } - return false -} +} \ No newline at end of file diff --git a/routers/commentsRouter_______go_src_auxpi_controllers.go b/routers/commentsRouter_______go_src_auxpi_controllers.go index 8d71b2b..bac5583 100644 --- a/routers/commentsRouter_______go_src_auxpi_controllers.go +++ b/routers/commentsRouter_______go_src_auxpi_controllers.go @@ -27,8 +27,8 @@ func init() { beego.GlobalControllerRouter["auxpi/controllers:PagesController"] = append(beego.GlobalControllerRouter["auxpi/controllers:PagesController"], beego.ControllerComments{ - Method: "AboutShow", - Router: `/about/`, + Method: "SmmsShow", + Router: `/Smms`, AllowHTTPMethods: []string{"get"}, MethodParams: param.Make(), Filters: nil, @@ -36,17 +36,8 @@ func init() { beego.GlobalControllerRouter["auxpi/controllers:PagesController"] = append(beego.GlobalControllerRouter["auxpi/controllers:PagesController"], beego.ControllerComments{ - Method: "InstallShow", - Router: `/app`, - AllowHTTPMethods: []string{"get"}, - MethodParams: param.Make(), - Filters: nil, - Params: nil}) - - beego.GlobalControllerRouter["auxpi/controllers:PagesController"] = append(beego.GlobalControllerRouter["auxpi/controllers:PagesController"], - beego.ControllerComments{ - Method: "TestShow", - Router: `/test`, + Method: "AboutShow", + Router: `/about/`, AllowHTTPMethods: []string{"get"}, MethodParams: param.Make(), Filters: nil, diff --git a/routers/commentsRouter_______go_src_auxpi_controllers_api.go b/routers/commentsRouter_______go_src_auxpi_controllers_api.go deleted file mode 100644 index c80582c..0000000 --- a/routers/commentsRouter_______go_src_auxpi_controllers_api.go +++ /dev/null @@ -1,19 +0,0 @@ -package routers - -import ( - "github.com/astaxie/beego" - "github.com/astaxie/beego/context/param" -) - -func init() { - - beego.GlobalControllerRouter["auxpi/controllers/api:ApiUpLoadController"] = append(beego.GlobalControllerRouter["auxpi/controllers/api:ApiUpLoadController"], - beego.ControllerComments{ - Method: "AuthUpLoadHandle", - Router: `/api/v1/upload/`, - AllowHTTPMethods: []string{"post"}, - MethodParams: param.Make(), - Filters: nil, - Params: nil}) - -} diff --git a/routers/commentsRouter_controllers.go b/routers/commentsRouter_controllers.go index 8d71b2b..bac5583 100644 --- a/routers/commentsRouter_controllers.go +++ b/routers/commentsRouter_controllers.go @@ -27,8 +27,8 @@ func init() { beego.GlobalControllerRouter["auxpi/controllers:PagesController"] = append(beego.GlobalControllerRouter["auxpi/controllers:PagesController"], beego.ControllerComments{ - Method: "AboutShow", - Router: `/about/`, + Method: "SmmsShow", + Router: `/Smms`, AllowHTTPMethods: []string{"get"}, MethodParams: param.Make(), Filters: nil, @@ -36,17 +36,8 @@ func init() { beego.GlobalControllerRouter["auxpi/controllers:PagesController"] = append(beego.GlobalControllerRouter["auxpi/controllers:PagesController"], beego.ControllerComments{ - Method: "InstallShow", - Router: `/app`, - AllowHTTPMethods: []string{"get"}, - MethodParams: param.Make(), - Filters: nil, - Params: nil}) - - beego.GlobalControllerRouter["auxpi/controllers:PagesController"] = append(beego.GlobalControllerRouter["auxpi/controllers:PagesController"], - beego.ControllerComments{ - Method: "TestShow", - Router: `/test`, + Method: "AboutShow", + Router: `/about/`, AllowHTTPMethods: []string{"get"}, MethodParams: param.Make(), Filters: nil, diff --git a/routers/commentsRouter_controllers_api.go b/routers/commentsRouter_controllers_api.go deleted file mode 100644 index c80582c..0000000 --- a/routers/commentsRouter_controllers_api.go +++ /dev/null @@ -1,19 +0,0 @@ -package routers - -import ( - "github.com/astaxie/beego" - "github.com/astaxie/beego/context/param" -) - -func init() { - - beego.GlobalControllerRouter["auxpi/controllers/api:ApiUpLoadController"] = append(beego.GlobalControllerRouter["auxpi/controllers/api:ApiUpLoadController"], - beego.ControllerComments{ - Method: "AuthUpLoadHandle", - Router: `/api/v1/upload/`, - AllowHTTPMethods: []string{"post"}, - MethodParams: param.Make(), - Filters: nil, - Params: nil}) - -} diff --git a/routers/router.go b/routers/router.go index 3fcac51..99e7a8b 100644 --- a/routers/router.go +++ b/routers/router.go @@ -1,16 +1,24 @@ package routers import ( + "auxpi/bootstrap" "auxpi/controllers" "auxpi/controllers/api" "auxpi/controllers/upload" "github.com/astaxie/beego" ) +var siteConfig = bootstrap.Config() + func init() { //注册控制器内的路由 beego.Include(&controllers.UpLoadController{}) beego.Include(&page.PagesController{}) - beego.Include(&api.ApiUpLoadController{}) + //API 路由单独控制 + if siteConfig.OpenApiUpLoad { + beego.Router("api/v1/upload", &api.ApiUpLoadController{}, "post:ApiUpLoadHandle") + beego.Router("api/v1/upload", &api.ApiUpLoadController{}, "get,put,patch,delete,options,head:ErrorCapture") + } else { + beego.Router("api/v1/upload", &api.ApiUpLoadController{}, "*:ErrorCapture") + } } - diff --git a/server/Sina.go b/server/Sina.go index fac009a..a50f7c2 100644 --- a/server/Sina.go +++ b/server/Sina.go @@ -1,10 +1,10 @@ package server import ( + "auxpi/auxpiAll" "auxpi/bootstrap" "encoding/base64" "fmt" - "github.com/astaxie/beego" "github.com/astaxie/beego/cache" "hash/crc32" "io/ioutil" @@ -18,39 +18,12 @@ import ( type Sina struct { } -//easyjson:json -type SinaMsg struct { - Code string `json:"code"` - Data SinaData `json:"data"` -} - -//easyjson:json -type SinaData struct { - Count int `json:"count"` - Data string `json:"data"` - Pics SinaPics `json:"pics"` -} - -//easyjson:json -type SinaPics struct { - Pic_1 picInfo `json:"pic_1"` -} - -//easyjson:json -type picInfo struct { - Width int `json:"width"` - Size int `json:"size"` - Ret int `json:"ret"` - Height int `json:"height"` - Name string `json:"name"` - Pid string `json:"pid"` -} - var picType = []string{"png", "jpg", "jpeg", "gif", "bmp"} var memoryCache, _ = cache.NewCache("memory", `{"interval":3600}`) //获取 config 的配置 var siteConfig = bootstrap.Config() +//新浪图床登录 func (this *Sina) Login(name string, pass string) interface{} { url := "https://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.15)&_=1403138799543" userInfo := make(map[string]string) @@ -60,7 +33,7 @@ func (this *Sina) Login(name string, pass string) interface{} { return cookie } -//返回综合 []*httpCookie +//获取新浪图床 Cookie func (this *Sina) getCookies(durl string, data map[string]string) (interface{}) { //尝试从缓存里面获取 Cookie if memoryCache.Get("SinaCookies") != nil { @@ -131,6 +104,7 @@ func (this *Sina) UpLoadToSina(img []byte, imgType string) string { return this.getSinaUrl(body, imgType) } +//获取 Sina 图床 URL func (this *Sina) getSinaUrl(body []byte, imgType string) string { str := string(body) //正则获取 @@ -139,8 +113,7 @@ func (this *Sina) getSinaUrl(body []byte, imgType string) string { res := regexp.MustCompile(pat) rule := regexp.MustCompile(check) jsons := res.FindAllStringSubmatch(str, -1) - //beego.Alert(rule, jsons) - msg := SinaMsg{} + msg := auxpi.SinaMsg{} //解析 json 到 struct msg.UnmarshalJSON([]byte(jsons[0][1])) //验证 pid 的合法性 @@ -156,8 +129,6 @@ func (this *Sina) getSinaUrl(body []byte, imgType string) string { suffix = "jpg" } sinaUrl := "https://ws" + sinaNumber + ".sinaimg.cn/" + size + "/" + pid + "." + suffix - //转成 rune 直接截取即可 "image/png" - beego.Alert(sinaUrl) return sinaUrl } return "" diff --git a/server/Sina_easyjson.go b/server/Sina_easyjson.go deleted file mode 100644 index 33806c5..0000000 --- a/server/Sina_easyjson.go +++ /dev/null @@ -1,458 +0,0 @@ -// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. - -package server - -import ( - json "encoding/json" - easyjson "github.com/mailru/easyjson" - jlexer "github.com/mailru/easyjson/jlexer" - jwriter "github.com/mailru/easyjson/jwriter" -) - -// suppress unused package warning -var ( - _ *json.RawMessage - _ *jlexer.Lexer - _ *jwriter.Writer - _ easyjson.Marshaler -) - -func easyjsonB7954fafDecodeAuxpiServer(in *jlexer.Lexer, out *picInfo) { - isTopLevel := in.IsStart() - if in.IsNull() { - if isTopLevel { - in.Consumed() - } - in.Skip() - return - } - in.Delim('{') - for !in.IsDelim('}') { - key := in.UnsafeString() - in.WantColon() - if in.IsNull() { - in.Skip() - in.WantComma() - continue - } - switch key { - case "width": - out.Width = int(in.Int()) - case "size": - out.Size = int(in.Int()) - case "ret": - out.Ret = int(in.Int()) - case "height": - out.Height = int(in.Int()) - case "name": - out.Name = string(in.String()) - case "pid": - out.Pid = string(in.String()) - default: - in.SkipRecursive() - } - in.WantComma() - } - in.Delim('}') - if isTopLevel { - in.Consumed() - } -} -func easyjsonB7954fafEncodeAuxpiServer(out *jwriter.Writer, in picInfo) { - out.RawByte('{') - first := true - _ = first - { - const prefix string = ",\"width\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - out.Int(int(in.Width)) - } - { - const prefix string = ",\"size\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - out.Int(int(in.Size)) - } - { - const prefix string = ",\"ret\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - out.Int(int(in.Ret)) - } - { - const prefix string = ",\"height\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - out.Int(int(in.Height)) - } - { - const prefix string = ",\"name\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - out.String(string(in.Name)) - } - { - const prefix string = ",\"pid\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - out.String(string(in.Pid)) - } - out.RawByte('}') -} - -// MarshalJSON supports json.Marshaler interface -func (v picInfo) MarshalJSON() ([]byte, error) { - w := jwriter.Writer{} - easyjsonB7954fafEncodeAuxpiServer(&w, v) - return w.Buffer.BuildBytes(), w.Error -} - -// MarshalEasyJSON supports easyjson.Marshaler interface -func (v picInfo) MarshalEasyJSON(w *jwriter.Writer) { - easyjsonB7954fafEncodeAuxpiServer(w, v) -} - -// UnmarshalJSON supports json.Unmarshaler interface -func (v *picInfo) UnmarshalJSON(data []byte) error { - r := jlexer.Lexer{Data: data} - easyjsonB7954fafDecodeAuxpiServer(&r, v) - return r.Error() -} - -// UnmarshalEasyJSON supports easyjson.Unmarshaler interface -func (v *picInfo) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjsonB7954fafDecodeAuxpiServer(l, v) -} -func easyjsonB7954fafDecodeAuxpiServer1(in *jlexer.Lexer, out *SinaPics) { - isTopLevel := in.IsStart() - if in.IsNull() { - if isTopLevel { - in.Consumed() - } - in.Skip() - return - } - in.Delim('{') - for !in.IsDelim('}') { - key := in.UnsafeString() - in.WantColon() - if in.IsNull() { - in.Skip() - in.WantComma() - continue - } - switch key { - case "pic_1": - (out.Pic_1).UnmarshalEasyJSON(in) - default: - in.SkipRecursive() - } - in.WantComma() - } - in.Delim('}') - if isTopLevel { - in.Consumed() - } -} -func easyjsonB7954fafEncodeAuxpiServer1(out *jwriter.Writer, in SinaPics) { - out.RawByte('{') - first := true - _ = first - { - const prefix string = ",\"pic_1\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - (in.Pic_1).MarshalEasyJSON(out) - } - out.RawByte('}') -} - -// MarshalJSON supports json.Marshaler interface -func (v SinaPics) MarshalJSON() ([]byte, error) { - w := jwriter.Writer{} - easyjsonB7954fafEncodeAuxpiServer1(&w, v) - return w.Buffer.BuildBytes(), w.Error -} - -// MarshalEasyJSON supports easyjson.Marshaler interface -func (v SinaPics) MarshalEasyJSON(w *jwriter.Writer) { - easyjsonB7954fafEncodeAuxpiServer1(w, v) -} - -// UnmarshalJSON supports json.Unmarshaler interface -func (v *SinaPics) UnmarshalJSON(data []byte) error { - r := jlexer.Lexer{Data: data} - easyjsonB7954fafDecodeAuxpiServer1(&r, v) - return r.Error() -} - -// UnmarshalEasyJSON supports easyjson.Unmarshaler interface -func (v *SinaPics) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjsonB7954fafDecodeAuxpiServer1(l, v) -} -func easyjsonB7954fafDecodeAuxpiServer2(in *jlexer.Lexer, out *SinaMsg) { - isTopLevel := in.IsStart() - if in.IsNull() { - if isTopLevel { - in.Consumed() - } - in.Skip() - return - } - in.Delim('{') - for !in.IsDelim('}') { - key := in.UnsafeString() - in.WantColon() - if in.IsNull() { - in.Skip() - in.WantComma() - continue - } - switch key { - case "code": - out.Code = string(in.String()) - case "data": - (out.Data).UnmarshalEasyJSON(in) - default: - in.SkipRecursive() - } - in.WantComma() - } - in.Delim('}') - if isTopLevel { - in.Consumed() - } -} -func easyjsonB7954fafEncodeAuxpiServer2(out *jwriter.Writer, in SinaMsg) { - out.RawByte('{') - first := true - _ = first - { - const prefix string = ",\"code\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - out.String(string(in.Code)) - } - { - const prefix string = ",\"data\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - (in.Data).MarshalEasyJSON(out) - } - out.RawByte('}') -} - -// MarshalJSON supports json.Marshaler interface -func (v SinaMsg) MarshalJSON() ([]byte, error) { - w := jwriter.Writer{} - easyjsonB7954fafEncodeAuxpiServer2(&w, v) - return w.Buffer.BuildBytes(), w.Error -} - -// MarshalEasyJSON supports easyjson.Marshaler interface -func (v SinaMsg) MarshalEasyJSON(w *jwriter.Writer) { - easyjsonB7954fafEncodeAuxpiServer2(w, v) -} - -// UnmarshalJSON supports json.Unmarshaler interface -func (v *SinaMsg) UnmarshalJSON(data []byte) error { - r := jlexer.Lexer{Data: data} - easyjsonB7954fafDecodeAuxpiServer2(&r, v) - return r.Error() -} - -// UnmarshalEasyJSON supports easyjson.Unmarshaler interface -func (v *SinaMsg) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjsonB7954fafDecodeAuxpiServer2(l, v) -} -func easyjsonB7954fafDecodeAuxpiServer3(in *jlexer.Lexer, out *SinaData) { - isTopLevel := in.IsStart() - if in.IsNull() { - if isTopLevel { - in.Consumed() - } - in.Skip() - return - } - in.Delim('{') - for !in.IsDelim('}') { - key := in.UnsafeString() - in.WantColon() - if in.IsNull() { - in.Skip() - in.WantComma() - continue - } - switch key { - case "count": - out.Count = int(in.Int()) - case "data": - out.Data = string(in.String()) - case "pics": - (out.Pics).UnmarshalEasyJSON(in) - default: - in.SkipRecursive() - } - in.WantComma() - } - in.Delim('}') - if isTopLevel { - in.Consumed() - } -} -func easyjsonB7954fafEncodeAuxpiServer3(out *jwriter.Writer, in SinaData) { - out.RawByte('{') - first := true - _ = first - { - const prefix string = ",\"count\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - out.Int(int(in.Count)) - } - { - const prefix string = ",\"data\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - out.String(string(in.Data)) - } - { - const prefix string = ",\"pics\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - (in.Pics).MarshalEasyJSON(out) - } - out.RawByte('}') -} - -// MarshalJSON supports json.Marshaler interface -func (v SinaData) MarshalJSON() ([]byte, error) { - w := jwriter.Writer{} - easyjsonB7954fafEncodeAuxpiServer3(&w, v) - return w.Buffer.BuildBytes(), w.Error -} - -// MarshalEasyJSON supports easyjson.Marshaler interface -func (v SinaData) MarshalEasyJSON(w *jwriter.Writer) { - easyjsonB7954fafEncodeAuxpiServer3(w, v) -} - -// UnmarshalJSON supports json.Unmarshaler interface -func (v *SinaData) UnmarshalJSON(data []byte) error { - r := jlexer.Lexer{Data: data} - easyjsonB7954fafDecodeAuxpiServer3(&r, v) - return r.Error() -} - -// UnmarshalEasyJSON supports easyjson.Unmarshaler interface -func (v *SinaData) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjsonB7954fafDecodeAuxpiServer3(l, v) -} -func easyjsonB7954fafDecodeAuxpiServer4(in *jlexer.Lexer, out *Sina) { - isTopLevel := in.IsStart() - if in.IsNull() { - if isTopLevel { - in.Consumed() - } - in.Skip() - return - } - in.Delim('{') - for !in.IsDelim('}') { - key := in.UnsafeString() - in.WantColon() - if in.IsNull() { - in.Skip() - in.WantComma() - continue - } - switch key { - default: - in.SkipRecursive() - } - in.WantComma() - } - in.Delim('}') - if isTopLevel { - in.Consumed() - } -} -func easyjsonB7954fafEncodeAuxpiServer4(out *jwriter.Writer, in Sina) { - out.RawByte('{') - first := true - _ = first - out.RawByte('}') -} - -// MarshalJSON supports json.Marshaler interface -func (v Sina) MarshalJSON() ([]byte, error) { - w := jwriter.Writer{} - easyjsonB7954fafEncodeAuxpiServer4(&w, v) - return w.Buffer.BuildBytes(), w.Error -} - -// MarshalEasyJSON supports easyjson.Marshaler interface -func (v Sina) MarshalEasyJSON(w *jwriter.Writer) { - easyjsonB7954fafEncodeAuxpiServer4(w, v) -} - -// UnmarshalJSON supports json.Unmarshaler interface -func (v *Sina) UnmarshalJSON(data []byte) error { - r := jlexer.Lexer{Data: data} - easyjsonB7954fafDecodeAuxpiServer4(&r, v) - return r.Error() -} - -// UnmarshalEasyJSON supports easyjson.Unmarshaler interface -func (v *Sina) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjsonB7954fafDecodeAuxpiServer4(l, v) -} diff --git a/server/Smms.go b/server/Smms.go new file mode 100644 index 0000000..bb03295 --- /dev/null +++ b/server/Smms.go @@ -0,0 +1,34 @@ +package server + +import ( + "auxpi/auxpiAll" + "bytes" + "io/ioutil" + "mime/multipart" + "net/http" + "regexp" +) + +type Smms struct { +} + +//上传 SM 图床 返回图片 URL +func (this *Smms) UpLoatToSmms(img []byte, imgInfo string) string { + body := new(bytes.Buffer) + w := multipart.NewWriter(body) + content_type := w.FormDataContentType() + pat := `filename="(.*)"` + res := regexp.MustCompile(pat) + name := res.FindAllStringSubmatch(imgInfo, -1) + file, _ := w.CreateFormFile("smfile", name[0][1]) + file.Write(img) + w.Close() + req, _ := http.NewRequest("POST", "https://sm.ms/api/upload", body) + req.Header.Set("Content-Type", content_type) + resp, _ := http.DefaultClient.Do(req) + defer resp.Body.Close() + data, _ := ioutil.ReadAll(resp.Body) + sm := auxpi.SmResponse{} + sm.UnmarshalJSON([]byte(string(data))) + return string(sm.Data.Url) +} diff --git a/server/SouGou.go b/server/SouGou.go index 42076a8..533deec 100644 --- a/server/SouGou.go +++ b/server/SouGou.go @@ -18,7 +18,7 @@ func (this *SouGou) UpLoadToSouGou(img []byte) string { sufStr := "DQotLS0tLS1XZWJLaXRGb3JtQm91bmRhcnlHTGZHQjBIZ1VOdHBUVDFrLS0NCg==" preStr = bootstrap.Decode(base64.StdEncoding, preStr) sufStr = bootstrap.Decode(base64.StdEncoding, sufStr) - imgStr := bootstrap.Decode(base64.StdEncoding, base64.StdEncoding.EncodeToString(img)) + imgStr := string(img) data := []byte(preStr + string(img) + sufStr) url := "http://pic.sogou.com/pic/upload_pic.jsp" client := &http.Client{} diff --git a/static/app/newicon/iconfont.css b/static/app/newicon/iconfont.css index 7964e28..429c174 100644 --- a/static/app/newicon/iconfont.css +++ b/static/app/newicon/iconfont.css @@ -1,10 +1,10 @@ @font-face {font-family: "iconfont"; - src: url('iconfont.eot?t=1541323666371'); /* IE9*/ - src: url('iconfont.eot?t=1541323666371#iefix') format('embedded-opentype'), /* IE6-IE8 */ - url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAdMAAsAAAAACiAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFY7j0tIY21hcAAAAYAAAABuAAABstYsnFZnbHlmAAAB8AAAA0kAAAO4ouPbfWhlYWQAAAU8AAAALwAAADYTP/0haGhlYQAABWwAAAAgAAAAJAf2A29obXR4AAAFjAAAAA4AAAAUFAAAAGxvY2EAAAWcAAAADAAAAAwBhgKGbWF4cAAABagAAAAfAAAAIAEWAIZuYW1lAAAFyAAAAUUAAAJtPlT+fXBvc3QAAAcQAAAAPAAAAE00I4RTeJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2BkYWCcwMDKwMHUyXSGgYGhH0IzvmYwYuRgYGBiYGVmwAoC0lxTGByeSr4MY27438AQw9zA0AAUZgTJAQDlugxUeJztkcENgCAMRV8BjTEu4A4O4QCencKTJxeWhCmwpR4cwk9e0/9DaFKADojKoiSQE8F0aCotj4wtT6zqRwYC4Z7zlq+y1wrf/pXoPT/2alCvM6Tn19Tq+rpk23Osz5ujGyNfjv1C2R3iAwirGecAAHicTVLNbxtFFJ83Mzvrzdq7Xns9Yye2s/5axzIsij92W5c6aRDEEAk3h9RCpFKkVFAhUSIa1PZE2iBVRYZyQXxEQiBEwL1yag7lwLER4sAfYMSxUqUeqHthYZwTozfz3rz303vze/MQIPTvkALZRT2ELDsheGMJ/OAsMA+/AB4OfMEDP/Cg3QrcwM+D8BuiiwVXecpmKs+BLew8NP1Eu+XhooF/+ZJFMMVQ6RQy1NTKCbWxVNyYye/uBDyb6+X003c7MWclO2efev9qHSAS+erX9TsPfn9wZx2uHoaTNmACVLlEtWQ26poZU53dbKZqudSti4Plcz08l4zE45ot8Ory8uDivl0/exMwY6wF+o+XZRKZC8lFJK9vyZhsIR0J5CCkuKjqo4AjwRBYNu9CYLVcA6p5aPgtt8gwOg6fKAqYx8dgKkr4JHxWahZguqHYLEjZ+l/wWIKxCP/IlKCYgeem6mB2CioiRGXth+Rn4qM4KqIV9BJCYIAqe9cIZNkutD1ISocBJQ+q0iMjNqswt6pyGvhuVVquL/KQartliZKWFBXC+38rytOjjX5jUPJadHLdOe+Ve5XRmNLxSJ6L4YF6+Cpz4GGZLd7Gm8sqvPnB9UT4zQ0l45SHW1b/Y9Kmk/tHE1pInquf+qR/NPmn73aSukn//Gk0VpTxqLdfJenOTmr1N9KvwXyZ1JWoi7djL79F1srF3izpWUiV/MZUkCzKogXUQl20it5ANyRLt4urLlOZnBRXPWE4fTsvyytIUpKqz4UBRPoZF9yfwpjbkprbLGAedCEPrNLwg/ZJAps3fa7anLjMhMWTHjaDKVpw6WAyLHMs+i5+mlWYPd8Yqi6O0DNvX/mCvGhR8ejDjyDeqV8Kr+2NKB3t7d2j9N5310oMYoW12wsLS4dpJ5eec5w1La6RhBVR4HPdwPSuHjXN6AVTiWvPv27QWByAW7Gobt1qCpVQyhOxFK0NYcg4Wa3I/4rmnFpv3vtBG+zgT99TtctX3t0Ov6bvXNjYjdDNjcFNOJP+TCPNVNVpdQ7+wrxWIDizML/OsJGMESPxODZD9vMRMNOmvq3pcViZUSmmKcMSGNOI89rpV1g06iQhKr4/lOP9H+a9uFoAAAB4nGNgZGBgAGKvIonv8fw2Xxm4WRhA4AbLnUkI+n8PizhzCZDLwcAEEgUAIYIKNwB4nGNgZGBgbvjfwBDDwsDA8P8lizgDUAQFsAIAcccEbXicY2FgYGBBwwABBAAVAAAAAAAAAGwAqgEaAdx4nGNgZGBgYGWoYmBnAAEmIOYCQgaG/2A+AwAVXAGdAHicZY9NTsMwEIVf+gekEqqoYIfkBWIBKP0Rq25YVGr3XXTfpk6bKokjx63UA3AejsAJOALcgDvwSCebNpbH37x5Y08A3OAHHo7fLfeRPVwyO3INF7gXrlN/EG6QX4SbaONVuEX9TdjHM6bCbXRheYPXuGL2hHdhDx18CNdwjU/hOvUv4Qb5W7iJO/wKt9Dx6sI+5l5XuI1HL/bHVi+cXqnlQcWhySKTOb+CmV7vkoWt0uqca1vEJlODoF9JU51pW91T7NdD5yIVWZOqCas6SYzKrdnq0AUb5/JRrxeJHoQm5Vhj/rbGAo5xBYUlDowxQhhkiMro6DtVZvSvsUPCXntWPc3ndFsU1P9zhQEC9M9cU7qy0nk6T4E9XxtSdXQrbsuelDSRXs1JErJCXta2VELqATZlV44RelzRiT8oZ0j/AAlabsgAAAB4nGNgYoAALgbsgJWRiZGZkYWRlZGNgS09sySjNIk9vTQxr7LUmK04vzQ9v5S9IjMvJzEvnYEBAMJqCyc=') format('woff'), - url('iconfont.ttf?t=1541323666371') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ - url('iconfont.svg?t=1541323666371#iconfont') format('svg'); /* iOS 4.1- */ + src: url('iconfont.eot?t=1541350998228'); /* IE9*/ + src: url('iconfont.eot?t=1541350998228#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAfIAAsAAAAACsAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFY7j0tIY21hcAAAAYAAAAB2AAAByILNiOVnbHlmAAAB+AAAA7MAAAQ40o5V8WhlYWQAAAWsAAAALgAAADYTQNKpaGhlYQAABdwAAAAgAAAAJAf2A3BobXR4AAAF/AAAAA4AAAAYGAAAAGxvY2EAAAYMAAAADgAAAA4DRgIGbWF4cAAABhwAAAAfAAAAIAEXAIZuYW1lAAAGPAAAAUUAAAJtPlT+fXBvc3QAAAeEAAAAQQAAAFK/wA2NeJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2BkYWCcwMDKwMHUyXSGgYGhH0IzvmYwYuRgYGBiYGVmwAoC0lxTGByeSr4MY27438AQw9zA0AAUZgTJAQDlugxUeJztkcEJhEAMRV90XERswB48WoXM2VLEg+0asApNJu7C9uAf3jD/EyaQAA1QG6ORQHYE12qplLymK3liNt/TUpGOQSfNup3LdcG/+0ms+nu8k9hfVeny4VVf7vy4xqcY+FunwHegOfA96RbYHDmXgHQDhM8epgAAeJxNUk1sG0UUnjczO2vHP+u11zNxYjvrn91NFDCKf3ZblzppUBvTSIQc0giRSpFSQYXkENGgtifcBqkqChQlQvxEQiBEwL1yaqSkB46NEAcuJByMOCIh9UCDVLIwDheeZt68ee/Te/O9eUhB6J82eUraKIR0JFAGFRBSHNXRPd0TutAt4Tk1VRFezUlaPa3qSU/UHOz7/rF/fOz/fXF3b3cPnkrlX9j9T9qbmxtQ3ARrY8M/3PR/sVrLrYPln3vqwGq1DlotsA5brcMWAll/nQJZRU2EdCMueHkcXO8ssBJ+DkrYcwX3XK8Etapne24WhFsWDSy4ypMGU3kGDGFkoeLGa9USzkfxw09YAFMMVj2XolqwGFfL4/m5vuzqisfTmWYmdPpePWJOpgeNU29dGwUIBD79fvbu3o97d2fh2rZ/VANMgCpXaDCRDttaSlMHFirJkUzy9uX5iXNNPJgIxGJBQ+CpiYn5y2vG6NlbgBljVQh9c1UmkbmQFCJ5fUG6ZFH2VSBT9tRGjos8jgRDoBu8AZ5etaPgZKHsVu08w2jff6wooO3vg6Yo/mP/r0IlB70N+UpOrsX/BfclGAv/p1QB8il4pndsDfRAeYSorP2IfEdcFEN5NIleQAiioMrelT1ZtgG1EiSkIwqFEjjSIyMGs5jtqJx6ru1Iy3ZFFpI1uyhR0pJLBf/Bn4ryZGdupjxfKFXp0Q3z5VKxaXW6lHY7Uo/5W+r2i8yER0U2dgcvTKjw6ts34v7nN5WUWVxf1GfeIzV69GDniOYS50ZPvT+zc3Q8Y9cTIY3++m2nqyjdTnPNIf31leTUD2RmBIaKZFQJ23gpcv41Ml3MNwdIU0eq5NelgqRRGg2jKmqgKfQKuilZ2g3s2ExlclJs9YRh7+28KK8gSUmqLhdRINLPuOBuD8bsqjy5wTxWggZkgVll16udJDB4xeWqwYnNNBg76WHF66EFlw4mwzLHmGvjJ2mFGUPlddXGAXrm9eWPyfM6Fb+/8y7E6qNX/OvtDqWddvs+pfe/vF5gEMlN3xkeHt/uNzP9g6Y5HYwFSVwPKPBRKIrpvVBY08KXNCUWfPalKI3EALgeCYf02xWhEkp5PJKkI+uwzjiZsuR/hTPmSHOo9HVwfgV/8KYavLrcWvI/o29cmlsN0IW5+Vtwpv/DIKkkHbNa3/oN85EcwanhoVmGo4kIicb/iPSRtWwAtH4ttBQMxWCyT6WYJqO6wJgGzIunL7Bw2ExAWHy1Lcf7X+iK6vwAeJxjYGRgYADifLnOvnh+m68M3CwMIHCD1T0MQf9vYBFnbgByORiYQKIA9GgIcAAAeJxjYGRgYG7438AQw8LAwPD/JYs4A1AEBbABAHHIBG54nGNhYGBgwYIBAWgAGQAAAAAAAABAAKwA6gFaAhwAAHicY2BkYGBgY6hiYGcAASYg5gJCBob/YD4DABV3AZ4AeJxlj01OwzAQhV/6B6QSqqhgh+QFYgEo/RGrblhUavdddN+mTpsqiSPHrdQDcB6OwAk4AtyAO/BIJ5s2lsffvHljTwDc4Acejt8t95E9XDI7cg0XuBeuU38QbpBfhJto41W4Rf1N2MczpsJtdGF5g9e4YvaEd2EPHXwI13CNT+E69S/hBvlbuIk7/Aq30PHqwj7mXle4jUcv9sdWL5xeqeVBxaHJIpM5v4KZXu+Sha3S6pxrW8QmU4OgX0lTnWlb3VPs10PnIhVZk6oJqzpJjMqt2erQBRvn8lGvF4kehCblWGP+tsYCjnEFhSUOjDFCGGSIyujoO1Vm9K+xQ8Jee1Y9zed0WxTU/3OFAQL0z1xTurLSeTpPgT1fG1J1dCtuy56UNJFezUkSskJe1rZUQuoBNmVXjhF6XNGJPyhnSP8ACVpuyAAAAHicY2BigAAuBuyAjZGJkZmRhZGVkY2RnYGpOJctPbMkozSJPb00Ma+y1JitOL80Pb+UvSIzLycxL52BAQDiNAwSAAAA') format('woff'), + url('iconfont.ttf?t=1541350998228') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ + url('iconfont.svg?t=1541350998228#iconfont') format('svg'); /* iOS 4.1- */ } .iconfont { @@ -15,6 +15,8 @@ -moz-osx-font-smoothing: grayscale; } +.icon-sm:before { content: "\e634"; } + .icon-github:before { content: "\e678"; } .icon-guanyu3:before { content: "\e64c"; } diff --git a/static/app/newicon/iconfont.eot b/static/app/newicon/iconfont.eot index 9fdbdb2..9190c58 100644 Binary files a/static/app/newicon/iconfont.eot and b/static/app/newicon/iconfont.eot differ diff --git a/static/app/newicon/iconfont.js b/static/app/newicon/iconfont.js index 2d91c7e..32b4d7f 100644 --- a/static/app/newicon/iconfont.js +++ b/static/app/newicon/iconfont.js @@ -1 +1 @@ -(function(window){var svgSprite='';var script=function(){var scripts=document.getElementsByTagName("script");return scripts[scripts.length-1]}();var shouldInjectCss=script.getAttribute("data-injectcss");var ready=function(fn){if(document.addEventListener){if(~["complete","loaded","interactive"].indexOf(document.readyState)){setTimeout(fn,0)}else{var loadFn=function(){document.removeEventListener("DOMContentLoaded",loadFn,false);fn()};document.addEventListener("DOMContentLoaded",loadFn,false)}}else if(document.attachEvent){IEContentLoaded(window,fn)}function IEContentLoaded(w,fn){var d=w.document,done=false,init=function(){if(!done){done=true;fn()}};var polling=function(){try{d.documentElement.doScroll("left")}catch(e){setTimeout(polling,50);return}init()};polling();d.onreadystatechange=function(){if(d.readyState=="complete"){d.onreadystatechange=null;init()}}}};var before=function(el,target){target.parentNode.insertBefore(el,target)};var prepend=function(el,target){if(target.firstChild){before(el,target.firstChild)}else{target.appendChild(el)}};function appendSvg(){var div,svg;div=document.createElement("div");div.innerHTML=svgSprite;svgSprite=null;svg=div.getElementsByTagName("svg")[0];if(svg){svg.setAttribute("aria-hidden","true");svg.style.position="absolute";svg.style.width=0;svg.style.height=0;svg.style.overflow="hidden";prepend(svg,document.body)}}if(shouldInjectCss&&!window.__iconfont__svg__cssinject__){window.__iconfont__svg__cssinject__=true;try{document.write("")}catch(e){console&&console.log(e)}}ready(appendSvg)})(window) \ No newline at end of file +(function(window){var svgSprite='';var script=function(){var scripts=document.getElementsByTagName("script");return scripts[scripts.length-1]}();var shouldInjectCss=script.getAttribute("data-injectcss");var ready=function(fn){if(document.addEventListener){if(~["complete","loaded","interactive"].indexOf(document.readyState)){setTimeout(fn,0)}else{var loadFn=function(){document.removeEventListener("DOMContentLoaded",loadFn,false);fn()};document.addEventListener("DOMContentLoaded",loadFn,false)}}else if(document.attachEvent){IEContentLoaded(window,fn)}function IEContentLoaded(w,fn){var d=w.document,done=false,init=function(){if(!done){done=true;fn()}};var polling=function(){try{d.documentElement.doScroll("left")}catch(e){setTimeout(polling,50);return}init()};polling();d.onreadystatechange=function(){if(d.readyState=="complete"){d.onreadystatechange=null;init()}}}};var before=function(el,target){target.parentNode.insertBefore(el,target)};var prepend=function(el,target){if(target.firstChild){before(el,target.firstChild)}else{target.appendChild(el)}};function appendSvg(){var div,svg;div=document.createElement("div");div.innerHTML=svgSprite;svgSprite=null;svg=div.getElementsByTagName("svg")[0];if(svg){svg.setAttribute("aria-hidden","true");svg.style.position="absolute";svg.style.width=0;svg.style.height=0;svg.style.overflow="hidden";prepend(svg,document.body)}}if(shouldInjectCss&&!window.__iconfont__svg__cssinject__){window.__iconfont__svg__cssinject__=true;try{document.write("")}catch(e){console&&console.log(e)}}ready(appendSvg)})(window) \ No newline at end of file diff --git a/static/app/newicon/iconfont.svg b/static/app/newicon/iconfont.svg index 7d3dd03..4946418 100644 --- a/static/app/newicon/iconfont.svg +++ b/static/app/newicon/iconfont.svg @@ -20,6 +20,9 @@ Created by iconfont /> + + + diff --git a/static/app/newicon/iconfont.ttf b/static/app/newicon/iconfont.ttf index 70637f5..063dc66 100644 Binary files a/static/app/newicon/iconfont.ttf and b/static/app/newicon/iconfont.ttf differ diff --git a/static/app/newicon/iconfont.woff b/static/app/newicon/iconfont.woff index a6c65d5..f53c486 100644 Binary files a/static/app/newicon/iconfont.woff and b/static/app/newicon/iconfont.woff differ diff --git a/utils/upload.go b/utils/upload.go new file mode 100644 index 0000000..f4ef20a --- /dev/null +++ b/utils/upload.go @@ -0,0 +1,93 @@ +package utils + +import ( + "auxpi/auxpiAll" + "auxpi/bootstrap" + "auxpi/server" + "github.com/astaxie/beego" + "mime/multipart" + "strings" +) + +type UpLoadTools struct { + beego.Controller + server.Smms + server.SouGou + server.Sina +} + +var picType = []string{"png", "jpg", "jpeg", "gif", "bmp"} +var siteConfig = bootstrap.Config() +//返回不同图床的 URL +func (this *UpLoadTools) HandleUrl(apiSelect string, f multipart.File, h *multipart.FileHeader) string { + imgMime := h.Header.Get("Content-Type") + imgInfo := h.Header.Get("Content-Disposition") + //读取文件 + size := h.Size + fileContent := make([]byte, size) + f.Read(fileContent) + url := "" + switch apiSelect { + case "SouGou": + url = this.UpLoadToSouGou(fileContent) + case "Sina": + if siteConfig.SiteUploadWay.OpenSinaPicStore == false { + url = "" + } + url = this.UpLoadToSina(fileContent, imgMime) + case "Smms": + url = this.UpLoatToSmms(fileContent, imgInfo) + default: + switch siteConfig.ApiDefault { + case "SouGou": + url = this.UpLoadToSouGou(fileContent) + case "Smms": + url = this.UpLoatToSmms(fileContent, imgInfo) + } + + } + return url +} + +//验证文件后缀&文件MIME +func (this *UpLoadTools) Validate(contentType string, fileName string) bool { + //首先检测文件的后缀 + isSuffix := false + for _, pType := range picType { + if strings.HasSuffix(fileName, pType) { + isSuffix = true + break + } + } + //然后检测 MIME 类型 + //beego.Alert(contentType) + if strings.HasPrefix(contentType, "image") && isSuffix { + for _, pType := range picType { + if strings.HasSuffix(contentType, pType) { + return true + } + } + + } + return false +} + +//错误resp +func (this *UpLoadTools) ErrorResp(code int, msg string) { + result := &auxpi.ErrorJson{} + result.Code = code + result.Msg = msg + this.Data["json"] = result + this.ServeJSON() +} + +func (this *UpLoadTools) SuccResp(code int, msg string, url string, name string) { + result := &auxpi.ResultJson{} + result.Code = code + result.Msg = msg + result.Data.Url = url + result.Data.Name = name + //beego.Alert(result) + this.Data["json"] = result + this.ServeJSON() +} diff --git a/views/layouts/footer.tpl b/views/layouts/footer.tpl index aa947e3..8cd77c5 100644 --- a/views/layouts/footer.tpl +++ b/views/layouts/footer.tpl @@ -11,7 +11,7 @@ \ No newline at end of file diff --git a/views/layouts/left.tpl b/views/layouts/left.tpl index 0a65ec5..530d72f 100644 --- a/views/layouts/left.tpl +++ b/views/layouts/left.tpl @@ -8,6 +8,10 @@
新浪图床
+ + +
SMMS图床
+
说明
diff --git a/views/upload/box.tpl b/views/upload/box.tpl index 6a3fd80..f97c1fe 100644 --- a/views/upload/box.tpl +++ b/views/upload/box.tpl @@ -3,7 +3,7 @@

Image Upload

-

最大可上传 5 MB的图片,单次同时可选择 10 张。

+

最大可上传 {{ .maxPicSize}} KB的图片,单次同时可选择 {{ .maxNumber}} 张。