Initial Commit
This commit is contained in:
commit
75ccbb95d6
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/lure-web
|
15
.woodpecker.yml
Normal file
15
.woodpecker.yml
Normal file
@ -0,0 +1,15 @@
|
||||
platform: linux/amd64
|
||||
steps:
|
||||
docker:
|
||||
image: gitea.elara.ws/elara6331/builder
|
||||
environment:
|
||||
- REGISTRY=gitea.elara.ws
|
||||
- REGISTRY_USERNAME=Elara6331
|
||||
- KO_DOCKER_REPO=gitea.elara.ws/lure
|
||||
- KO_DEFAULTBASEIMAGE=gitea.elara.ws/elara6331/static
|
||||
secrets: [ registry_password ]
|
||||
commands:
|
||||
- registry-login
|
||||
- ko build -B --platform=linux/amd64,linux/arm64,linux/riscv64 -t latest,${CI_COMMIT_TAG} --sbom=none
|
||||
when:
|
||||
event: tag
|
661
LICENSE
Normal file
661
LICENSE
Normal file
@ -0,0 +1,661 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
69
README.md
Normal file
69
README.md
Normal file
@ -0,0 +1,69 @@
|
||||
# lure-web
|
||||
|
||||
`lure-web` is a website for LURE and a web interface that lets users search and get information about LURE's packages.
|
||||
|
||||
LURE Web is powered by [Salix](https://gitea.elara.ws/Elara6331/salix) and [PicoCSS](https://picocss.com/).
|
||||
|
||||
## API
|
||||
|
||||
### Search Packages
|
||||
|
||||
- **Endpoint:** `GET /api/search`
|
||||
- **Description:** Search for packages based on specified search options.
|
||||
- **Parameters:**
|
||||
- None
|
||||
- **Query Parameters:**
|
||||
- `sort` (optional) - Sort the results by a specific attribute.
|
||||
- Possible values: "name", "version", "repo"
|
||||
- `filter` (optional) - Apply a filter to narrow down the search results.
|
||||
- Possible values: "inrepo", "arch"
|
||||
- `fv` (optional) - Filter value to be used based on the selected filter.
|
||||
- `q` (optional) - The search query string.
|
||||
- **Response:**
|
||||
- Content-Type: application/json
|
||||
- Body: List of translated packages based on search criteria.
|
||||
|
||||
### Get Package Information
|
||||
|
||||
- **Endpoint:** `GET /api/pkg/:repo/:pkg`
|
||||
- **Description:** Get detailed information about a specific package.
|
||||
- **Parameters:**
|
||||
- `:repo` - Repository name
|
||||
- `:pkg` - Package name
|
||||
- **Response:**
|
||||
- Content-Type: application/json
|
||||
- Body: Translated information about the specified package.
|
||||
|
||||
### Get Package Script
|
||||
|
||||
- **Endpoint:** `GET /api/pkg/:repo/:pkg/script`
|
||||
- **Description:** Get the script associated with a specific package.
|
||||
- **Parameters:**
|
||||
- `:repo` - Repository name
|
||||
- `:pkg` - Package name
|
||||
- **Query Parameters:**
|
||||
- `highlight` (optional) - If set to `true`, the script will be returned with syntax highlighting.
|
||||
- **Response:**
|
||||
- If `highlight` is `true`:
|
||||
- Content-Type: text/html
|
||||
- Body: Script with syntax highlighting in HTML format.
|
||||
- If `highlight` is not provided or set to "false":
|
||||
- Content-Type: text/x-shellscript
|
||||
- Body: Raw script content.
|
||||
|
||||
### Language Translation
|
||||
|
||||
- **Header:** `Accept-Language`
|
||||
- Use the Accept-Language header to specify the preferred language for response translations.
|
||||
- **Query Parameter:** `lang`
|
||||
- You can also include a `lang` query parameter instead of `Accept-Language` to specify the translation language.
|
||||
|
||||
### Error Handling
|
||||
|
||||
- **Response:**
|
||||
- Content-Type: application/json
|
||||
- Body: A JSON object with a single `error` string field containing an explanation of the error.
|
||||
|
||||
---
|
||||
|
||||
|
73
api.go
Normal file
73
api.go
Normal file
@ -0,0 +1,73 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/rs/cors"
|
||||
"github.com/uptrace/bunrouter"
|
||||
"lure.sh/lure/pkg/loggerctx"
|
||||
"lure.sh/lure/pkg/search"
|
||||
"go.elara.ws/logger/log"
|
||||
)
|
||||
|
||||
func registerAPI(mux *bunrouter.Router) {
|
||||
g := mux.NewGroup(
|
||||
"/api",
|
||||
bunrouter.WithMiddleware(apiErrorHandler, corsMiddleware()),
|
||||
)
|
||||
|
||||
g.GET("/*path", func(w http.ResponseWriter, req bunrouter.Request) error {
|
||||
return HTTPError{404, "endpoint not found: " + req.URL.Path}
|
||||
})
|
||||
|
||||
g.GET("/search", func(w http.ResponseWriter, req bunrouter.Request) error {
|
||||
ctx := loggerctx.With(req.Context(), log.Logger)
|
||||
pkgs, err := search.Search(ctx, searchOptions(req.Request))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bunrouter.JSON(w, translatePkgs(getLanguages(req.Request), pkgs))
|
||||
})
|
||||
|
||||
g.GET("/pkg/:repo/:pkg", func(w http.ResponseWriter, req bunrouter.Request) error {
|
||||
ctx := loggerctx.With(req.Context(), log.Logger)
|
||||
pkg, err := search.GetPkg(ctx, req.Param("repo"), req.Param("pkg"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bunrouter.JSON(w, translatePkg(getLanguages(req.Request), pkg))
|
||||
})
|
||||
|
||||
g.GET("/pkg/:repo/:pkg/script", func(w http.ResponseWriter, req bunrouter.Request) error {
|
||||
ctx := loggerctx.With(req.Context(), log.Logger)
|
||||
script, err := search.GetScript(ctx, req.Param("repo"), req.Param("pkg"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer script.Close()
|
||||
|
||||
if req.URL.Query().Get("highlight") == "true" {
|
||||
data, err := highlight(script)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
_, err = io.WriteString(w, string(data))
|
||||
return err
|
||||
} else {
|
||||
w.Header().Set("Content-Type", "text/x-shellscript")
|
||||
_, err = io.Copy(w, script)
|
||||
return err
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func corsMiddleware() bunrouter.MiddlewareFunc {
|
||||
ch := cors.AllowAll()
|
||||
return func(next bunrouter.HandlerFunc) bunrouter.HandlerFunc {
|
||||
return bunrouter.HTTPHandler(ch.Handler(next))
|
||||
}
|
||||
}
|
89
badge.go
Normal file
89
badge.go
Normal file
@ -0,0 +1,89 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/uptrace/bunrouter"
|
||||
"lure.sh/lure/pkg/search"
|
||||
)
|
||||
|
||||
type pkgCtxKey struct{}
|
||||
|
||||
//go:embed static/icons/badge-logo.base64
|
||||
var logoData string
|
||||
|
||||
// registerBadge registers the route for generating LURE badges
|
||||
func registerBadge(mux *bunrouter.Router) {
|
||||
// rp is a reverse proxy that will route user requests to shields.io
|
||||
// and return the responses, stripped of any irrelevant headers.
|
||||
rp := &httputil.ReverseProxy{
|
||||
Rewrite: func(pr *httputil.ProxyRequest) {
|
||||
pkg := pr.In.Context().Value(pkgCtxKey{}).(search.Package)
|
||||
pr.Out.URL = genBadgeURL(pkg, pr.In.URL.Query().Get("style"))
|
||||
pr.Out.Host = pr.Out.URL.Host
|
||||
},
|
||||
ModifyResponse: func(r *http.Response) error {
|
||||
// Remove all the irrelevant headers from the response.
|
||||
r.Header.Del("Alt-Svc")
|
||||
r.Header.Del("Cache-Control")
|
||||
r.Header.Del("Report-To")
|
||||
r.Header.Del("Cf-Cache-Status")
|
||||
r.Header.Del("Cf-Ray")
|
||||
r.Header.Del("Fly-Request-Id")
|
||||
r.Header.Del("Nel")
|
||||
r.Header.Del("Report-To")
|
||||
r.Header.Del("Server")
|
||||
r.Header.Del("Via")
|
||||
r.Header.Del("Set-Cookie")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
mux.GET("/pkg/:repo/:pkg/badge.svg", func(w http.ResponseWriter, req bunrouter.Request) error {
|
||||
repo := req.Param("repo")
|
||||
name := req.Param("pkg")
|
||||
|
||||
pkg, err := search.GetPkg(req.Context(), repo, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx := context.WithValue(req.Context(), pkgCtxKey{}, pkg)
|
||||
rp.ServeHTTP(w, req.Request.WithContext(ctx))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func genBadgeURL(pkg search.Package, style string) *url.URL {
|
||||
v := url.Values{}
|
||||
v.Set("label", pkg.Name)
|
||||
v.Set("message", genBadgeVersion(pkg))
|
||||
v.Set("logo", logoData)
|
||||
v.Set("color", "0060A8")
|
||||
if style != "" {
|
||||
v.Set("style", style)
|
||||
}
|
||||
return &url.URL{Scheme: "https", Host: "img.shields.io", Path: "/static/v1", RawQuery: v.Encode()}
|
||||
}
|
||||
|
||||
func genBadgeVersion(pkg search.Package) string {
|
||||
sb := strings.Builder{}
|
||||
if pkg.Epoch != 0 {
|
||||
sb.WriteString(strconv.Itoa(int(pkg.Epoch)))
|
||||
sb.WriteByte(':')
|
||||
}
|
||||
|
||||
sb.WriteString(pkg.Version)
|
||||
|
||||
if pkg.Release != 0 {
|
||||
sb.WriteByte('-')
|
||||
sb.WriteString(strconv.Itoa(pkg.Release))
|
||||
}
|
||||
return sb.String()
|
||||
}
|
90
error.go
Normal file
90
error.go
Normal file
@ -0,0 +1,90 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/uptrace/bunrouter"
|
||||
"go.elara.ws/logger/log"
|
||||
"go.elara.ws/salix"
|
||||
)
|
||||
|
||||
type HTTPError struct {
|
||||
StatusCode int `json:"status_code"`
|
||||
Msg string `json:"error"`
|
||||
}
|
||||
|
||||
func (he HTTPError) Error() string {
|
||||
return he.Msg
|
||||
}
|
||||
|
||||
func (he HTTPError) StatusText() string {
|
||||
return http.StatusText(he.StatusCode)
|
||||
}
|
||||
|
||||
func httpError(err error) *HTTPError {
|
||||
switch err := err.(type) {
|
||||
case nil:
|
||||
return nil
|
||||
case HTTPError:
|
||||
return &err
|
||||
case *HTTPError:
|
||||
return err
|
||||
default:
|
||||
switch {
|
||||
case errors.Is(err, sql.ErrNoRows):
|
||||
return &HTTPError{404, err.Error()}
|
||||
default:
|
||||
return &HTTPError{500, err.Error()}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func siteErrorHandler(ns *salix.Namespace) bunrouter.MiddlewareFunc {
|
||||
return func(next bunrouter.HandlerFunc) bunrouter.HandlerFunc {
|
||||
tmpl, ok := ns.GetTemplate("error.html")
|
||||
if !ok {
|
||||
return basicErrorHandler(next)
|
||||
}
|
||||
|
||||
|
||||
return func(w http.ResponseWriter, req bunrouter.Request) error {
|
||||
err := next(w, req)
|
||||
if err != nil {
|
||||
he := httpError(err)
|
||||
w.WriteHeader(he.StatusCode)
|
||||
err := tmpl.
|
||||
WithVarMap(map[string]any{"error": he}).
|
||||
Execute(w)
|
||||
if err != nil {
|
||||
log.Error("Error while executing error template").Err(err).Send()
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func apiErrorHandler(next bunrouter.HandlerFunc) bunrouter.HandlerFunc {
|
||||
return func(w http.ResponseWriter, req bunrouter.Request) error {
|
||||
err := next(w, req)
|
||||
if err != nil {
|
||||
he := httpError(err)
|
||||
w.WriteHeader(he.StatusCode)
|
||||
_ = bunrouter.JSON(w, map[string]string{"error": he.Msg})
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func basicErrorHandler(next bunrouter.HandlerFunc) bunrouter.HandlerFunc {
|
||||
return func(w http.ResponseWriter, req bunrouter.Request) error {
|
||||
err := next(w, req)
|
||||
if err != nil {
|
||||
he := httpError(err)
|
||||
http.Error(w, he.Msg, he.StatusCode)
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
66
go.mod
Normal file
66
go.mod
Normal file
@ -0,0 +1,66 @@
|
||||
module lure.sh/lure-web
|
||||
|
||||
go 1.21.3
|
||||
|
||||
require (
|
||||
github.com/alecthomas/chroma/v2 v2.9.1
|
||||
github.com/rs/cors v1.10.1
|
||||
github.com/uptrace/bunrouter v1.0.21
|
||||
go.elara.ws/logger v0.0.0-20230421022458-e80700db2090
|
||||
go.elara.ws/salix v0.0.0-20231228151958-745b6c0e5f10
|
||||
golang.org/x/text v0.13.0
|
||||
lure.sh/lure v0.1.4-0.20231223033536-5dc31f43aa39
|
||||
)
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect
|
||||
github.com/acomagu/bufpipe v1.0.4 // indirect
|
||||
github.com/cloudflare/circl v1.3.3 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
|
||||
github.com/dlclark/regexp2 v1.10.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.5.0 // indirect
|
||||
github.com/go-git/go-git/v5 v5.9.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/gookit/color v1.5.1 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/jmoiron/sqlx v1.3.5 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/sergi/go-diff v1.2.0 // indirect
|
||||
github.com/skeema/knownhosts v1.2.0 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
|
||||
go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4 // indirect
|
||||
golang.org/x/crypto v0.13.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/net v0.15.0 // indirect
|
||||
golang.org/x/sync v0.3.0 // indirect
|
||||
golang.org/x/sys v0.12.0 // indirect
|
||||
golang.org/x/term v0.12.0 // indirect
|
||||
golang.org/x/tools v0.13.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
lukechampine.com/uint128 v1.2.0 // indirect
|
||||
lure.sh/fakeroot v0.0.0-20231024000108-b130d64a68ee // indirect
|
||||
modernc.org/cc/v3 v3.40.0 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.13 // indirect
|
||||
modernc.org/libc v1.24.1 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.6.0 // indirect
|
||||
modernc.org/opt v0.1.3 // indirect
|
||||
modernc.org/sqlite v1.25.0 // indirect
|
||||
modernc.org/strutil v1.1.3 // indirect
|
||||
modernc.org/token v1.0.1 // indirect
|
||||
mvdan.cc/sh/v3 v3.7.0 // indirect
|
||||
)
|
245
go.sum
Normal file
245
go.sum
Normal file
@ -0,0 +1,245 @@
|
||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
||||
github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ=
|
||||
github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
|
||||
github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink=
|
||||
github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
|
||||
github.com/alecthomas/chroma/v2 v2.9.1 h1:0O3lTQh9FxazJ4BYE/MOi/vDGuHn7B+6Bu902N2UZvU=
|
||||
github.com/alecthomas/chroma/v2 v2.9.1/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw=
|
||||
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
|
||||
github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||
github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
|
||||
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
|
||||
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
|
||||
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
|
||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
|
||||
github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
|
||||
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
|
||||
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
|
||||
github.com/go-git/go-git/v5 v5.9.0 h1:cD9SFA7sHVRdJ7AYck1ZaAa/yeuBvGPxwXDL8cxrObY=
|
||||
github.com/go-git/go-git/v5 v5.9.0/go.mod h1:RKIqga24sWdMGZF+1Ekv9kylsDz6LzdTSI2s/OsZWE0=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gookit/color v1.5.1 h1:Vjg2VEcdHpwq+oY63s/ksHrgJYCTo0bwWvmmYWdE9fQ=
|
||||
github.com/gookit/color v1.5.1/go.mod h1:wZFzea4X8qN6vHOSP2apMb4/+w/orMznEzYsIHPaqKM=
|
||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
||||
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
|
||||
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
|
||||
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
|
||||
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
|
||||
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo=
|
||||
github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
|
||||
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM=
|
||||
github.com/skeema/knownhosts v1.2.0/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/uptrace/bunrouter v1.0.21 h1:HXarvX+N834sXyHpl+I/TuE11m19kLW/qG5u3YpHUag=
|
||||
github.com/uptrace/bunrouter v1.0.21/go.mod h1:TwT7Bc0ztF2Z2q/ZzMuSVkcb/Ig/d3MQeP2cxn3e1hI=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.elara.ws/logger v0.0.0-20230421022458-e80700db2090 h1:RVC8XvWo6Yw4HUshqx4TSzuBDScDghafU6QFRJ4xPZg=
|
||||
go.elara.ws/logger v0.0.0-20230421022458-e80700db2090/go.mod h1:qng49owViqsW5Aey93lwBXONw20oGbJIoLVscB16mPM=
|
||||
go.elara.ws/salix v0.0.0-20231228151958-745b6c0e5f10 h1:Z0XcJexULcFf0ITcvQml/z+c/RW4fpon3nDWuoDcr3Q=
|
||||
go.elara.ws/salix v0.0.0-20231228151958-745b6c0e5f10/go.mod h1:niWia13iw7qDrS1C1mlqv5hxO1sunt8CcOQAB5yVlNU=
|
||||
go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4 h1:Ep54XceQlKhcCHl9awG+wWP4kz4kIP3c3Lzw/Gc/zwY=
|
||||
go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4/go.mod h1:/7PNW7nFnDR5W7UXZVc04gdVLR/wBNgkm33KgIz0OBk=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
|
||||
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
lure.sh/fakeroot v0.0.0-20231024000108-b130d64a68ee h1:kSXIuMid56Q29WEl7EQb5QUtmGqQqAu66EZ2G0OSUfU=
|
||||
lure.sh/fakeroot v0.0.0-20231024000108-b130d64a68ee/go.mod h1:/v0u0AZ+wbzUWhV02KzciOf1KFNh7/7rbkz5Z0b5gDA=
|
||||
lure.sh/lure v0.1.4-0.20231223033536-5dc31f43aa39 h1:9Rw9+Pmmkbjxiq0eREq4RJPn+ggL1jYFpgrgOkgQ9YQ=
|
||||
lure.sh/lure v0.1.4-0.20231223033536-5dc31f43aa39/go.mod h1:h1+OIDD39zDosjThxQ9szoWW9Z1lGcm7M6eeo6v0OJY=
|
||||
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
|
||||
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
|
||||
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
|
||||
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
|
||||
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
|
||||
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
|
||||
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
|
||||
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
|
||||
modernc.org/libc v1.24.1 h1:uvJSeCKL/AgzBo2yYIPPTy82v21KgGnizcGYfBHaNuM=
|
||||
modernc.org/libc v1.24.1/go.mod h1:FmfO1RLrU3MHJfyi9eYYmZBfi/R+tqZ6+hQ3yQQUkak=
|
||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.6.0 h1:i6mzavxrE9a30whzMfwf7XWVODx2r5OYXvU46cirX7o=
|
||||
modernc.org/memory v1.6.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sqlite v1.25.0 h1:AFweiwPNd/b3BoKnBOfFm+Y260guGMF+0UFk0savqeA=
|
||||
modernc.org/sqlite v1.25.0/go.mod h1:FL3pVXie73rg3Rii6V/u5BoHlSoyeZeIgKZEgHARyCU=
|
||||
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
|
||||
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
|
||||
modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY=
|
||||
modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c=
|
||||
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
|
||||
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY=
|
||||
modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE=
|
||||
mvdan.cc/sh/v3 v3.7.0 h1:lSTjdP/1xsddtaKfGg7Myu7DnlHItd3/M2tomOcNNBg=
|
||||
mvdan.cc/sh/v3 v3.7.0/go.mod h1:K2gwkaesF/D7av7Kxl0HbF5kGOd2ArupNTX3X44+8l8=
|
43
main.go
Normal file
43
main.go
Normal file
@ -0,0 +1,43 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/uptrace/bunrouter"
|
||||
"go.elara.ws/logger"
|
||||
"go.elara.ws/logger/log"
|
||||
"lure.sh/lure/pkg/loggerctx"
|
||||
)
|
||||
|
||||
//go:embed static
|
||||
var static embed.FS
|
||||
|
||||
func init() {
|
||||
log.Logger = logger.NewPretty(os.Stderr)
|
||||
}
|
||||
|
||||
func main() {
|
||||
mux := bunrouter.New()
|
||||
|
||||
fileServer := http.FileServer(http.FS(static))
|
||||
mux.Use(cache).GET("/static/*path", bunrouter.HTTPHandler(fileServer))
|
||||
|
||||
ctx := loggerctx.With(context.Background(), log.Logger)
|
||||
|
||||
registerBadge(mux)
|
||||
registerSite(mux)
|
||||
registerAPI(mux)
|
||||
registerWebhook(ctx, mux)
|
||||
|
||||
http.ListenAndServe(":8080", mux)
|
||||
}
|
||||
|
||||
func cache(next bunrouter.HandlerFunc) bunrouter.HandlerFunc {
|
||||
return bunrouter.HandlerFunc(func(w http.ResponseWriter, req bunrouter.Request) error {
|
||||
w.Header().Set("Cache-Control", "max-age=86400, stale-while-revalidate=86400")
|
||||
return next(w, req)
|
||||
})
|
||||
}
|
111
pkg.go
Normal file
111
pkg.go
Normal file
@ -0,0 +1,111 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
"lure.sh/lure/pkg/search"
|
||||
)
|
||||
|
||||
type Package struct {
|
||||
Name string `json:"name"`
|
||||
Repository string `json:"repository"`
|
||||
Version string `json:"version"`
|
||||
Release int `json:"release"`
|
||||
Epoch uint `json:"epoch"`
|
||||
Description string `json:"description"`
|
||||
Homepage string `json:"homepage"`
|
||||
Maintainer string `json:"maintainer"`
|
||||
Architectures []string `json:"architectures"`
|
||||
Licenses []string `json:"licenses"`
|
||||
Provides []string `json:"provides"`
|
||||
Conflicts []string `json:"conflicts"`
|
||||
Replaces []string `json:"replaces"`
|
||||
Depends map[string][]string `json:"depends"`
|
||||
BuildDepends map[string][]string `json:"build_depends"`
|
||||
}
|
||||
|
||||
func (p Package) FullVersion() string {
|
||||
ver := fmt.Sprintf("%s-%d", p.Version, p.Release)
|
||||
if p.Epoch != 0 {
|
||||
ver = fmt.Sprintf("%d:%s", p.Epoch, ver)
|
||||
}
|
||||
return ver
|
||||
}
|
||||
|
||||
func translatePkgs(langs []string, pkgs []search.Package) []Package {
|
||||
out := make([]Package, len(pkgs))
|
||||
for i, pkg := range pkgs {
|
||||
out[i] = translatePkg(langs, pkg)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func translatePkg(langs []string, pkg search.Package) Package {
|
||||
return Package{
|
||||
Name: pkg.Name,
|
||||
Repository: pkg.Repository,
|
||||
Version: pkg.Version,
|
||||
Release: pkg.Release,
|
||||
Epoch: pkg.Epoch,
|
||||
Description: performTranslation(langs, pkg.Description),
|
||||
Homepage: performTranslation(langs, pkg.Homepage),
|
||||
Maintainer: performTranslation(langs, pkg.Maintainer),
|
||||
Architectures: pkg.Architectures,
|
||||
Licenses: pkg.Licenses,
|
||||
Provides: pkg.Provides,
|
||||
Conflicts: pkg.Conflicts,
|
||||
Depends: pkg.Depends,
|
||||
BuildDepends: pkg.BuildDepends,
|
||||
}
|
||||
}
|
||||
|
||||
func getLanguages(req *http.Request) []string {
|
||||
al := req.Header.Get("Accept-Language")
|
||||
lang := req.URL.Query().Get("lang")
|
||||
|
||||
if al == "" && lang == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
tags, _, _ := language.ParseAcceptLanguage(al)
|
||||
|
||||
if lang != "" {
|
||||
langTag, err := language.Parse(lang)
|
||||
if err == nil {
|
||||
tags = append(tags, langTag)
|
||||
}
|
||||
}
|
||||
|
||||
return getLangBases(tags)
|
||||
}
|
||||
|
||||
func getLangBases(langs []language.Tag) []string {
|
||||
out := make([]string, len(langs)+1)
|
||||
for i, lang := range langs {
|
||||
base, _ := lang.Base()
|
||||
out[i] = base.String()
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func performTranslation(langs []string, v map[string]string) string {
|
||||
if len(langs) == 0 {
|
||||
val, ok := v[""]
|
||||
if !ok {
|
||||
return "<unknown>"
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
for _, name := range langs {
|
||||
val, ok := v[name]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
return "<unknown>"
|
||||
}
|
193
site.go
Normal file
193
site.go
Normal file
@ -0,0 +1,193 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
"github.com/alecthomas/chroma/v2/formatters/html"
|
||||
"github.com/alecthomas/chroma/v2/lexers"
|
||||
"github.com/alecthomas/chroma/v2/styles"
|
||||
"github.com/uptrace/bunrouter"
|
||||
"go.elara.ws/logger/log"
|
||||
"go.elara.ws/salix"
|
||||
"lure.sh/lure/pkg/loggerctx"
|
||||
"lure.sh/lure/pkg/search"
|
||||
)
|
||||
|
||||
//go:embed tmpls
|
||||
var tmpls embed.FS
|
||||
|
||||
func registerSite(mux *bunrouter.Router) {
|
||||
tmplFS, err := fs.Sub(tmpls, "tmpls")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ns := salix.New().
|
||||
WithEscapeHTML(true).
|
||||
WithWriteOnSuccess(true).
|
||||
WithVarMap(map[string]any{
|
||||
"now": time.Now,
|
||||
})
|
||||
err = ns.ParseFSGlob(tmplFS, "*.html")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
g := mux.WithMiddleware(siteErrorHandler(ns))
|
||||
|
||||
g.GET("/*path", func(w http.ResponseWriter, req bunrouter.Request) error {
|
||||
if req.Param("path") == "" {
|
||||
return ns.ExecuteTemplate(w, "home.html", nil)
|
||||
}
|
||||
return HTTPError{404, "page not found: " + req.URL.Path}
|
||||
})
|
||||
|
||||
g.GET("/about", func(w http.ResponseWriter, req bunrouter.Request) error {
|
||||
return ns.ExecuteTemplate(w, "about.html", nil)
|
||||
})
|
||||
|
||||
g.GET("/icons", func(w http.ResponseWriter, req bunrouter.Request) error {
|
||||
return ns.ExecuteTemplate(w, "icons.html", nil)
|
||||
})
|
||||
|
||||
g.GET("/pkgs", func(w http.ResponseWriter, req bunrouter.Request) error {
|
||||
ctx := loggerctx.With(req.Context(), log.Logger)
|
||||
pkgs, err := search.Search(ctx, searchOptions(req.Request))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ns.ExecuteTemplate(w, "pkgs.html", map[string]any{
|
||||
"pkgs": translatePkgs(getLanguages(req.Request), pkgs),
|
||||
"query": req.URL.Query(),
|
||||
})
|
||||
})
|
||||
|
||||
g.GET("/pkg/:repo/:pkg", func(w http.ResponseWriter, req bunrouter.Request) error {
|
||||
ctx := loggerctx.With(req.Context(), log.Logger)
|
||||
pkg, err := search.GetPkg(ctx, req.Param("repo"), req.Param("pkg"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ns.ExecuteTemplate(w, "pkg.html", map[string]any{
|
||||
"url": getURL(req.Request),
|
||||
"pkg": translatePkg(getLanguages(req.Request), pkg),
|
||||
})
|
||||
})
|
||||
|
||||
g.GET("/pkg/:repo/:pkg/script", func(w http.ResponseWriter, req bunrouter.Request) error {
|
||||
repoName := req.Param("repo")
|
||||
pkgName := req.Param("pkg")
|
||||
|
||||
ctx := loggerctx.With(req.Context(), log.Logger)
|
||||
script, err := search.GetScript(ctx, repoName, pkgName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer script.Close()
|
||||
|
||||
data, err := highlight(script)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ns.ExecuteTemplate(w, "script.html", map[string]any{
|
||||
"repoName": repoName,
|
||||
"pkgName": pkgName,
|
||||
"script": data,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// searchOptions gets a search.Options struct from the request's
|
||||
// query parameters.
|
||||
func searchOptions(r *http.Request) search.Options {
|
||||
sort := search.SortBy(search.SortByNone)
|
||||
switch r.URL.Query().Get("sort") {
|
||||
case "name":
|
||||
sort = search.SortByName
|
||||
case "version":
|
||||
sort = search.SortByVersion
|
||||
case "repo":
|
||||
sort = search.SortByRepo
|
||||
}
|
||||
|
||||
filter := search.FilterNone
|
||||
switch r.URL.Query().Get("filter") {
|
||||
case "inrepo":
|
||||
filter = search.FilterInRepo
|
||||
case "arch":
|
||||
filter = search.FilterSupportsArch
|
||||
}
|
||||
|
||||
return search.Options{
|
||||
Filter: filter,
|
||||
FilterValue: r.URL.Query().Get("fv"),
|
||||
SortBy: sort,
|
||||
Query: r.URL.Query().Get("q"),
|
||||
}
|
||||
}
|
||||
|
||||
// highlight returns an HTML string with syntax highlighting
|
||||
// for the contents of the given reader.
|
||||
func highlight(r io.Reader) (salix.HTML, error) {
|
||||
data, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
lexer := lexers.Get("bash")
|
||||
if lexer == nil {
|
||||
lexer = lexers.Fallback
|
||||
}
|
||||
lexer = chroma.Coalesce(lexer)
|
||||
|
||||
style := styles.Get("modus-vivendi")
|
||||
formatter := html.New()
|
||||
iterator, err := lexer.Tokenise(nil, string(data))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
out := &strings.Builder{}
|
||||
err = formatter.Format(out, style, iterator)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return salix.HTML(out.String()), nil
|
||||
}
|
||||
|
||||
// getURL attempts to get the current URL of the request
|
||||
func getURL(r *http.Request) string {
|
||||
u := *r.URL
|
||||
|
||||
xfp := r.Header.Get("X-Forwarded-Proto")
|
||||
if xfp == "" {
|
||||
if r.TLS == nil {
|
||||
u.Scheme = "http"
|
||||
} else {
|
||||
u.Scheme = "https"
|
||||
}
|
||||
} else {
|
||||
u.Scheme = xfp
|
||||
}
|
||||
|
||||
xfh := r.Header.Get("X-Forwarded-Host")
|
||||
if xfh == "" {
|
||||
if r.URL.Host == "" {
|
||||
u.Host = r.Host
|
||||
}
|
||||
} else {
|
||||
u.Host = xfh
|
||||
}
|
||||
|
||||
return u.String()
|
||||
}
|
108
static/css/lure.css
Normal file
108
static/css/lure.css
Normal file
@ -0,0 +1,108 @@
|
||||
:root {
|
||||
--primary: #0060A8;
|
||||
--primary-hover: #026EBF;
|
||||
--secondary: #0EAAAA;
|
||||
--secondary-hover: #13b4b4;
|
||||
--font-size: 16px;
|
||||
}
|
||||
|
||||
.centered {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.justify-space-between {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
a.close {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
nav:not([aria-label="breadcrumb"]) {
|
||||
/* Workaround for horizontal overflow due to justify-content */
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* Add some spacing between the navbar items and the edge of the page */
|
||||
nav:not([aria-label="breadcrumb"]) ul {
|
||||
padding-left: 2rem;
|
||||
padding-right: 2rem;
|
||||
}
|
||||
|
||||
nav:not([aria-label="breadcrumb"]) a {
|
||||
color: #dddedf;
|
||||
}
|
||||
|
||||
nav:not([aria-label="breadcrumb"]) a.active {
|
||||
background-color: var(--primary);
|
||||
}
|
||||
|
||||
nav:not([aria-label="breadcrumb"]) a:hover:not(.active) {
|
||||
color: var(--primary-hover);
|
||||
}
|
||||
|
||||
/* Keep the footer at the bottom */
|
||||
body > main {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* Make the footer smaller */
|
||||
body > footer {
|
||||
--block-spacing-vertical: 2rem;
|
||||
}
|
||||
|
||||
/* Make card header and footer smaller */
|
||||
article > header, article > footer {
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
/* Make the bottom margin on card headers smaller */
|
||||
article > header {
|
||||
margin-bottom: calc(var(--block-spacing-vertical) / 2);
|
||||
}
|
||||
|
||||
/* Make the top margin on card footers smaller */
|
||||
article > footer {
|
||||
margin-top: calc(var(--block-spacing-vertical) / 2);
|
||||
}
|
||||
|
||||
/* Fix the icon height to 100px to make cards uniform */
|
||||
article > img.icon {
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
th {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
td {
|
||||
word-break: break-word;
|
||||
cursor: text;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
pre code {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.marginless {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
[role="link"], a {
|
||||
--color: var(--secondary);
|
||||
}
|
5
static/css/pico.min.css
vendored
Normal file
5
static/css/pico.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
static/css/pico.min.css.map
Normal file
1
static/css/pico.min.css.map
Normal file
File diff suppressed because one or more lines are too long
1
static/icons/badge-logo.base64
Normal file
1
static/icons/badge-logo.base64
Normal file
@ -0,0 +1 @@
|
||||
data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACNklEQVQ4jX3T32vXdRTH8cf5fL+zITLsJrroh3Rjd0VpfDPaXLMpDIbNgoTIiG6kLnPUP1AMU7qqC1G86Yfg1Km52Tf7zso5tnDRRVBBjrEugm7Km32/2+f99mIlrm297s7hvJ6cwzmHuzXb2ObX8bqfrm6+kxub6HTh+qjhyU3WULEiChtRU8ZlN77d/E/uIVmvFKM+mVoFiVXIn6/WNGNUM36zEIctxjm3Ki9rxnHNmLAYPV7f3lq7A9jaNSnplj2Az5RxVlv+XHYQU0qvrj/CXGOnm+M83vmDFM/IMSvbo/SFtnxS0hL6wPszVe/N3AX44+tBoSF5zcw37Sp5QNaDX4QJi/GKMAiO3tigMKx0ZBnw11eDijwknFPGGRUjGFLJhyRPKuOmcAwJp1TyKZH7hd8Lrfo7CkPCackB8rDQi3nhCDpFXjYXDijjJWGvwiH3FEereFA4rWK/7GNhF+YVupWxQ+E8xhT5uKXifmFA4V1vP/EBVJX5TUUU7u1J5hr3LZtzl6V4UeQhEZdU7dNfW3BiuhB53lvbzq99B3ONEc14Tiued6vyp1bULcQWzRizGC8YqC38d+sr15gdwwYpRrXlDkkXfpTtUsaIT6fa/x/wcPdFKfZjk+Syttyhd8djUvTJOpVGnJhuXx8AW7vOSHbLqpIrrlx71N7al5J9sp1KF3z0/R3f6l/4V5PfPaUVdc34Wyue1ff0rJPT/ZY84o3tH67rW6HxazX1ibpL1zvWK7kNwWfQQO79fcgAAAAASUVORK5CYII=
|
BIN
static/icons/favicon.ico
Normal file
BIN
static/icons/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 644 B |
65
static/icons/lure-no-text.svg
Normal file
65
static/icons/lure-no-text.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 17 KiB |
72
static/icons/lure-text-white.svg
Normal file
72
static/icons/lure-text-white.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 20 KiB |
104
static/icons/lure-text.svg
Normal file
104
static/icons/lure-text.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 21 KiB |
77
tmpls/about.html
Normal file
77
tmpls/about.html
Normal file
@ -0,0 +1,77 @@
|
||||
#macro("content"):
|
||||
<nav aria-label="breadcrumb">
|
||||
<ul>
|
||||
<li><a href="/">Home</a></li>
|
||||
<li>About</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<h1>About LURE</h1>
|
||||
|
||||
<section class="container">
|
||||
<hgroup><h2>Why does LURE exist?</h2></hgroup>
|
||||
<p>
|
||||
LURE was created because packaging software for multiple Linux distros can be difficult and
|
||||
error-prone, and installing those packages can be a nightmare for users unless they're available
|
||||
in their distro's official repositories.
|
||||
</p>
|
||||
<p>
|
||||
Take Discord for example. It only provides a deb file and a tar.gz file, and it's not available
|
||||
in most official repositories. That means users of RPM distros have to manually install discord
|
||||
using the tarball or rely on community-maintained repositories that don't always have
|
||||
up-to-date versions of Discord. That's also made worse by the fact that Discord refuses to run
|
||||
if there's a newer version available.
|
||||
</p>
|
||||
<p>
|
||||
LURE fixes that by always providing the most up to date version of Discord, which means all that
|
||||
RPM users have to do is run <code>lure in discord</code> and LURE will get the tarball and
|
||||
automatically build an RPM package out of it for them. When there's a newer version of Discord
|
||||
available, users can just run <code>lure up</code> and LURE will automatically download the updated
|
||||
version of Discord and install it. That also helps users of deb distros because it means they don't
|
||||
have to manually download packages for software like Discord.
|
||||
</p>
|
||||
</section>
|
||||
<section class="container">
|
||||
<hgroup><h2>How does LURE work?</h2></hgroup>
|
||||
<p>
|
||||
Similar to Arch Linux's AUR, LURE has a repository of shell scripts that tell it how to build a
|
||||
package. When you run a LURE command, it updates its repository and interprets the shell script
|
||||
for the package you want to install using its built-in bash implementation. Then, it uses the
|
||||
code inside the script to build a file structure to be included in the final package. It detects
|
||||
which distro you're running, resolves dependencies, detects which package manager you have and
|
||||
which package format it uses, builds metadata for the package from the information in the shell
|
||||
script, builds the final package, and then runs the install command for your package manager to
|
||||
install it.
|
||||
</p>
|
||||
</section>
|
||||
<section class="container">
|
||||
<hgroup><h2>How does LURE keep its packages up to date?</h2></hgroup>
|
||||
<p>
|
||||
LURE can automatically update its packages using a bot called
|
||||
<a href="https://gitea.elara.ws/lure/lure-updater">lure-updater</a>. It accepts plugins that
|
||||
detect when software is updated upstream and updates the LURE package accordingly. The plugins
|
||||
that are currently running in my instance of the bot can be found in the
|
||||
<a href="https://gitea.elara.ws/lure/updater-plugins">updater-plugins</a> repo. The
|
||||
<code>discord-bin</code> package, for example, checks Discord's API every hour to see if they've
|
||||
released an updated version, and if they have, it pushes an update to LURE's repo.
|
||||
</p>
|
||||
</section>
|
||||
<section class="container">
|
||||
<hgroup><h2>How do I add my own package to LURE?</h2></hgroup>
|
||||
<p>
|
||||
LURE provides <a href="https://github.com/lure-sh/lure/blob/master/docs/packages">comprehensive documentation</a>
|
||||
for packagers. If you need help with anything, feel free to ask on LURE's <a href="https://reddit.com/r/linux_user_repository">subreddit</a>.
|
||||
If you find a bug or would like a feature to be added, please open an issue on LURE's git repo.
|
||||
</p>
|
||||
</section>
|
||||
<section class="container">
|
||||
<hgroup><h2>Can I use and modify LURE's icons?</h2></hgroup>
|
||||
<p>
|
||||
LURE's icons are available on the <a href="/icons">icons page</a> of this site. They're licensed
|
||||
under CC-BY-NC-SA 4.0, which means you're free to share, modify, and use the icons for non-commercial
|
||||
purposes as long as you give appropriate credit and indicate any changes made to the original icons.
|
||||
</p>
|
||||
</section>
|
||||
#!macro
|
||||
|
||||
#include("base.html", title = "About", desc = "About the Linux User Repository")
|
35
tmpls/base.html
Normal file
35
tmpls/base.html
Normal file
@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-theme="dark">
|
||||
<head>
|
||||
<title>#(title) | LURE Web</title>
|
||||
<meta name="description" content='#(desc | title + " on LURE Web")' />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.8" />
|
||||
<link rel="stylesheet" href="/static/css/pico.min.css">
|
||||
<link rel="stylesheet" href="/static/css/lure.css">
|
||||
<link rel="icon" href="/static/icons/favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="/"><img src="/static/icons/lure-text-white.svg" alt="LURE Logo" width="85"></a></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="/" class='#(title == "Home" ? "active" : "")'>Home</a></li>
|
||||
<li><a href="/pkgs" class='#(title == "Package Search" ? "active" : "")'>Packages</a></li>
|
||||
<li><a href="/about" class='#(title == "About" ? "active" : "")'>About</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<main class="container">
|
||||
#macro("content")
|
||||
</main>
|
||||
|
||||
|
||||
<footer class="container">
|
||||
<div class="headings marginless">
|
||||
<h2>Copyright © #(now().Year()) LURE Web Contributors</h2>
|
||||
</div>
|
||||
<a href="https://github.com/lure-sh/lure-web" target="_blank">Source Code</a>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
25
tmpls/error.html
Normal file
25
tmpls/error.html
Normal file
@ -0,0 +1,25 @@
|
||||
#macro("content"):
|
||||
<nav aria-label="breadcrumb">
|
||||
<ul>
|
||||
<li><a href="/">Home</a></li>
|
||||
<li>Error</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<h1>Error</h1>
|
||||
<br><br>
|
||||
|
||||
|
||||
<section class="container centered">
|
||||
<div>
|
||||
<hgroup>
|
||||
<h1>Something Went Wrong!</h1>
|
||||
<h2>Error #(error.StatusCode): #(error.StatusText())</h2>
|
||||
</hgroup>
|
||||
<pre><code>#(error.Msg)</code></pre>
|
||||
<a href="/" role="button">Go Back Home</a>
|
||||
</div>
|
||||
</section>
|
||||
#!macro
|
||||
|
||||
#include("base.html", title = sprintf("Error %d", error.StatusCode), desc = sprintf("Error %d: %s", error.StatusCode, error.StatusText()))
|
32
tmpls/home.html
Normal file
32
tmpls/home.html
Normal file
@ -0,0 +1,32 @@
|
||||
#macro("content"):
|
||||
<section class="container centered">
|
||||
<img src="/static/icons/lure-no-text.svg" alt="LURE logo without text" width="200">
|
||||
<hgroup>
|
||||
<h1>LURE</h1>
|
||||
<h2>The community repository missing from your Linux distro</h2>
|
||||
</hgroup>
|
||||
</section>
|
||||
|
||||
<section class="container">
|
||||
<hgroup>
|
||||
<h1>What does LURE do?</h1>
|
||||
<h2>LURE allows you to:</h2>
|
||||
</hgroup>
|
||||
<ul>
|
||||
<li>Access a wide range of software beyond what's available in official repositories</li>
|
||||
<li>Get new versions of software as they come out, before official repositories ship them</li>
|
||||
<li>Install unofficial software without having to deal with a separate package manager</li>
|
||||
<li>Release software for Linux without having to package it for different distributions</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section class="container">
|
||||
<hgroup>
|
||||
<h1>Install LURE</h1>
|
||||
<h2>Paste this into your Linux terminal and the install script will set everything up for you</h2>
|
||||
</hgroup>
|
||||
<pre><code>curl -fsSL lure.sh/install | bash</code></pre>
|
||||
<p>LURE is also available on the AUR as <code>linux-user-repository-bin</code> and distro packages are provided at the <a href="https://gitea.elara.ws/lure/lure/releases/latest">latest Gitea release</a></p>
|
||||
</section>
|
||||
#!macro
|
||||
|
||||
#include("base.html", title = "Home", desc = "The community repository missing from your Linux distro")
|
39
tmpls/icons.html
Normal file
39
tmpls/icons.html
Normal file
@ -0,0 +1,39 @@
|
||||
#macro("content"):
|
||||
<nav aria-label="breadcrumb">
|
||||
<ul>
|
||||
<li><a href="/">Home</a></li>
|
||||
<li>Icons</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<h1>Icons</h1>
|
||||
|
||||
<section class="container">
|
||||
<div class="grid">
|
||||
<div>
|
||||
<article class="centered">
|
||||
<header><strong>Without Text</strong></header>
|
||||
<img class="icon" src="/static/icons/lure-no-text.svg" alt="LURE icon without text">
|
||||
<footer><a href="/static/icons/lure-no-text.svg" download>Download</a></footer>
|
||||
</article>
|
||||
</div>
|
||||
<div>
|
||||
<article class="centered">
|
||||
<header><strong>With Text</strong></header>
|
||||
<img class="icon" src="/static/icons/lure-text.svg" alt="LURE icon with text">
|
||||
<footer><a href="/static/icons/lure-text.svg" download>Download</a></footer>
|
||||
</article>
|
||||
</div>
|
||||
<div>
|
||||
<article class="centered">
|
||||
<header><strong>White With Text</strong></header>
|
||||
<img class="icon" src="/static/icons/lure-text-white.svg" alt="LURE icon with text">
|
||||
<footer><a href="/static/icons/lure-text-white.svg" download>Download</a></footer>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
<strong>Note: The icons on this page are licensed under CC-BY-NC-SA 4.0 unless otherwise specified</strong>
|
||||
</section>
|
||||
#!macro
|
||||
|
||||
#include("base.html", title = "Icons", desc = "Linux User Repository icons and logos")
|
142
tmpls/pkg.html
Normal file
142
tmpls/pkg.html
Normal file
@ -0,0 +1,142 @@
|
||||
#macro("content"):
|
||||
<nav aria-label="breadcrumb">
|
||||
<ul>
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/pkgs">Packages</a></li>
|
||||
<li>#(pkg.Name)</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<hgroup>
|
||||
<h1>#(pkg.Name)</h1>
|
||||
<h2>#(pkg.FullVersion())</h2>
|
||||
</hgroup>
|
||||
<a href="/pkg/#(pkg.Repository)/#(pkg.Name)/script" role="button">View script</a>
|
||||
<a href="javascript:openModal()" role="button" class="secondary">View badge</a>
|
||||
<br>
|
||||
<article>
|
||||
<h3 >Package Information</h3>
|
||||
<table>
|
||||
<tbody>
|
||||
#if(pkg.Description != ""):
|
||||
<tr>
|
||||
<th>Description:</th>
|
||||
<td>#(pkg.Description)</td>
|
||||
</tr>
|
||||
#!if
|
||||
#if(pkg.Homepage != ""):
|
||||
<tr>
|
||||
<th>Homepage:</th>
|
||||
<td><a href="#(pkg.Homepage)" target="_blank">#(pkg.Homepage)</a></td>
|
||||
</tr>
|
||||
#!if
|
||||
#if(pkg.Maintainer != ""):
|
||||
<tr>
|
||||
<th>Maintainer:</th>
|
||||
<td>#(pkg.Maintainer)</td>
|
||||
</tr>
|
||||
#!if
|
||||
#if(len(pkg.Licenses) != 0):
|
||||
<tr>
|
||||
<th>Licenses:</th>
|
||||
<td>
|
||||
#for(i, license in pkg.Licenses):
|
||||
#if(hasPrefix(license, "custom")):
|
||||
#(license)#if(i != len(pkg.Licenses) - 1):, #!if
|
||||
#else:
|
||||
<a href="https://spdx.org/licenses/#(license).html" target="_blank">#(license)</a>#if(i != len(pkg.Licenses) - 1):, #!if
|
||||
#!if
|
||||
#!for
|
||||
</td>
|
||||
</tr>
|
||||
#!if
|
||||
#if(len(pkg.Architectures) != 0):
|
||||
<tr>
|
||||
<th>Architectures:</th>
|
||||
<td>#(join(pkg.Architectures, ", "))</td>
|
||||
</tr>
|
||||
#!if
|
||||
#if(len(pkg.Conflicts) != 0):
|
||||
<tr>
|
||||
<th>Conflicts:</th>
|
||||
<td>#(join(pkg.Conflicts, ", "))</td>
|
||||
</tr>
|
||||
#!if
|
||||
#if(len(pkg.Provides) != 0):
|
||||
<tr>
|
||||
<th>Provides:</th>
|
||||
<td>#(join(pkg.Provides, ", "))</td>
|
||||
</tr>
|
||||
#!if
|
||||
<tr>
|
||||
<th>Repository:</th>
|
||||
<td>#(pkg.Repository)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</article>
|
||||
|
||||
#if(len(pkg.Depends) > 0):
|
||||
<article>
|
||||
<h3 >Runtime Dependencies</h3>
|
||||
<table>
|
||||
<tbody>
|
||||
#for(override, pkgList in pkg.Depends):
|
||||
<tr>
|
||||
<th>#(override == "" ? "default" : override):</th>
|
||||
<td>#(join(pkgList, ", "))</td>
|
||||
</tr>
|
||||
#!for
|
||||
</tbody>
|
||||
</table>
|
||||
</article>
|
||||
#!if
|
||||
|
||||
#if(len(pkg.BuildDepends) > 0):
|
||||
<article>
|
||||
<h3 >Build Dependencies</h3>
|
||||
<table>
|
||||
<tbody>
|
||||
#for(override, pkgList in pkg.BuildDepends):
|
||||
<tr>
|
||||
<th>#(override == "" ? "default" : override):</th>
|
||||
<td>#(join(pkgList, ", "))</td>
|
||||
</tr>
|
||||
#!for
|
||||
</tbody>
|
||||
</table>
|
||||
</article>
|
||||
#!if
|
||||
|
||||
<dialog id="badgeModal">
|
||||
<article style="min-width: 60%">
|
||||
<header>
|
||||
<strong>Badge</strong>
|
||||
<a aria-label="Close" class="close" href="javascript:closeModal()"></a>
|
||||
</header>
|
||||
<p class="centered">
|
||||
<img src="#(url)/badge.svg" alt="LURE badge for #(pkg.Name)">
|
||||
<hr>
|
||||
</p>
|
||||
<label for="badgeMarkdown">Markdown</label>
|
||||
<input id="badgeMarkdown" value="[![LURE badge for #(pkg.Name)](#(url)/badge.svg)](#(url))" readonly />
|
||||
<label for="badgeHTML">HTML</label>
|
||||
<input id="badgeHTML" value='<a href="#(url)"><img src="#(url)/badge.svg" alt="LURE badge for #(pkg.Name)">' readonly />
|
||||
</article>
|
||||
</dialog>
|
||||
|
||||
<script>
|
||||
function openModal() {
|
||||
let modal = document.getElementById("badgeModal");
|
||||
document.documentElement.classList.add('modal-is-open');
|
||||
modal.setAttribute('open', true);
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
let modal = document.getElementById("badgeModal");
|
||||
document.documentElement.classList.remove('modal-is-open');
|
||||
modal.removeAttribute('open');
|
||||
}
|
||||
</script>
|
||||
#!macro
|
||||
|
||||
#include("base.html", title = pkg.Name + " Package", desc = sprintf("%s %s - %s", pkg.Name, pkg.FullVersion(), pkg.Description))
|
62
tmpls/pkgs.html
Normal file
62
tmpls/pkgs.html
Normal file
@ -0,0 +1,62 @@
|
||||
#macro("content"):
|
||||
<nav aria-label="breadcrumb">
|
||||
<ul>
|
||||
<li><a href="/">Home</a></li>
|
||||
<li>Packages</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<h1>Package Search</h1>
|
||||
|
||||
<form action="/pkgs">
|
||||
<input type="text" id="query" name="q" value='#(query.Get("q"))' placeholder="Query"/>
|
||||
<div class="grid">
|
||||
<div>
|
||||
<select name="sort">
|
||||
<option value="">Unsorted</option>
|
||||
<option value="name" #(query.Get("sort") == "name" ? "selected" : "")>Sort by Name</option>
|
||||
<option value="version" #(query.Get("sort") == "version" ? "selected" : "")>Sort by Version</option>
|
||||
<option value="repo" #(query.Get("sort") == "repo" ? "selected" : "")>Sort by Repository</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<select name="filter" onchange="onFilterChange(event)">
|
||||
<option value="">No Filter</option>
|
||||
<option value="inrepo" #(query.Get("filter") == "inrepo" ? "selected" : "")>In Repo</option>
|
||||
<option value="arch" #(query.Get("filter") == "arch" ? "selected" : "")>Supports Architecture</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<input class='#(query.Get("filter") == "" ? "hidden" : "")' type="text" id="filterValue" name="fv" value='#(query.Get("fv"))' placeholder="Filter Value"/>
|
||||
<button type="submit">Search</button>
|
||||
</form>
|
||||
<hr>
|
||||
|
||||
#for(pkg in pkgs):
|
||||
<article>
|
||||
<header>
|
||||
<div class="justify-space-between">
|
||||
<span class="left"><strong>#(pkg.Repository) / #(pkg.Name)</strong></span>
|
||||
<span class="right">#(pkg.Version)</span>
|
||||
</div>
|
||||
</header>
|
||||
<p>#(pkg.Description)</p>
|
||||
<footer class="centered">
|
||||
<a href="/pkg/#(pkg.Repository)/#(pkg.Name)">More Info →</a>
|
||||
</footer>
|
||||
</article>
|
||||
#!for
|
||||
|
||||
<script>
|
||||
function onFilterChange(e) {
|
||||
let filterValue = document.getElementById('filterValue');
|
||||
if (e.srcElement.value == '') {
|
||||
filterValue.classList.add('hidden');
|
||||
} else {
|
||||
filterValue.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
#!macro
|
||||
|
||||
#include("base.html", title = "Package Search", desc = "Search for LURE packages")
|
19
tmpls/script.html
Normal file
19
tmpls/script.html
Normal file
@ -0,0 +1,19 @@
|
||||
#macro("content"):
|
||||
<nav aria-label="breadcrumb">
|
||||
<ul>
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/pkgs">Packages</a></li>
|
||||
<li><a href="/pkg/#(repoName)/#(pkgName)">#(pkgName)</a></li>
|
||||
<li>Script</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<hgroup>
|
||||
<h1>Build Script</h1>
|
||||
<h2>#(repoName) / #(pkgName)</h2>
|
||||
</hgroup>
|
||||
|
||||
#(script)
|
||||
#!macro
|
||||
|
||||
#include("base.html", title = pkgName + " Build Script", desc = sprintf("Build script for the %s package", pkgName))
|
79
webhook.go
Normal file
79
webhook.go
Normal file
@ -0,0 +1,79 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/uptrace/bunrouter"
|
||||
"go.elara.ws/logger/log"
|
||||
"lure.sh/lure/pkg/repos"
|
||||
)
|
||||
|
||||
func registerWebhook(ctx context.Context, mux *bunrouter.Router) {
|
||||
g := mux.WithMiddleware(apiErrorHandler)
|
||||
|
||||
g.POST("/webhook", func(w http.ResponseWriter, req bunrouter.Request) error {
|
||||
if req.Header.Get("X-GitHub-Event") != "push" {
|
||||
return HTTPError{http.StatusBadRequest, "only push events are accepted by this bot"}
|
||||
}
|
||||
|
||||
err := verifySecure(req.Request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go pullRepos(ctx)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func verifySecure(req *http.Request) error {
|
||||
sigStr := req.Header.Get("X-Hub-Signature-256")
|
||||
sig, err := hex.DecodeString(strings.TrimPrefix(sigStr, "sha256="))
|
||||
if err != nil {
|
||||
return HTTPError{http.StatusBadRequest, "invalid hmac value"}
|
||||
}
|
||||
|
||||
secretStr, ok := os.LookupEnv("LURE_WEB_GITHUB_SECRET")
|
||||
if !ok {
|
||||
return HTTPError{http.StatusInternalServerError, "LURE_WEB_GITHUB_SECRET must be set to the secret used for setting up the github webhook"}
|
||||
}
|
||||
secret := []byte(secretStr)
|
||||
|
||||
h := hmac.New(sha256.New, secret)
|
||||
_, err = io.Copy(h, req.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !hmac.Equal(h.Sum(nil), sig) {
|
||||
log.Warn("Insecure webhook request").
|
||||
Str("from", req.RemoteAddr).
|
||||
Bytes("sig", sig).
|
||||
Bytes("hmac", h.Sum(nil)).
|
||||
Send()
|
||||
|
||||
return HTTPError{http.StatusUnauthorized, "insecure webhook request"}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var pullMtx sync.Mutex
|
||||
|
||||
func pullRepos(ctx context.Context) {
|
||||
pullMtx.Lock()
|
||||
defer pullMtx.Unlock()
|
||||
|
||||
err := repos.Pull(ctx, nil)
|
||||
if err != nil {
|
||||
log.Warn("Error while pulling repositories").Err(err).Send()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user