mirror of
https://github.com/BluemediaGER/ScanOS.git
synced 2024-11-21 08:15:29 +01:00
Initial commit
This commit is contained in:
commit
b9d5c06956
674
LICENSE
Normal file
674
LICENSE
Normal file
|
@ -0,0 +1,674 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 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 General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<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 General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
69
README.md
Normal file
69
README.md
Normal file
|
@ -0,0 +1,69 @@
|
|||
|
||||
<h1 align="center">
|
||||
<br>
|
||||
<img src="https://raw.githubusercontent.com/BluemediaGER/ScanOS/main/docs/img/logo.png" alt="ScanOS" width="200">
|
||||
<br>
|
||||
ScanOS
|
||||
<br>
|
||||
</h1>
|
||||
|
||||
<h4 align="center">A modern operating system for enterprise-grade network image scanners.</h4>
|
||||
|
||||
<p align="center">
|
||||
<a href="#key-features">Key Features</a> •
|
||||
<a href="#supported-devices">Supported Devices</a> •
|
||||
<a href="#motivation">Motivation</a> •
|
||||
<a href="#how-to-use">How To Use</a> •
|
||||
<a href="#credits">Credits</a>
|
||||
</p>
|
||||
|
||||
## Key Features
|
||||
|
||||
- [x] Modern UI design on top of a modern operating system (Debian 12)
|
||||
- [x] Implemented fully using open-source components
|
||||
- [ ] Scan to mail
|
||||
- [ ] Scan to folder (SMB / FTP)
|
||||
- [ ] Scan to Paperless-ngx
|
||||
- [ ] Editing of document properties right on the scanner (document type, correspondent, tags, etc.)
|
||||
- [ ] OTA updates using RAUC
|
||||
- [ ] Encrypted temporary storage for scanned files
|
||||
|
||||
## Supported devices
|
||||
|
||||
These devices are currently supported by ScanOS:
|
||||
* Fujitsu N7100
|
||||
|
||||
## Motivation
|
||||
|
||||
I've recently bought a Fujitsu N7100 image scanner for cheap on eBay. The N7100 is basically a USB scanner combined with an Intel Atom x86 board, a SATA SSD, and a touchscreen. By default, it runs Windows 8 Embedded Standard and a lot of proprietary software. Windows 8 Embedded is EOL since April 2023 and receives no further updates. Additionally, writing custom extensions for the scanner's software to use its full potential is hard:
|
||||
- You need the extension SDK, which is not easily accessible. EOL of the device made it even harder to acquire.
|
||||
- Extensions are written in C# using .NET 4.5 and WPF
|
||||
- The development process is quite ugly
|
||||
|
||||
So my idea was to try to get Linux running on this thing. Surprisingly, that task was really easy, as it's fairly standard hardware. Even the hardware scan button is just a USB keyboard with exactly one key: an enter key. The built-in scanner worked out of the box with SANE, so I started the development of a customized OS for use on the scanner.
|
||||
|
||||
## How To Use
|
||||
|
||||
ScanOS is currently in a very early development stage. There is no stable version or standardized development environment yet. Check back later for further usage instructions.
|
||||
|
||||
## Credits
|
||||
|
||||
ScanOS is built on top of many other great projects. Some of them are:
|
||||
|
||||
- [Debian 12 (Bookworm)](https://www.debian.org/)
|
||||
- [Nginx](https://nginx.org/en/)
|
||||
- [Sway](https://swaywm.org/)
|
||||
- [Chromium](https://www.chromium.org/Home/)
|
||||
- [debos - Debian OS images builder](https://github.com/go-debos/debos)
|
||||
- [RAUC](https://rauc.io/)
|
||||
- [Vue.js 3](https://vuejs.org/)
|
||||
- [Tailwind CSS](https://tailwindcss.com/)
|
||||
- [Libinsane](https://gitlab.gnome.org/World/OpenPaperwork/libinsane/-/tree/master)
|
||||
- [FastAPI](https://fastapi.tiangolo.com/)
|
||||
|
||||
---
|
||||
|
||||
> [bluemedia.dev](https://bluemedia.dev) ·
|
||||
> GitHub [@BluemediaGER](https://github.com/BluemediaGER) ·
|
||||
> Mastodon [@Bluemedia@chaos.social](https://chaos.social/@Bluemedia)
|
||||
|
2
backend/.gitignore
vendored
Normal file
2
backend/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
.venv
|
||||
**/__pycache__
|
116
backend/alembic.ini
Normal file
116
backend/alembic.ini
Normal file
|
@ -0,0 +1,116 @@
|
|||
# A generic, single database configuration.
|
||||
|
||||
[alembic]
|
||||
# path to migration scripts
|
||||
script_location = alembic
|
||||
|
||||
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
|
||||
# Uncomment the line below if you want the files to be prepended with date and time
|
||||
# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file
|
||||
# for all available tokens
|
||||
file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
|
||||
|
||||
# sys.path path, will be prepended to sys.path if present.
|
||||
# defaults to the current working directory.
|
||||
prepend_sys_path = .
|
||||
|
||||
# timezone to use when rendering the date within the migration file
|
||||
# as well as the filename.
|
||||
# If specified, requires the python-dateutil library that can be
|
||||
# installed by adding `alembic[tz]` to the pip requirements
|
||||
# string value is passed to dateutil.tz.gettz()
|
||||
# leave blank for localtime
|
||||
timezone = UTC
|
||||
|
||||
# max length of characters to apply to the
|
||||
# "slug" field
|
||||
truncate_slug_length = 20
|
||||
|
||||
# set to 'true' to run the environment during
|
||||
# the 'revision' command, regardless of autogenerate
|
||||
# revision_environment = false
|
||||
|
||||
# set to 'true' to allow .pyc and .pyo files without
|
||||
# a source .py file to be detected as revisions in the
|
||||
# versions/ directory
|
||||
# sourceless = false
|
||||
|
||||
# version location specification; This defaults
|
||||
# to alembic/versions. When using multiple version
|
||||
# directories, initial revisions must be specified with --version-path.
|
||||
# The path separator used here should be the separator specified by "version_path_separator" below.
|
||||
# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions
|
||||
|
||||
# version path separator; As mentioned above, this is the character used to split
|
||||
# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep.
|
||||
# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.
|
||||
# Valid values for version_path_separator are:
|
||||
#
|
||||
# version_path_separator = :
|
||||
# version_path_separator = ;
|
||||
# version_path_separator = space
|
||||
version_path_separator = os # Use os.pathsep. Default configuration used for new projects.
|
||||
|
||||
# set to 'true' to search source files recursively
|
||||
# in each "version_locations" directory
|
||||
# new in Alembic version 1.10
|
||||
# recursive_version_locations = false
|
||||
|
||||
# the output encoding used when revision files
|
||||
# are written from script.py.mako
|
||||
# output_encoding = utf-8
|
||||
|
||||
sqlalchemy.url = sqlite:///./scanner.db
|
||||
|
||||
|
||||
[post_write_hooks]
|
||||
# post_write_hooks defines scripts or Python functions that are run
|
||||
# on newly generated revision scripts. See the documentation for further
|
||||
# detail and examples
|
||||
|
||||
# format using "black" - use the console_scripts runner, against the "black" entrypoint
|
||||
# hooks = black
|
||||
# black.type = console_scripts
|
||||
# black.entrypoint = black
|
||||
# black.options = -l 79 REVISION_SCRIPT_FILENAME
|
||||
|
||||
# lint with attempts to fix using "ruff" - use the exec runner, execute a binary
|
||||
# hooks = ruff
|
||||
# ruff.type = exec
|
||||
# ruff.executable = %(here)s/.venv/bin/ruff
|
||||
# ruff.options = --fix REVISION_SCRIPT_FILENAME
|
||||
|
||||
# Logging configuration
|
||||
[loggers]
|
||||
keys = root,sqlalchemy,alembic
|
||||
|
||||
[handlers]
|
||||
keys = console
|
||||
|
||||
[formatters]
|
||||
keys = generic
|
||||
|
||||
[logger_root]
|
||||
level = WARN
|
||||
handlers = console
|
||||
qualname =
|
||||
|
||||
[logger_sqlalchemy]
|
||||
level = WARN
|
||||
handlers =
|
||||
qualname = sqlalchemy.engine
|
||||
|
||||
[logger_alembic]
|
||||
level = INFO
|
||||
handlers =
|
||||
qualname = alembic
|
||||
|
||||
[handler_console]
|
||||
class = StreamHandler
|
||||
args = (sys.stderr,)
|
||||
level = NOTSET
|
||||
formatter = generic
|
||||
|
||||
[formatter_generic]
|
||||
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||
datefmt = %H:%M:%S
|
1
backend/alembic/README
Normal file
1
backend/alembic/README
Normal file
|
@ -0,0 +1 @@
|
|||
Generic single-database configuration.
|
79
backend/alembic/env.py
Normal file
79
backend/alembic/env.py
Normal file
|
@ -0,0 +1,79 @@
|
|||
from logging.config import fileConfig
|
||||
|
||||
from sqlalchemy import engine_from_config
|
||||
from sqlalchemy import pool
|
||||
|
||||
from alembic import context
|
||||
|
||||
# this is the Alembic Config object, which provides
|
||||
# access to the values within the .ini file in use.
|
||||
config = context.config
|
||||
|
||||
# Interpret the config file for Python logging.
|
||||
# This line sets up loggers basically.
|
||||
if config.config_file_name is not None:
|
||||
fileConfig(config.config_file_name)
|
||||
|
||||
# add your model's MetaData object here
|
||||
# for 'autogenerate' support
|
||||
# from myapp import mymodel
|
||||
# target_metadata = mymodel.Base.metadata
|
||||
from app.data import models
|
||||
target_metadata = models.Base.metadata
|
||||
|
||||
# other values from the config, defined by the needs of env.py,
|
||||
# can be acquired:
|
||||
# my_important_option = config.get_main_option("my_important_option")
|
||||
# ... etc.
|
||||
|
||||
|
||||
def run_migrations_offline() -> None:
|
||||
"""Run migrations in 'offline' mode.
|
||||
|
||||
This configures the context with just a URL
|
||||
and not an Engine, though an Engine is acceptable
|
||||
here as well. By skipping the Engine creation
|
||||
we don't even need a DBAPI to be available.
|
||||
|
||||
Calls to context.execute() here emit the given string to the
|
||||
script output.
|
||||
|
||||
"""
|
||||
url = config.get_main_option("sqlalchemy.url")
|
||||
context.configure(
|
||||
url=url,
|
||||
target_metadata=target_metadata,
|
||||
literal_binds=True,
|
||||
dialect_opts={"paramstyle": "named"},
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
def run_migrations_online() -> None:
|
||||
"""Run migrations in 'online' mode.
|
||||
|
||||
In this scenario we need to create an Engine
|
||||
and associate a connection with the context.
|
||||
|
||||
"""
|
||||
connectable = engine_from_config(
|
||||
config.get_section(config.config_ini_section, {}),
|
||||
prefix="sqlalchemy.",
|
||||
poolclass=pool.NullPool,
|
||||
)
|
||||
|
||||
with connectable.connect() as connection:
|
||||
context.configure(
|
||||
connection=connection, target_metadata=target_metadata
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
if context.is_offline_mode():
|
||||
run_migrations_offline()
|
||||
else:
|
||||
run_migrations_online()
|
26
backend/alembic/script.py.mako
Normal file
26
backend/alembic/script.py.mako
Normal file
|
@ -0,0 +1,26 @@
|
|||
"""${message}
|
||||
|
||||
Revision ID: ${up_revision}
|
||||
Revises: ${down_revision | comma,n}
|
||||
Create Date: ${create_date}
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
${imports if imports else ""}
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = ${repr(up_revision)}
|
||||
down_revision: Union[str, None] = ${repr(down_revision)}
|
||||
branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
|
||||
depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
${upgrades if upgrades else "pass"}
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
${downgrades if downgrades else "pass"}
|
0
backend/app/__init__.py
Normal file
0
backend/app/__init__.py
Normal file
0
backend/app/data/__init__.py
Normal file
0
backend/app/data/__init__.py
Normal file
12
backend/app/data/database.py
Normal file
12
backend/app/data/database.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
SQLALCHEMY_DATABASE_URL = "sqlite:///./scanner.db"
|
||||
|
||||
engine = create_engine(
|
||||
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
|
||||
)
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
|
||||
Base = declarative_base()
|
1
backend/app/data/models.py
Normal file
1
backend/app/data/models.py
Normal file
|
@ -0,0 +1 @@
|
|||
from .database import Base
|
13
backend/app/data/schemas.py
Normal file
13
backend/app/data/schemas.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
from pydantic import BaseModel
|
||||
import app.scanner.enums as scan
|
||||
|
||||
class ScanPage(BaseModel):
|
||||
filename: str
|
||||
size_bytes: int
|
||||
|
||||
class Config():
|
||||
orm_mode = True
|
||||
|
||||
class ScanStatus(BaseModel):
|
||||
pages: list[ScanPage]
|
||||
status: scan.Status
|
43
backend/app/main.py
Normal file
43
backend/app/main.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
import threading
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import FastAPI, Depends
|
||||
|
||||
from app.data import models
|
||||
from app.data.database import SessionLocal, engine
|
||||
|
||||
from app.scanner.scanner import Scanner
|
||||
from app.scanner.scanner import Status as ScannerStatus
|
||||
|
||||
models.Base.metadata.create_all(bind=engine)
|
||||
|
||||
__scanner = Scanner("/var/www/html/img")
|
||||
|
||||
@asynccontextmanager
|
||||
async def __lifespan(app: FastAPI):
|
||||
threading.Thread(target=__scanner.preload).start()
|
||||
yield
|
||||
|
||||
app = FastAPI(lifespan=__lifespan)
|
||||
|
||||
# SQLAlchemiy session dependency
|
||||
def get_db():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
def get_scanner():
|
||||
return __scanner
|
||||
|
||||
from app.routers import power
|
||||
app.include_router(power.router)
|
||||
|
||||
from app.routers import scan
|
||||
app.include_router(scan.router)
|
||||
|
||||
@app.get("/api/ready")
|
||||
async def __ready(scanner: Annotated[Scanner, Depends(get_scanner)]):
|
||||
return scanner.get_status() != ScannerStatus.INITIALIZED
|
0
backend/app/routers/__init__.py
Normal file
0
backend/app/routers/__init__.py
Normal file
14
backend/app/routers/power.py
Normal file
14
backend/app/routers/power.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
from fastapi import APIRouter
|
||||
import subprocess
|
||||
|
||||
router = APIRouter(prefix="/api/power")
|
||||
|
||||
@router.post("/shutdown")
|
||||
async def power_shutdown():
|
||||
subprocess.call(["sudo", "shutdown", "-h", "now"])
|
||||
return {}
|
||||
|
||||
@router.post("/restart")
|
||||
async def power_restart():
|
||||
subprocess.call(["sudo", "reboot"])
|
||||
return {}
|
23
backend/app/routers/scan.py
Normal file
23
backend/app/routers/scan.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
from typing import Annotated
|
||||
|
||||
from app.scanner.scanner import Scanner
|
||||
from app.main import get_scanner
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from app.data import schemas, models
|
||||
|
||||
router = APIRouter(prefix="/api/scan")
|
||||
|
||||
@router.post("")
|
||||
async def scan(scanner: Annotated[Scanner, Depends(get_scanner)]):
|
||||
scanner.scan()
|
||||
return []
|
||||
|
||||
@router.get("/status", response_model=schemas.ScanStatus)
|
||||
async def status(scanner: Annotated[Scanner, Depends(get_scanner)]):
|
||||
pages = [schemas.ScanPage.from_orm(page) for page in scanner.get_pages()]
|
||||
return schemas.ScanStatus(pages=pages,status=scanner.get_status())
|
||||
|
||||
@router.get("/debug")
|
||||
async def debug(scanner: Annotated[Scanner, Depends(get_scanner)]):
|
||||
return scanner.get_options()
|
0
backend/app/scanner/__init__.py
Normal file
0
backend/app/scanner/__init__.py
Normal file
22
backend/app/scanner/enums.py
Normal file
22
backend/app/scanner/enums.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
from enum import Enum
|
||||
|
||||
class Status(Enum):
|
||||
INITIALIZED = "initialized"
|
||||
IDLE = "idle"
|
||||
RUNNING = "running"
|
||||
DONE = "done"
|
||||
ERR_NO_PAPER = "err_no_paper"
|
||||
ERR_COVER_OPEN = "err_cover_open"
|
||||
|
||||
class Setting(Enum):
|
||||
PAPER_SOURCE = "source"
|
||||
COLOR_MODE = "color"
|
||||
RESOLUTION = "resolution"
|
||||
PAPER_SIZE = "paper_size"
|
||||
|
||||
class PaperSize(Enum):
|
||||
A3 = "a3"
|
||||
B3 = "b3"
|
||||
A4 = "a4"
|
||||
B4 = "b4"
|
||||
LETTER = "letter"
|
140
backend/app/scanner/scanner.py
Normal file
140
backend/app/scanner/scanner.py
Normal file
|
@ -0,0 +1,140 @@
|
|||
import gi, os, threading
|
||||
from typing import List
|
||||
|
||||
from PIL import Image
|
||||
from app.scanner.enums import Status
|
||||
|
||||
gi.require_version('Libinsane', '1.0')
|
||||
from gi.repository import Libinsane, GObject # type: ignore
|
||||
|
||||
class __LibinsaneSilentLogger(GObject.GObject, Libinsane.Logger):
|
||||
def do_log(self, lvl, msg):
|
||||
return
|
||||
|
||||
Libinsane.register_logger(__LibinsaneSilentLogger())
|
||||
|
||||
class Page:
|
||||
filename: str
|
||||
size_bytes: int
|
||||
|
||||
class Scanner:
|
||||
|
||||
def __get_device_id(self):
|
||||
"""
|
||||
List local scanners and get the device id of the first found device.
|
||||
|
||||
:param self: Instance of this class
|
||||
:returns: Device id of the first scan device
|
||||
"""
|
||||
devs = self.api.list_devices(Libinsane.DeviceLocations.LOCAL_ONLY)
|
||||
return devs[0].get_dev_id()
|
||||
|
||||
def __raw_to_img(self, params, img_bytes):
|
||||
"""
|
||||
|
||||
"""
|
||||
fmt = params.get_format()
|
||||
assert(fmt == Libinsane.ImgFormat.RAW_RGB_24)
|
||||
(w, h) = (
|
||||
params.get_width(),
|
||||
int(len(img_bytes) / 3 / params.get_width())
|
||||
)
|
||||
return Image.frombuffer("RGB", (w, h), img_bytes, "raw", "RGB", 0, 1)
|
||||
|
||||
def __write_file(self, scan_params, data, page_index, last_file):
|
||||
data = b"".join(data)
|
||||
if scan_params.get_format() == Libinsane.ImgFormat.RAW_RGB_24:
|
||||
filesize = len(data)
|
||||
img = self.__raw_to_img(scan_params, data)
|
||||
filename = f"out{page_index}.png"
|
||||
img.save(os.path.join(self.storage_path, filename), format="PNG")
|
||||
page = Page()
|
||||
page.filename = filename
|
||||
page.size_bytes = filesize
|
||||
self.scanned_pages.append(page)
|
||||
if last_file:
|
||||
self.status = Status.DONE
|
||||
|
||||
def __set_defaults(self):
|
||||
dev = self.api.get_device(self.device_id)
|
||||
opts = dev.get_options()
|
||||
opts = {opt.get_name(): opt for opt in opts}
|
||||
opts["sleeptimer"].set_value(1)
|
||||
opts["resolution"].set_value(200)
|
||||
dev.close()
|
||||
|
||||
def __scan(self):
|
||||
self.status = Status.RUNNING
|
||||
source = self.api.get_device(self.device_id)
|
||||
|
||||
opts = source.get_options()
|
||||
opts = {opt.get_name(): opt for opt in opts}
|
||||
if opts["cover-open"].get_value() == True:
|
||||
self.status = Status.ERR_COVER_OPEN
|
||||
return
|
||||
|
||||
session = source.scan_start()
|
||||
try:
|
||||
page_index = 0
|
||||
while not session.end_of_feed() and page_index < 50:
|
||||
# Do not assume that all the pages will have the same size !
|
||||
scan_params = session.get_scan_parameters()
|
||||
img = []
|
||||
while not session.end_of_page():
|
||||
data = session.read_bytes(256 * 1024)
|
||||
data = data.get_data()
|
||||
img.append(data)
|
||||
t = threading.Thread(target=self.__write_file, args=(scan_params, img, page_index, session.end_of_feed()))
|
||||
t.start()
|
||||
page_index += 1
|
||||
if page_index == 0:
|
||||
self.status = Status.ERR_NO_PAPER
|
||||
finally:
|
||||
session.cancel()
|
||||
source.close()
|
||||
|
||||
def __init__(self, storage_path):
|
||||
self.scanned_pages: List[Page] = []
|
||||
self.storage_path = storage_path
|
||||
self.status = Status.INITIALIZED
|
||||
|
||||
def preload(self):
|
||||
os.environ["LIBINSANE_NORMALIZER_SAFE_DEFAULTS"] = "0"
|
||||
self.api = Libinsane.Api.new_safebet()
|
||||
self.device_id = self.__get_device_id()
|
||||
self.__set_defaults()
|
||||
self.status = Status.IDLE
|
||||
|
||||
def scan(self):
|
||||
if self.status == Status.RUNNING:
|
||||
raise RuntimeError("already_running")
|
||||
if self.status == Status.INITIALIZED:
|
||||
self.preload()
|
||||
self.scanned_pages: List[Page] = []
|
||||
t = threading.Thread(target=self.__scan)
|
||||
t.start()
|
||||
|
||||
def get_status(self) -> Status:
|
||||
return self.status
|
||||
|
||||
def get_pages(self) -> List[Page]:
|
||||
return self.scanned_pages
|
||||
|
||||
def get_options(self):
|
||||
dev = self.api.get_device(self.device_id)
|
||||
opts = dev.get_options()
|
||||
result = {}
|
||||
for opt in opts:
|
||||
try:
|
||||
result[opt.get_name()] = opt.get_value()
|
||||
except Exception:
|
||||
continue
|
||||
dev.close()
|
||||
return result
|
||||
|
||||
def cleanup(self):
|
||||
if self.status == Status.RUNNING:
|
||||
raise RuntimeError("scan_running")
|
||||
if self.status != Status.INITIALIZED:
|
||||
self.api.cleanup()
|
||||
|
30
backend/requirements.txt
Normal file
30
backend/requirements.txt
Normal file
|
@ -0,0 +1,30 @@
|
|||
alembic==1.12.0
|
||||
annotated-types==0.5.0
|
||||
anyio==3.7.1
|
||||
click==8.1.7
|
||||
fastapi==0.103.1
|
||||
greenlet==2.0.2
|
||||
gunicorn==21.2.0
|
||||
h11==0.14.0
|
||||
httptools==0.6.0
|
||||
idna==3.4
|
||||
Mako==1.2.4
|
||||
MarkupSafe==2.1.3
|
||||
packaging==23.1
|
||||
Pillow==10.0.0
|
||||
pycairo==1.24.0
|
||||
pydantic==1.10.12
|
||||
pydantic_core==2.6.3
|
||||
PyGObject==3.44.1
|
||||
python-dateutil==2.8.2
|
||||
python-dotenv==1.0.0
|
||||
PyYAML==6.0.1
|
||||
six==1.16.0
|
||||
sniffio==1.3.0
|
||||
SQLAlchemy==2.0.20
|
||||
starlette==0.27.0
|
||||
typing_extensions==4.7.1
|
||||
uvicorn==0.23.2
|
||||
uvloop==0.17.0
|
||||
watchfiles==0.20.0
|
||||
websockets==11.0.3
|
BIN
docs/img/logo.png
Normal file
BIN
docs/img/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.8 KiB |
15
frontend/.eslintrc.cjs
Normal file
15
frontend/.eslintrc.cjs
Normal file
|
@ -0,0 +1,15 @@
|
|||
/* eslint-env node */
|
||||
require('@rushstack/eslint-patch/modern-module-resolution')
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
'extends': [
|
||||
'plugin:vue/vue3-essential',
|
||||
'eslint:recommended',
|
||||
'@vue/eslint-config-typescript',
|
||||
'@vue/eslint-config-prettier/skip-formatting'
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest'
|
||||
}
|
||||
}
|
28
frontend/.gitignore
vendored
Normal file
28
frontend/.gitignore
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
8
frontend/.prettierrc.json
Normal file
8
frontend/.prettierrc.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/prettierrc",
|
||||
"semi": false,
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"trailingComma": "none"
|
||||
}
|
8
frontend/.vscode/extensions.json
vendored
Normal file
8
frontend/.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"recommendations": [
|
||||
"Vue.volar",
|
||||
"Vue.vscode-typescript-vue-plugin",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode"
|
||||
]
|
||||
}
|
42
frontend/README.md
Normal file
42
frontend/README.md
Normal file
|
@ -0,0 +1,42 @@
|
|||
# ScanOS frontend
|
||||
|
||||
This folder contains the frontend of ScanOS. It is built using Vue 3 in Vite and Tailwind CSS. The frontend runs inside Chromium on the scanner.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
|
||||
|
||||
## Type Support for `.vue` Imports in TS
|
||||
|
||||
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
|
||||
|
||||
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
|
||||
|
||||
1. Disable the built-in TypeScript Extension
|
||||
1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette
|
||||
2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
|
||||
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
yarn install
|
||||
```
|
||||
|
||||
### Compile and Hot-Reload for Development
|
||||
|
||||
```sh
|
||||
yarn dev
|
||||
```
|
||||
|
||||
### Type-Check, Compile and Minify for Production
|
||||
|
||||
```sh
|
||||
yarn build
|
||||
```
|
||||
|
||||
### Lint with [ESLint](https://eslint.org/)
|
||||
|
||||
```sh
|
||||
yarn lint
|
||||
```
|
1
frontend/env.d.ts
vendored
Normal file
1
frontend/env.d.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/// <reference types="vite/client" />
|
12
frontend/index.html
Normal file
12
frontend/index.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
|
||||
<title>ScanOS</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
40
frontend/package.json
Normal file
40
frontend/package.json
Normal file
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"name": "scan-os-frontend",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "run-p type-check build-only",
|
||||
"preview": "vite preview",
|
||||
"build-only": "vite build",
|
||||
"type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
||||
"format": "prettier --write src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.5.0",
|
||||
"bootstrap-icons": "^1.11.1",
|
||||
"pinia": "^2.1.6",
|
||||
"vue": "^3.3.4",
|
||||
"vue-router": "^4.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rushstack/eslint-patch": "^1.3.2",
|
||||
"@tsconfig/node18": "^18.2.0",
|
||||
"@types/node": "^20.5.7",
|
||||
"@vitejs/plugin-vue": "^4.3.1",
|
||||
"@vue/eslint-config-prettier": "^8.0.0",
|
||||
"@vue/eslint-config-typescript": "^11.0.3",
|
||||
"@vue/tsconfig": "^0.4.0",
|
||||
"autoprefixer": "^10.4.15",
|
||||
"eslint": "^8.46.0",
|
||||
"eslint-plugin-vue": "^9.16.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss": "^8.4.29",
|
||||
"prettier": "^3.0.0",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"typescript": "~5.1.6",
|
||||
"vite": "^4.4.9",
|
||||
"vue-tsc": "^1.8.8"
|
||||
}
|
||||
}
|
6
frontend/postcss.config.js
Normal file
6
frontend/postcss.config.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
52
frontend/src/App.vue
Normal file
52
frontend/src/App.vue
Normal file
|
@ -0,0 +1,52 @@
|
|||
<script setup lang="ts">
|
||||
import { RouterView } from 'vue-router'
|
||||
import TopBar from './components/TopBar.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full h-full bg-gray-100 flex flex-col">
|
||||
<TopBar />
|
||||
<div class="w-full h-full flex flex-row">
|
||||
<RouterView v-slot="{ Component, route}">
|
||||
<!--<Transition>-->
|
||||
<component :is="Component" :key="route.path" />
|
||||
<!--</Transition-->
|
||||
</RouterView>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
@keyframes slidein {
|
||||
0% {
|
||||
transform: translateX(-200vh);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
@keyframes slideout {
|
||||
0% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(+200vh);
|
||||
}
|
||||
}
|
||||
.v-enter-from {
|
||||
transform: translateX(-200vh);
|
||||
}
|
||||
.v-enter-active {
|
||||
animation: slidein 0.3s ease-in-out;
|
||||
}
|
||||
.v-leave-from {
|
||||
transform: translateX(0);
|
||||
}
|
||||
.v-leave-active {
|
||||
animation: slideout 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.v-leave-to {
|
||||
transform: translateX(+200vh);
|
||||
}
|
||||
</style>
|
20
frontend/src/assets/main.css
Normal file
20
frontend/src/assets/main.css
Normal file
|
@ -0,0 +1,20 @@
|
|||
body {
|
||||
min-height: 100vh;
|
||||
font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
|
||||
Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
#app {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 1024px;
|
||||
height: 768px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
4
frontend/src/components/LoadingSpinner.vue
Normal file
4
frontend/src/components/LoadingSpinner.vue
Normal file
|
@ -0,0 +1,4 @@
|
|||
<template>
|
||||
<div class="inline-block animate-spin rounded-full border-4 border-solid border-current border-r-transparent align-[-0.125em] motion-reduce:animate-[spin_1.5s_linear_infinite]">
|
||||
</div>
|
||||
</template>
|
12
frontend/src/components/ModalComponent.vue
Normal file
12
frontend/src/components/ModalComponent.vue
Normal file
|
@ -0,0 +1,12 @@
|
|||
<template>
|
||||
<div class="fixed inset-0 bg-black bg-opacity-30 flex justify-center items-center">
|
||||
<div class="bg-white rounded-lg shadow-lg flex">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'ModalComponent'
|
||||
};
|
||||
</script>
|
18
frontend/src/components/ScannedPage.vue
Normal file
18
frontend/src/components/ScannedPage.vue
Normal file
|
@ -0,0 +1,18 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import LoadingSpinner from '@/components/LoadingSpinner.vue';
|
||||
|
||||
const props = defineProps({
|
||||
imgUrl: String
|
||||
})
|
||||
|
||||
const imgLoaded = ref(false)
|
||||
</script>
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<div class="w-full h-full rounded-lg shadow-lg bg-white flex justify-center items-center">
|
||||
<LoadingSpinner v-if="!imgLoaded" class="w-10 h-10 text-gray-600" />
|
||||
<img v-if="imgUrl" v-show="imgLoaded" :src="imgUrl" @load="imgLoaded=true" class="w-full h-full rounded-lg object-cover">
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
5
frontend/src/components/ShadowCard.vue
Normal file
5
frontend/src/components/ShadowCard.vue
Normal file
|
@ -0,0 +1,5 @@
|
|||
<template>
|
||||
<div class="bg-white rounded-lg shadow-lg">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
22
frontend/src/components/TopBar.vue
Normal file
22
frontend/src/components/TopBar.vue
Normal file
|
@ -0,0 +1,22 @@
|
|||
<script setup lang="ts">
|
||||
import Logo from './icons/Logo.vue';
|
||||
import SettingsGear from './icons/SettingsGear.vue';
|
||||
import PowerButton from './icons/PowerButton.vue';
|
||||
import { RouterLink } from 'vue-router';
|
||||
</script>
|
||||
<template>
|
||||
<div class="w-full h-28 p-4 flex-shrink-0">
|
||||
<div class="w-full h-full pl-6 pr-6 rounded-lg shadow-lg bg-white flex flex-row justify-center items-center gap-5">
|
||||
<RouterLink to="/" class="h-full w-auto pt-4 pb-4 flex-none">
|
||||
<Logo class="h-full w-auto flex-none text-gray-600" />
|
||||
</RouterLink>
|
||||
<span class="h-full grow"></span>
|
||||
<RouterLink to="/settings" class="h-full w-auto pt-5 pb-5 flex-none">
|
||||
<SettingsGear class="h-full w-auto text-gray-600" />
|
||||
</RouterLink>
|
||||
<RouterLink to="/power" class="h-full w-auto pt-5 pb-5 flex-none">
|
||||
<PowerButton class="h-full w-auto text-gray-600" />
|
||||
</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
5
frontend/src/components/icons/ArrowClockwise.vue
Normal file
5
frontend/src/components/icons/ArrowClockwise.vue
Normal file
|
@ -0,0 +1,5 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<use xlink:href="~/bootstrap-icons/bootstrap-icons.svg#arrow-clockwise"/>
|
||||
</svg>
|
||||
</template>
|
5
frontend/src/components/icons/EnvelopeAt.vue
Normal file
5
frontend/src/components/icons/EnvelopeAt.vue
Normal file
|
@ -0,0 +1,5 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<use xlink:href="~/bootstrap-icons/bootstrap-icons.svg#envelope-at"/>
|
||||
</svg>
|
||||
</template>
|
5
frontend/src/components/icons/FolderSymlink.vue
Normal file
5
frontend/src/components/icons/FolderSymlink.vue
Normal file
|
@ -0,0 +1,5 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<use xlink:href="~/bootstrap-icons/bootstrap-icons.svg#folder-symlink"/>
|
||||
</svg>
|
||||
</template>
|
15
frontend/src/components/icons/Logo.vue
Normal file
15
frontend/src/components/icons/Logo.vue
Normal file
|
@ -0,0 +1,15 @@
|
|||
<template>
|
||||
<svg viewBox="0 0 410.5 131">
|
||||
<g transform="matrix(1,0,0,1,0,0)" fill="currentColor">
|
||||
<rect width="128" height="131" rx="8"></rect>
|
||||
</g>
|
||||
<g transform="matrix(1.1107004880905151,0,0,1.1107004880905151,12.586962218116945,9.922718916840406)" fill="#ffffff">
|
||||
<polygon xmlns="http://www.w3.org/2000/svg" points="60.3031502,62.5137749 85.3330688,47.9245911 85.2139664,18.9536228 60.0643883,4.5712423 35.0344353,19.1604233 60.1840477,33.5422134"></polygon>
|
||||
<polygon xmlns="http://www.w3.org/2000/svg" points="85.3342133,51.8689041 85.3330688,51.8689041 60.3031502,66.4577866 35.1541672,52.0760002 35.2731972,81.0466766 60.4234772,95.4287567 85.4533157,80.8395767"></polygon>
|
||||
<polygon xmlns="http://www.w3.org/2000/svg" points="56.8537292,35.8179016 31.704155,21.4358158 6.6742005,36.0249977 6.7939329,64.9959641 31.9435081,79.3780518 31.8238506,50.4073792"></polygon>
|
||||
</g>
|
||||
<g transform="matrix(1.724704384803772,0,0,1.724704384803772,144.68856912093733,21.131404856786435)" fill="currentColor">
|
||||
<path d="M13.4 40.4 c-3.8 0 -7.4 -1.2 -10.52 -3.52 c-0.56 -0.4 -0.96 -1.08 -0.96 -1.88 c0 -1.28 1.04 -2.28 2.32 -2.28 c0.68 0 1.12 0.2 1.44 0.44 c2.32 1.84 4.8 2.88 7.84 2.88 s4.96 -1.44 4.96 -3.52 l0 -0.08 c0 -2 -1.12 -3.08 -6.32 -4.28 c-5.96 -1.44 -9.32 -3.2 -9.32 -8.36 l0 -0.08 c0 -4.8 4 -8.12 9.56 -8.12 c3.52 0 6.36 0.92 8.88 2.6 c0.56 0.32 1.08 1 1.08 1.96 c0 1.28 -1.04 2.28 -2.32 2.28 c-0.48 0 -0.88 -0.12 -1.28 -0.36 c-2.16 -1.4 -4.24 -2.12 -6.44 -2.12 c-2.88 0 -4.56 1.48 -4.56 3.32 l0 0.08 c0 2.16 1.28 3.12 6.68 4.4 c5.92 1.44 8.96 3.56 8.96 8.2 l0 0.08 c0 5.24 -4.12 8.36 -10 8.36 z M38.400000000000006 40.48 c-6.28 0 -10.92 -4.92 -10.92 -10.92 l0 -0.08 c0 -6 4.64 -11.04 11 -11.04 c3.32 0 5.6 1.08 7.4 2.64 c0.32 0.28 0.76 0.88 0.76 1.68 c0 1.24 -1 2.2 -2.24 2.2 c-0.6 0 -1.12 -0.24 -1.44 -0.48 c-1.24 -1.04 -2.6 -1.8 -4.52 -1.8 c-3.52 0 -6.12 3.04 -6.12 6.72 l0 0.08 c0 3.76 2.6 6.76 6.32 6.76 c1.92 0 3.4 -0.76 4.72 -1.88 c0.28 -0.24 0.76 -0.52 1.32 -0.52 c1.16 0 2.08 0.96 2.08 2.12 c0 0.64 -0.24 1.16 -0.68 1.52 c-1.88 1.8 -4.16 3 -7.68 3 z M57.559999999999995 40.44 c-3.96 0 -7.48 -2.28 -7.48 -6.52 l0 -0.08 c0 -4.56 3.56 -6.8 8.72 -6.8 c2.36 0 4.04 0.36 5.68 0.88 l0 -0.52 c0 -3 -1.84 -4.6 -5.24 -4.6 c-1.84 0 -3.36 0.32 -4.68 0.84 c-0.28 0.08 -0.52 0.12 -0.76 0.12 c-1.12 0 -2.04 -0.88 -2.04 -2 c0 -0.88 0.6 -1.64 1.32 -1.92 c2 -0.76 4.04 -1.24 6.8 -1.24 c3.16 0 5.52 0.84 7 2.36 c1.56 1.52 2.28 3.76 2.28 6.52 l0 10.4 c0 1.32 -1.04 2.32 -2.36 2.32 c-1.4 0 -2.36 -0.96 -2.36 -2.04 l0 -0.8 c-1.44 1.72 -3.64 3.08 -6.88 3.08 z M58.92 36.92 c3.2 0 5.64 -1.84 5.64 -4.52 l0 -1.44 c-1.24 -0.48 -2.88 -0.84 -4.8 -0.84 c-3.12 0 -4.96 1.32 -4.96 3.52 l0 0.08 c0 2.04 1.8 3.2 4.12 3.2 z M74.96000000000001 37.8 l0 -16.72 c0 -1.36 1.04 -2.44 2.4 -2.44 s2.44 1.08 2.44 2.44 l0 1.04 c1.36 -1.96 3.32 -3.68 6.6 -3.68 c4.76 0 7.52 3.2 7.52 8.08 l0 11.28 c0 1.36 -1.04 2.4 -2.4 2.4 s-2.44 -1.04 -2.44 -2.4 l0 -9.8 c0 -3.28 -1.64 -5.16 -4.52 -5.16 c-2.8 0 -4.76 1.96 -4.76 5.24 l0 9.72 c0 1.36 -1.08 2.4 -2.44 2.4 c-1.32 0 -2.4 -1.04 -2.4 -2.4 z M113.68 40.48 c-8.6 0 -14.6 -6.52 -14.6 -14.4 l0 -0.08 c0 -7.88 6.08 -14.48 14.68 -14.48 s14.6 6.52 14.6 14.4 l0 0.08 c0 7.88 -6.08 14.48 -14.68 14.48 z M113.75999999999999 35.92 c5.56 0 9.44 -4.4 9.44 -9.84 l0 -0.08 c0 -5.44 -3.96 -9.96 -9.52 -9.96 s-9.44 4.44 -9.44 9.88 l0 0.08 c0 5.44 3.96 9.92 9.52 9.92 z M144.12 40.4 c-3.8 0 -7.4 -1.2 -10.52 -3.52 c-0.56 -0.4 -0.96 -1.08 -0.96 -1.88 c0 -1.28 1.04 -2.28 2.32 -2.28 c0.68 0 1.12 0.2 1.44 0.44 c2.32 1.84 4.8 2.88 7.84 2.88 s4.96 -1.44 4.96 -3.52 l0 -0.08 c0 -2 -1.12 -3.08 -6.32 -4.28 c-5.96 -1.44 -9.32 -3.2 -9.32 -8.36 l0 -0.08 c0 -4.8 4 -8.12 9.56 -8.12 c3.52 0 6.36 0.92 8.88 2.6 c0.56 0.32 1.08 1 1.08 1.96 c0 1.28 -1.04 2.28 -2.32 2.28 c-0.48 0 -0.88 -0.12 -1.28 -0.36 c-2.16 -1.4 -4.24 -2.12 -6.44 -2.12 c-2.88 0 -4.56 1.48 -4.56 3.32 l0 0.08 c0 2.16 1.28 3.12 6.68 4.4 c5.92 1.44 8.96 3.56 8.96 8.2 l0 0.08 c0 5.24 -4.12 8.36 -10 8.36 z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
36
frontend/src/components/icons/PaperlessLogo.vue
Normal file
36
frontend/src/components/icons/PaperlessLogo.vue
Normal file
|
@ -0,0 +1,36 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2812.1333 801.20001">
|
||||
<g transform="matrix(1.3333333,0,0,-1.3333333,0,801.2)">
|
||||
<g transform="scale(0.1)">
|
||||
<path style="fill:currentColor;fill-opacity:1;fill-rule:nonzero;stroke:none" d="m 7436.71,2391.22 c 0,126.66 -37.86,232.21 -112.83,318.83 -74.98,86.62 -173.97,128.84 -295.54,128.84 -121.56,0 -219.83,-42.94 -294.08,-128.84 -74.24,-85.89 -111.37,-192.17 -111.37,-318.83 0,-126.65 37.13,-232.2 111.37,-318.82 74.25,-86.63 171.79,-128.85 294.08,-128.85 122.29,0 220.56,42.95 295.54,128.85 74.97,85.89 112.83,192.17 112.83,318.82 z m -25.48,-908.44 v 149.95 c -131.03,-145.58 -313.73,-219.1 -548.85,-219.1 -163.06,0 -311.55,42.22 -444.03,127.39 -132.48,85.16 -236.58,202.36 -312.28,351.58 -75.7,149.22 -112.83,315.19 -112.83,497.9 0,182.7 37.85,349.4 112.83,498.62 74.98,149.22 179.07,266.42 312.28,351.59 133.21,85.16 280.97,127.38 444.03,127.38 235.12,0 418.55,-72.79 548.85,-219.1 v 149.95 h 620.91 V 1482.05 h -620.91 v 0.73"/>
|
||||
<path style="fill:currentColor;fill-opacity:1;fill-rule:nonzero;stroke:none" d="m 9841.75,2391.22 c 0,126.66 -37.13,232.21 -111.37,318.83 -74.25,86.62 -171.79,128.84 -294.08,128.84 -122.29,0 -219.83,-42.94 -294.08,-128.84 -74.25,-85.89 -111.37,-192.17 -111.37,-318.83 0,-126.65 37.12,-232.2 111.37,-318.82 74.25,-86.63 171.79,-128.85 294.08,-128.85 122.29,0 219.83,42.95 294.08,128.85 74.24,85.89 111.37,192.17 111.37,318.82 z m 205.95,849.49 c 133.3,-85.17 236.6,-202.37 312.3,-351.59 75.7,-149.22 112.9,-315.19 112.9,-498.62 0,-183.44 -37.9,-349.4 -112.9,-497.9 -74.9,-148.5 -179,-266.42 -312.3,-351.58 -133.16,-85.17 -280.93,-127.39 -443.98,-127.39 -235.12,0 -418.55,72.79 -548.85,219.1 V 427.301 H 8433.95 V 3298.94 h 620.92 v -149.95 c 131.02,145.58 313.73,219.1 548.85,219.1 157.22,1.4 311.47,-42.85 443.98,-127.38"/>
|
||||
<path style="fill:currentColor;fill-opacity:1;fill-rule:nonzero;stroke:none" d="m 11351.5,2600.14 h 676.2 c -15.1,89.29 -54.4,160.62 -117.9,214.01 -60.9,52.41 -138.8,80.87 -219.1,80.07 -80.8,0.68 -159.1,-27.74 -220.6,-80.07 -64.1,-53.14 -103.4,-124.48 -118.6,-214.01 z m 849.4,-502.99 385.8,-359.6 c -204.5,-215.46 -485.5,-323.19 -842.9,-323.19 -204.5,0 -385.8,42.22 -542.3,127.38 -156.5,85.17 -278.1,201.64 -362.5,349.4 -84.4,147.77 -128.8,314.46 -131,500.08 0,180.53 42.9,345.52 128.8,494.99 84.2,147.45 206.7,269.42 354.5,353.04 150,85.89 316.7,128.84 500.1,128.84 180.5,0 346.5,-44.4 496.4,-132.48 150,-88.08 269.4,-214.74 357.4,-380.7 88.1,-165.97 132.5,-357.41 132.5,-575.06 l -2.9,-85.16 h -1323.3 c 26.2,-82.99 76.4,-150.68 152.1,-204.55 75.7,-53.86 155,-80.07 240.2,-80.07 104.8,0 191.4,16.01 259.9,47.31 72.9,35.54 139.5,82.75 197.2,139.77"/>
|
||||
<path style="fill:currentColor;fill-opacity:1;fill-rule:nonzero;stroke:none" d="m 13621.8,3093.67 c 53.3,75.56 124,137.22 206,179.79 80.8,41.49 160.2,61.88 238.8,61.88 83,0 145.6,-11.65 189.2,-35.67 l -78.6,-529.2 c -52.2,13.97 -106,21.55 -160.1,22.57 -124.5,0 -221.3,-31.31 -290.4,-93.18 -69.2,-61.87 -104.9,-176.15 -104.9,-341.39 v -875.69 h -620.9 v 1816.16 h 620.9 v -205.27 0"/>
|
||||
<path style="fill:currentColor;fill-opacity:1;fill-rule:nonzero;stroke:none" d="M 15108.2,4259.79 V 1482.78 h -620.9 v 2777.01 h 620.9"/>
|
||||
<g transform="scale(1.0668)">
|
||||
<path style="fill:currentColor;fill-opacity:1;fill-rule:nonzero;stroke:none" d="m 15056.7,2437.33 h 634 c -14.3,83.93 -51.2,150.8 -110.5,200.61 -57.1,49.13 -130.1,75.81 -205.4,75.06 -75.7,0.63 -149.1,-26 -206.8,-75.06 -59.3,-49.81 -96.9,-116.68 -111.3,-200.61 z m 796.4,-471.49 361.6,-337.09 c -191.7,-201.97 -455.1,-302.95 -790.1,-302.95 -191.8,0 -361.7,39.58 -508.4,119.4 -146.7,79.84 -260.7,189.02 -339.8,327.53 -79.2,138.51 -120.8,294.77 -122.8,468.76 0,169.23 40.2,323.89 120.8,464 78.9,138.22 193.7,252.55 332.3,330.93 140.5,80.52 296.7,120.78 468.8,120.78 169.2,0 324.8,-41.62 465.3,-124.19 140.6,-82.56 252.4,-201.29 335,-356.86 82.6,-155.58 124.2,-335.03 124.2,-539.05 l -2.7,-79.83 h -1240.6 c 24.6,-77.79 71.8,-141.24 142.7,-191.74 71,-50.49 145.3,-75.06 225.2,-75.06 98.2,0 179.4,15.01 243.6,44.35 68.4,33.14 131,77.42 184.9,131.02"/>
|
||||
</g>
|
||||
<g transform="scale(1.18166)">
|
||||
<path style="fill:currentColor;fill-opacity:1;fill-rule:nonzero;stroke:none" d="m 15575.6,2446.21 c -61,0 -105.4,-6.78 -132.4,-20.94 -27.1,-14.18 -41.3,-36.35 -41.3,-67.76 0,-31.43 20.9,-54.22 63.4,-69 42.5,-14.79 110.9,-30.19 204.6,-46.82 123.1,-18.48 227.6,-41.48 313.5,-69 85,-27.1 161.6,-75.69 222.3,-141.06 62.9,-66.53 94.3,-159.55 94.3,-279.06 0,-184.19 -67.4,-322.99 -202,-416.42 -134.3,-93.02 -309.9,-139.84 -525.5,-139.84 -158.3,0 -304.9,22.59 -439.8,67.76 -134.9,45.18 -230.8,90.76 -287.7,136.76 l 207.6,373.31 c 62.8,-55.45 144.4,-97.33 244.6,-125.67 100.3,-28.34 192.8,-43.12 277.8,-43.12 121.4,0 182.3,32.03 182.3,96.71 0,33.27 -20.3,57.91 -60.9,73.3 -40.7,15.41 -106,32.03 -196.5,48.67 -116.5,20.33 -215.1,44.35 -295.7,72.08 -81.8,28.05 -154.6,77.13 -211.3,142.29 -59.8,67.15 -90,160.17 -90,277.83 0,167.55 59.7,298.77 178.6,392.4 118.9,93.63 283.4,141.06 493.5,141.06 143.7,0 273.7,-22.17 389.9,-66.52 116.3,-44.35 209.3,-89.53 279.1,-135.52 l -202.1,-362.22 c -71,48.5 -148.3,87.04 -229.8,114.57 -84.9,31.43 -164.4,46.21 -236.5,46.21"/>
|
||||
</g>
|
||||
<g transform="scale(1.29393)" id="g32">
|
||||
<path id="path34" style="fill:currentColor;fill-opacity:1;fill-rule:nonzero;stroke:none" d="m 15638.4,2233.96 c -55.7,0 -96.2,-6.19 -120.9,-19.13 -24.8,-12.94 -37.7,-33.19 -37.7,-61.88 0,-28.7 19.1,-49.51 57.9,-63.01 38.8,-13.5 101.3,-27.57 186.8,-42.75 112.5,-16.88 207.9,-37.89 286.4,-63.01 77.6,-24.76 147.5,-69.13 203,-128.83 57.4,-60.76 86.1,-145.7 86.1,-254.84 0,-168.21 -61.5,-294.97 -184.6,-380.29 -122.5,-84.96 -282.9,-127.71 -479.8,-127.71 -144.5,0 -278.5,20.63 -401.7,61.88 -122.6,41.07 -210.2,82.7 -262.6,124.9 l 189.5,340.91 c 57.4,-50.64 131.8,-88.88 223.4,-114.77 91.6,-25.87 176,-39.37 253.6,-39.37 111.5,0 166.6,29.25 166.6,88.32 0,30.38 -18.6,52.88 -55.7,66.94 -37.1,14.07 -96.8,29.25 -179.5,44.45 -106.3,18.56 -196.3,40.5 -270,65.82 -74.6,25.61 -141.1,70.44 -192.9,129.95 -54.6,61.32 -82.2,146.26 -82.2,253.72 0,153.01 54.6,272.84 163.2,358.35 108.5,85.51 258.7,128.83 450.5,128.83 131.3,0 250,-20.25 356.2,-60.76 106.1,-40.5 191.1,-81.76 254.8,-123.76 l -184.5,-330.79 c -64.9,44.29 -135.4,79.49 -209.8,104.63 -78.3,28.7 -150.8,42.2 -216.1,42.2"/>
|
||||
</g>
|
||||
<g transform="scale(1.13017)" id="g36">
|
||||
<path id="path38" style="fill:currentColor;fill-opacity:1;fill-rule:nonzero;stroke:none" d="m 15912.3,769.049 h 387.7 v -111.43 h -387.7 v 111.43"/>
|
||||
</g>
|
||||
<g transform="scale(1.18041)" id="g40">
|
||||
<path style="fill:currentColor;fill-opacity:1;fill-rule:nonzero;stroke:none" d="m 15982.5,918.845 c 31.2,12.777 64.8,19.273 98.6,19.121 67.3,0 120.3,-20.967 159.8,-62.902 39.4,-41.935 59.1,-102.981 59.1,-182.538 V 339.181 h -120.8 v 324.362 c 0,57.353 -10.5,99.907 -31.5,127.651 -20.9,27.755 -56.1,41.316 -104.8,41.316 -38,0.755 -74.3,-15.265 -99.3,-43.781 -26.5,-29.598 -39.5,-70.917 -39.5,-124.567 V 339.8 h -120.9 v 583.367 h 120.9 v -58.59 c 22.1,23.237 48.8,41.706 78.4,54.268"/>
|
||||
</g>
|
||||
<g transform="scale(1.2352)" id="g44">
|
||||
<path style="fill:currentColor;fill-opacity:1;fill-rule:nonzero;stroke:none" d="m 16175.1,516.837 c 15.4,28.791 23.1,61.06 22.3,93.703 0.4,32.788 -7.3,65.162 -22.3,94.285 -14.3,27.096 -35.6,49.739 -62,65.412 -26.8,15.951 -57.6,24.11 -88.9,23.576 -64.1,0.98 -123.1,-35.031 -151.5,-92.527 -28.3,-57.189 -28.3,-124.303 0,-181.502 13.7,-27.734 34.7,-51.18 60.7,-67.767 27.1,-17.188 58.7,-26.005 90.8,-25.351 31.3,-0.332 62.2,8.033 88.9,24.168 26,16.378 47.2,39.094 62,66.003 z m 47.7,-456.7159 c -50.7,-40.0744 -114.3,-60.11161269 -190.4,-60.11161269 -44.1,-0.34786831 -88,7.85233269 -129,24.16421269 -37.9,14.8982 -71.1,39.7265 -96,71.8916 -25.5,33.3127 -40.1,73.6307 -41.9,115.5017 h 115.5 c 4.1,-35.944 20.6,-63.641 49.5,-81.913 30.6,-19.355 66.3,-29.199 102.5,-28.285 38.8,-0.867 76.7,11.615 107.3,35.362 30,23.567 45.4,60.694 45.4,111.375 V 393.67 c -22.7,-21.179 -49.3,-37.608 -78.4,-48.326 -30.6,-12.216 -63.2,-18.418 -96.1,-18.263 -49.1,-0.673 -97.5,12.384 -139.6,37.709 -41.2,24.708 -75.2,59.884 -98.5,101.951 -23.9,44.527 -36.1,94.43 -35.3,144.972 -0.7,50.729 11.4,100.803 35.3,145.564 47.3,87.249 138.9,141.225 238.1,140.251 64.4,-0.006 126.5,-23.712 174.5,-66.588 v 51.854 h 114.3 v -635.27 c -1.2,-85.452 -26.6,-147.329 -77.2,-187.4029"/>
|
||||
</g>
|
||||
<g transform="scale(1.28763)" id="g48">
|
||||
<path style="fill:currentColor;fill-opacity:1;fill-rule:nonzero;stroke:none" d="m 15863,310.936 h -122.1 l 214.2,271.35 -205.7,263.437 h 127.7 l 145.3,-188.251 145.9,188.251 h 122.1 L 16085.7,586.238 16300,311.494 h -127.8 L 16017.3,509.362 15863,310.936"/>
|
||||
</g>
|
||||
<path style="fill:currentColor;fill-opacity:1;fill-rule:nonzero;stroke:none" d="m 5164.87,3106.77 c -68.42,-65.51 -158.68,-98.27 -269.33,-98.27 l -457.13,-2.91 v 679.87 l 457.13,2.92 c 111.37,0 200.91,-32.76 269.33,-98.27 67.13,-62.22 104.48,-150.16 102.64,-241.67 0,-96.09 -34.21,-176.89 -102.64,-241.67 z m 646.39,708.99 c -83.71,137.58 -199.45,245.31 -346.49,325.38 -147.04,80.07 -313,119.38 -498.62,119.38 h -659.49 c -60.58,-282.42 -153.4,-556.94 -276.61,-818.18 -71.71,-151.07 -154.17,-296.78 -246.77,-436.02 V 1483.51 h 653.67 v 982.69 h 529.2 c 184.89,0 351.58,40.04 498.62,119.38 147.04,79.34 262.78,187.8 346.49,325.38 83.28,135.47 126.91,291.57 125.93,450.58 0,165.24 -41.49,316.64 -125.93,454.22"/>
|
||||
<path style="fill:#225025;fill-opacity:1;fill-rule:nonzero;stroke:none" d="M 894.902,1648.75 C 1218.1,2022.9 838.125,2662.74 609.559,2871.65 996.082,2207.06 970.605,1820.54 894.902,1648.75 Z m 31.301,-595.44 c -25.48,120.11 -75.707,361.05 -82.258,361.05 C -226.82,2054.93 -100.164,3163.55 254.332,3797.56 330.035,2999.04 1743.65,2448 919.648,1471.86 c -6.55,-12.37 37.856,-164.51 75.707,-304.27 164.505,278.8 412.005,614.37 398.895,646.39 -1013.988,2471.29 2154.64,2661.28 2813.41,4195 297.72,-1482.77 -152.14,-3777.17 -2699.12,-4360.23 -12.38,-6.55 -462.23,-798.531 -481.89,-805.078 0,12.367 -189.982,6.547 -164.505,69.879 13.832,38.578 38.578,88.809 64.054,139.759 h 0.004"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
5
frontend/src/components/icons/PowerButton.vue
Normal file
5
frontend/src/components/icons/PowerButton.vue
Normal file
|
@ -0,0 +1,5 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<use xlink:href="~/bootstrap-icons/bootstrap-icons.svg#power"/>
|
||||
</svg>
|
||||
</template>
|
5
frontend/src/components/icons/SettingsGear.vue
Normal file
5
frontend/src/components/icons/SettingsGear.vue
Normal file
|
@ -0,0 +1,5 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<use xlink:href="~/bootstrap-icons/bootstrap-icons.svg#gear"/>
|
||||
</svg>
|
||||
</template>
|
14
frontend/src/main.ts
Normal file
14
frontend/src/main.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import './assets/main.css'
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(createPinia())
|
||||
app.use(router)
|
||||
|
||||
app.mount('#app')
|
25
frontend/src/router/index.ts
Normal file
25
frontend/src/router/index.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import HomeView from '../views/HomeView.vue'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: HomeView
|
||||
},
|
||||
{
|
||||
path: '/power',
|
||||
name: 'power',
|
||||
component: () => import('@/views/PowerView.vue')
|
||||
},
|
||||
{
|
||||
path: '/scan',
|
||||
name: 'scan',
|
||||
component: () => import('@/views/ScanView.vue')
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
export default router
|
50
frontend/src/views/HomeView.vue
Normal file
50
frontend/src/views/HomeView.vue
Normal file
|
@ -0,0 +1,50 @@
|
|||
<script setup lang="ts">
|
||||
import ShadowCard from '@/components/ShadowCard.vue';
|
||||
import PaperlessLogo from '@/components/icons/PaperlessLogo.vue'
|
||||
import EnvelopeAt from '@/components/icons/EnvelopeAt.vue';
|
||||
import FolderSymlink from '@/components/icons/FolderSymlink.vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full h-full pt-10 pb-10 pl-4 pr-4 flex flex-shrink-0 flex-row justify-between items-center gap-5 text-gray-700">
|
||||
<RouterLink to="/scan?backend=paperless" class="h-2/3 max-w-xs">
|
||||
<ShadowCard class="h-full w-full">
|
||||
<div class="h-full w-full p-5 flex flex-col justify-center items-center">
|
||||
<div class="h-full w-full flex flex-col justify-center items-center gap-4">
|
||||
<PaperlessLogo class="h-16 w-auto" />
|
||||
<h1 class="text-xl select-none">Paperless-ngx</h1>
|
||||
</div>
|
||||
<div class="h-full w-full flex justify-center items-start">
|
||||
<p class="text-lg select-none">Scan your documents right into Paperless-ngx. You can also customize correspondent, document type and tags.</p>
|
||||
</div>
|
||||
</div>
|
||||
</ShadowCard>
|
||||
</RouterLink>
|
||||
<RouterLink to="/scan?backend=mail" class="h-2/3 max-w-xs">
|
||||
<ShadowCard class="h-full w-full">
|
||||
<div class="h-full w-full p-5 flex flex-col justify-center items-center">
|
||||
<div class="h-full w-full flex flex-col justify-center items-center gap-4">
|
||||
<EnvelopeAt class="h-16 w-auto" />
|
||||
<h1 class="text-xl select-none">Scan2Mail</h1>
|
||||
</div>
|
||||
<div class="h-full w-full flex justify-center items-start">
|
||||
<p class="text-lg select-none">Scan your documents and send them as a PDF file attachment via email.</p>
|
||||
</div>
|
||||
</div>
|
||||
</ShadowCard>
|
||||
</RouterLink>
|
||||
<RouterLink to="/scan?backend=storage-share" class="h-2/3 max-w-xs">
|
||||
<ShadowCard class="h-full w-full">
|
||||
<div class="h-full w-full p-5 flex flex-col justify-center items-center">
|
||||
<div class="h-full w-full flex flex-col justify-center items-center gap-4">
|
||||
<FolderSymlink class="h-16 w-auto" />
|
||||
<h1 class="text-xl select-none">Network Share</h1>
|
||||
</div>
|
||||
<div class="h-full w-full flex justify-center items-start">
|
||||
<p class="text-lg select-none">Scan your documents and save them as PDF files to a network storage share.</p>
|
||||
</div>
|
||||
</div>
|
||||
</ShadowCard>
|
||||
</RouterLink>
|
||||
</div>
|
||||
</template>
|
47
frontend/src/views/PowerView.vue
Normal file
47
frontend/src/views/PowerView.vue
Normal file
|
@ -0,0 +1,47 @@
|
|||
<script setup lang="ts">
|
||||
import ShadowCard from '@/components/ShadowCard.vue';
|
||||
import PowerButton from '@/components/icons/PowerButton.vue';
|
||||
import ArrowClockwise from '@/components/icons/ArrowClockwise.vue';
|
||||
import ModalComponent from '@/components/ModalComponent.vue';
|
||||
import axios from 'axios';
|
||||
import {ref} from 'vue';
|
||||
|
||||
const showModal = ref(false);
|
||||
const action = ref("");
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full h-full pt-10 pb-10 pl-4 pr-4 flex flex-shrink-0 flex-row justify-center items-center gap-20 text-gray-700">
|
||||
<ShadowCard @click="action='shutdown';showModal=true" class="h-1/3 w-1/5">
|
||||
<div class="w-full h-full flex flex-col justify-center items-center">
|
||||
<PowerButton class="w-12 h-12 text-red-700" />
|
||||
<h1 class="mt-5 text-xl font-semibold">Shutdown</h1>
|
||||
</div>
|
||||
</ShadowCard>
|
||||
<ShadowCard @click="action='restart';showModal=true" class="h-1/3 w-1/5">
|
||||
<div class="w-full h-full flex flex-col justify-center items-center">
|
||||
<ArrowClockwise class="w-12 h-12 text-orange-400" />
|
||||
<h1 class="mt-5 text-xl font-semibold">Restart</h1>
|
||||
</div>
|
||||
</ShadowCard>
|
||||
<ModalComponent v-if="showModal">
|
||||
<div class="flex flex-col p-10">
|
||||
<h1 class="font-semibold text-lg mb-5">Do you really want to {{ action }}?</h1>
|
||||
<div class="flex flex-row justify-between items-center">
|
||||
<button @click="confirm(action)" class="h-12 w-20 rounded-md bg-green-500 text-white font-semibold">Yes</button>
|
||||
<button @click="showModal=false" class="h-12 w-20 rounded-md bg-red-500 text-white font-semibold">No</button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalComponent>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
function confirm(action: String) {
|
||||
if (action == "shutdown") {
|
||||
axios.post('/api/power/shutdown', {});
|
||||
} else {
|
||||
axios.post('/api/power/restart', {});
|
||||
}
|
||||
}
|
||||
</script>
|
44
frontend/src/views/ScanView.vue
Normal file
44
frontend/src/views/ScanView.vue
Normal file
|
@ -0,0 +1,44 @@
|
|||
<script setup lang="ts">
|
||||
import ScannedPage from '@/components/ScannedPage.vue';
|
||||
import { useRoute } from 'vue-router'
|
||||
import { ref } from 'vue';
|
||||
import axios from 'axios';
|
||||
|
||||
const route = useRoute()
|
||||
let backend = route.query.backend;
|
||||
|
||||
const data = ref({
|
||||
pages: new Array<any>(),
|
||||
status: "idle"
|
||||
})
|
||||
|
||||
function getUpdate() {
|
||||
axios.get('/api/scan/status')
|
||||
.then(function (response) {
|
||||
data.value = response.data
|
||||
if (response.data.status === "running") {
|
||||
setTimeout(getUpdate, 1000)
|
||||
}
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error);
|
||||
})
|
||||
}
|
||||
axios.post('/api/scan')
|
||||
.then(function (response) {
|
||||
data.value.status = "running"
|
||||
setTimeout(getUpdate, 2000)
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<dev class="w-full h-full flex flex-col">
|
||||
<div class="w-full h-full p-2 flex flex-row flex-wrap overflow-auto">
|
||||
<ScannedPage v-for="page in data.pages" :key="page.filename" class="w-1/5 h-1/2" :imgUrl="'/img/' + page.filename" />
|
||||
<ScannedPage v-if="data.status==='running'" class="w-1/5 h-1/2" />
|
||||
</div>
|
||||
<div class="w-full h-28 p-4 flex">
|
||||
|
||||
</div>
|
||||
</dev>
|
||||
</template>
|
9
frontend/tailwind.config.js
Normal file
9
frontend/tailwind.config.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
12
frontend/tsconfig.app.json
Normal file
12
frontend/tsconfig.app.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
||||
"exclude": ["src/**/__tests__/*"],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
}
|
11
frontend/tsconfig.json
Normal file
11
frontend/tsconfig.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.node.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
}
|
||||
]
|
||||
}
|
16
frontend/tsconfig.node.json
Normal file
16
frontend/tsconfig.node.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"extends": "@tsconfig/node18/tsconfig.json",
|
||||
"include": [
|
||||
"vite.config.*",
|
||||
"vitest.config.*",
|
||||
"cypress.config.*",
|
||||
"nightwatch.conf.*",
|
||||
"playwright.config.*"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"types": ["node"]
|
||||
}
|
||||
}
|
16
frontend/vite.config.ts
Normal file
16
frontend/vite.config.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { fileURLToPath, URL } from 'node:url'
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
}
|
||||
}
|
||||
})
|
2828
frontend/yarn.lock
Normal file
2828
frontend/yarn.lock
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue