Compare commits
34 commits
ef31e0de40
...
41fef540bf
Author | SHA1 | Date | |
---|---|---|---|
41fef540bf | |||
1571c30c38 | |||
05024c8065 | |||
5a89cdb205 | |||
2d034531af | |||
5197923e3b | |||
89a5f95b71 | |||
78ec0fc074 | |||
ba60aace6b | |||
a7bb641774 | |||
c3364cd419 | |||
2516b7ad5d | |||
0220dc0fe2 | |||
f8291f593d | |||
812b96302a | |||
9d3a3062e8 | |||
2f21a6f6b7 | |||
a787e3c648 | |||
415c1e4407 | |||
8a83ef15e9 | |||
1752eff2d9 | |||
c2e85e24ec | |||
46cae0018b | |||
f1b94962e3 | |||
57d88558cc | |||
840de1ac0e | |||
5d4edab2a4 | |||
79457ee9c3 | |||
d45aa32900 | |||
e1b0c60b65 | |||
2f1c5277e0 | |||
47df04fbfc | |||
72059fd95e | |||
9ae1fbbb06 |
10
.docker/apache2/soshot.conf
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<VirtualHost *:80>
|
||||||
|
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||||
|
ServerName sohsot.local
|
||||||
|
DocumentRoot /var/www/public/
|
||||||
|
<Directory "/var/www/public/">
|
||||||
|
Require all granted
|
||||||
|
AllowOverride All
|
||||||
|
Options -Indexes
|
||||||
|
</Directory>
|
||||||
|
</VirtualHost>
|
3
.docker/start.sh
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#/bin/bash
|
||||||
|
cron
|
||||||
|
/usr/sbin/apache2ctl -DFOREGROUND
|
9
.gitignore
vendored
|
@ -1,4 +1,5 @@
|
||||||
cache/*
|
cache/
|
||||||
!cache/index.html
|
composer.lock
|
||||||
.project
|
datas/config.json
|
||||||
.settings
|
datas/soshot.sqlite
|
||||||
|
vendor/
|
|
@ -1,5 +0,0 @@
|
||||||
AddDefaultCharset UTF-8
|
|
||||||
Options -Indexes
|
|
||||||
DirectoryIndex index.php index.html
|
|
||||||
FileETag none
|
|
||||||
SetOutputFilter DEFLATE
|
|
5
DockerRun
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
docker run -v soshot_datas:/var/www/datas -v soshot_cache_img:/var/www/cache/img -e TZ=UTC -p 8080:80 --name soshot soshot:0.2.0
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
docker run -v soshot_datas:/var/www/datas soshot --name soshot soshot
|
40
Dockerfile
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
FROM ubuntu/apache2
|
||||||
|
|
||||||
|
MAINTAINER Knah Tsaeb <knah-tsaeb_soshot@knah-tsaeb.org>
|
||||||
|
|
||||||
|
LABEL version="0.2.0"
|
||||||
|
LABEL description="Apache 2 / PHP / SoShot"
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
RUN apt-get -y update && apt-get install -y php php-gd php-json php-sqlite3 php-mbstring php-xml php-curl wget gnupg git composer php-imagick sudo cron imagemagick && apt-get clean && apt-get autoclean && apt-get autoremove
|
||||||
|
|
||||||
|
RUN wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb && apt -y install ./google-chrome-stable_current_amd64.deb && apt-get -y update && apt-get install -y google-chrome-stable && apt-get clean && rm google-chrome-stable_current_amd64.deb && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN rm -r /var/www/ && mkdir /var/www/
|
||||||
|
|
||||||
|
WORKDIR /var/www/
|
||||||
|
RUN git clone https://forge.leslibres.org/Knah-Tsaeb/Soshot.git --branch dev --single-branch --depth 1 .
|
||||||
|
RUN composer install --no-dev && chown -R www-data:www-data /var/www/
|
||||||
|
|
||||||
|
COPY .docker/start.sh /usr/bin/start.sh
|
||||||
|
RUN chmod +x /usr/bin/start.sh
|
||||||
|
|
||||||
|
WORKDIR /
|
||||||
|
|
||||||
|
COPY .docker/apache2/soshot.conf /etc/apache2/sites-available/soshot.conf
|
||||||
|
RUN a2dissite 000-default.conf && a2ensite soshot.conf && a2enmod rewrite
|
||||||
|
|
||||||
|
RUN echo '*/4 * * * * www-data php /var/www/bin/thumbShoter.php' > /etc/cron.d/soshot && chmod 0644 /etc/cron.d/soshot && crontab /etc/cron.d/soshot
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
VOLUME soshot_datas
|
||||||
|
|
||||||
|
WORKDIR /var/www/
|
||||||
|
|
||||||
|
ENTRYPOINT "start.sh"
|
||||||
|
|
||||||
|
# Build image
|
||||||
|
# docker build -t soshot:0.2.0 .
|
||||||
|
# Run container
|
||||||
|
# docker run -v soshot_datas:/var/www/datas -v soshot_cache_img:/var/www/cache/img -e TZ=UTC -p 8080:80 --name soshot soshot:0.2.0
|
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>.
|
176
README.md
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
# !!!! Be careful !!!!
|
||||||
|
## Use at your own risk
|
||||||
|
|
||||||
|
|
||||||
|
Soshot make webshot of internet page with headless machine.
|
||||||
|
|
||||||
|
|
||||||
|
## Ideas
|
||||||
|
|
||||||
|
- If webshot exist in desired size, serve them, if not, verif if complete exist, if exist, crop and resize, save and serve
|
||||||
|
- If webshot not exist get information of page and add in the queue
|
||||||
|
- Cron job take a webshot of complete page and save them (1920 x complete lenth)
|
||||||
|
- Crop image and resize desired format (1920x1080, 1280x720, 160x90 ...), save and serve
|
||||||
|
|
||||||
|
SoShot always make complete webshot (1920x page lenght) + first demande size.
|
||||||
|
|
||||||
|
Size use 16/9 ratio :
|
||||||
|
|
||||||
|
|
||||||
|
- fav = define by config (default 48px)
|
||||||
|
- og = define by the website himself (no resize or crop)
|
||||||
|
- thumb = 160x90
|
||||||
|
- hd = 1280x720
|
||||||
|
- full = 1920x1080
|
||||||
|
- complete = 1920xpage height
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- PHP 8.*
|
||||||
|
- Sqlite3
|
||||||
|
- PDO
|
||||||
|
- Composer
|
||||||
|
- Imagick
|
||||||
|
- PHP-GD
|
||||||
|
- CRON
|
||||||
|
- Chrome / Chromium
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Make directory on your web server and go in
|
||||||
|
|
||||||
|
`
|
||||||
|
mkdir /var/www/soshot && cd /var/www/soshot
|
||||||
|
`
|
||||||
|
|
||||||
|
Clone git repo
|
||||||
|
|
||||||
|
`
|
||||||
|
git clone https://forge.leslibres.org/Knah-Tsaeb/Soshot.git .
|
||||||
|
`
|
||||||
|
|
||||||
|
Install dependencies
|
||||||
|
|
||||||
|
`
|
||||||
|
composer install --no-dev
|
||||||
|
`
|
||||||
|
|
||||||
|
Add cron task
|
||||||
|
|
||||||
|
|
||||||
|
`
|
||||||
|
sudo crontab -u www-data -e
|
||||||
|
`
|
||||||
|
|
||||||
|
|
||||||
|
`
|
||||||
|
php /var/www/soshot/bin/thumbShoter.php
|
||||||
|
`
|
||||||
|
|
||||||
|
Configure your web server to serve in "public" directory
|
||||||
|
|
||||||
|
Go to web page
|
||||||
|
https://shoshot.your-domain.tld
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
You can use a web interface (if option "use web gui" is set to true), https://shoshot.your-domain.tld/backend?page=settings
|
||||||
|
|
||||||
|
Or you can modify config file /datas/config.json.
|
||||||
|
|
||||||
|
#### Accept only request from 127.0.0.1
|
||||||
|
|
||||||
|
Default : true
|
||||||
|
|
||||||
|
ShoShot only work for local machine, return 404 if use with remote machine.
|
||||||
|
|
||||||
|
#### Use web gui
|
||||||
|
|
||||||
|
Default : false
|
||||||
|
|
||||||
|
Soshot only work with endpoint /api. Return 404 if endpoint is not /api
|
||||||
|
|
||||||
|
#### Use log
|
||||||
|
|
||||||
|
Default : true
|
||||||
|
|
||||||
|
Not implemented yet.
|
||||||
|
|
||||||
|
#### Always make PDF
|
||||||
|
|
||||||
|
Default : false
|
||||||
|
|
||||||
|
If true, SoShot make a complete webshot and PDF of page.
|
||||||
|
|
||||||
|
#### Favicon size
|
||||||
|
|
||||||
|
Default : 48
|
||||||
|
|
||||||
|
Define size of favicon.
|
||||||
|
|
||||||
|
#### Permit type of webshot
|
||||||
|
|
||||||
|
Default : none
|
||||||
|
|
||||||
|
Select authorized size or type of webshot.
|
||||||
|
|
||||||
|
#### Cache expiration (for web GUI) in hour
|
||||||
|
|
||||||
|
Default : 12
|
||||||
|
|
||||||
|
Not implemented yet.
|
||||||
|
|
||||||
|
#### Maximum work per batch
|
||||||
|
|
||||||
|
Default : 5
|
||||||
|
|
||||||
|
Foreach cron task as launch, SoShot make 5 webshot.
|
||||||
|
|
||||||
|
#### Api key
|
||||||
|
|
||||||
|
Default : random string, lenght 12
|
||||||
|
|
||||||
|
!!! DANGER !!!
|
||||||
|
|
||||||
|
If you change key, all previous generation will be invalid. Soshot re make all previous generation.
|
||||||
|
|
||||||
|
#### Password
|
||||||
|
|
||||||
|
Default : null
|
||||||
|
|
||||||
|
Password for admin interface. No min or max character.
|
||||||
|
|
||||||
|
#### Chrome path
|
||||||
|
|
||||||
|
Default : empty
|
||||||
|
|
||||||
|
If path of Chrome/Chromium is not in PATH, you can define here.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
For generating webshot the entry point is https://shoshot.your-domain.tld/api?
|
||||||
|
|
||||||
|
You need to define 3 parameters :
|
||||||
|
|
||||||
|
- type (fav, og, pdf, thumb, hd, full, complete)
|
||||||
|
- hmac (hash_hmac('sha1', url of page, your Api key);)
|
||||||
|
- url (url of page encode with url_encode)
|
||||||
|
|
||||||
|
|
||||||
|
Example : https://shoshot.your-domain.tld/api?type=fav&hmac=44a2b3f1bc43895c23779f4e71558e9976a4daf2&url=https%3A%2F%2Ffr.wikipedia.org%2Fwiki%2FLogiciel_libre
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
GNU General Public License v3.0
|
||||||
|
|
||||||
|
See LICENSE file for more detail.
|
||||||
|
|
||||||
|
**Thanks**
|
||||||
|
|
||||||
|
- [W3c](https://www.w3schools.com/w3css/) for w3.css
|
||||||
|
- [Fork Awesome](https://forkaweso.me/Fork-Awesome/) for icon
|
||||||
|
- [embed/embed](https://github.com/oscarotero/Embed) for embed (fav and og)
|
||||||
|
- [hassankhan/config](https://github.com/hassankhan/config) for config (config mananger)
|
||||||
|
- [chrome-php/chrome](https://github.com/chrome-php/chrome) for chrome-php (wrapper for chrome dev-tool)
|
||||||
|
- [stefangabos/zebra_image](https://github.com/stefangabos/Zebra_Image) for zebra-image (resize and manipulate image)
|
||||||
|
- [guzzlehttp/psr7](https://github.com/guzzle/psr7) for psr7 (PSR7 implement)
|
299
app/Controllers/Backend.php
Normal file
|
@ -0,0 +1,299 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controllers;
|
||||||
|
|
||||||
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
use App\DataBase\DataBase;
|
||||||
|
use App\Utils\Page;
|
||||||
|
use App\Utils\ShowImg;
|
||||||
|
use Noodlehaus\Config;
|
||||||
|
|
||||||
|
class Backend {
|
||||||
|
|
||||||
|
private $start = 0;
|
||||||
|
private $end = 15;
|
||||||
|
private $max = 15;
|
||||||
|
private $runningJob = false;
|
||||||
|
private $page = 'infos';
|
||||||
|
private $params;
|
||||||
|
private $conf;
|
||||||
|
private $title = 'Settings';
|
||||||
|
private $passwordRequired = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the index action for the controller.
|
||||||
|
*
|
||||||
|
* This method performs various actions based on the provided parameters and
|
||||||
|
* configuration. It checks if the user is logged in, sets the page to display,
|
||||||
|
* checks if a job is running, and handles displaying images. It sets the
|
||||||
|
* configuration and parameters for the object. If the page is set to 'settings',
|
||||||
|
* it handles first run installation and option settings. Finally, it generates
|
||||||
|
* the HTML content for the page by requiring the navigation, infos or settings,
|
||||||
|
* and footer templates.
|
||||||
|
*
|
||||||
|
* @param object $params The parameters object.
|
||||||
|
* @param object $conf The configuration object.
|
||||||
|
*
|
||||||
|
* @return string The generated HTML content for the page.
|
||||||
|
*/
|
||||||
|
public function index(object $params, object $conf): string {
|
||||||
|
if (isset($params->loginPassword)) {
|
||||||
|
if (!Login::login($params->loginPassword, $conf->password)) {
|
||||||
|
$content = $this->showLogin();
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Login::isLogin()) {
|
||||||
|
$content = $this->showLogin();
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($params->page)) {
|
||||||
|
$this->page = $params->page;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_exists(__DIR__ . '/../../cache/tmp/chrome-php-socket')) {
|
||||||
|
$this->runningJob = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($params->showImg)) {
|
||||||
|
$img = new ShowImg($params->type, $params->showImg, $conf->fileFormat);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->conf = $conf;
|
||||||
|
$this->params = $params;
|
||||||
|
|
||||||
|
if ($this->page === 'settings') {
|
||||||
|
if (isset($_SESSION['firstRun']) && $_SESSION['firstRun'] === 1) {
|
||||||
|
$conf->webPage = false;
|
||||||
|
$this->title = 'Install';
|
||||||
|
$this->conf->key = bin2hex(random_bytes(12));
|
||||||
|
$this->passwordRequired = "required";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->params->token) && Page::verifToken($this->params->token)) {
|
||||||
|
$this->conf = $this->setOption($params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require_once __DIR__ . '/../../tpl/nav.php';
|
||||||
|
|
||||||
|
if ($this->page === 'infos') {
|
||||||
|
$this->showInfos();
|
||||||
|
} else {
|
||||||
|
$this->showSettings();
|
||||||
|
}
|
||||||
|
require_once __DIR__ . '/../../tpl/footer.php';
|
||||||
|
$content = ob_get_contents();
|
||||||
|
ob_end_clean();
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays information about the generated data.
|
||||||
|
*
|
||||||
|
* This method sets the start and end index for the data to display based on the
|
||||||
|
* provided parameters. It retrieves the data list, total count, error count, and
|
||||||
|
* queue count from the `DataBase` class. It calculates the start, maximum, next,
|
||||||
|
* and previous index values for pagination. It then requires the 'infos.php'
|
||||||
|
* template to display the information.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function showInfos() {
|
||||||
|
if (!empty($this->params->start)) {
|
||||||
|
$this->start = $this->params->start;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($this->params->end)) {
|
||||||
|
$this->end = $this->max + $this->start;
|
||||||
|
}
|
||||||
|
|
||||||
|
$conx = new DataBase();
|
||||||
|
$genList = $conx->getList($this->start, $this->end);
|
||||||
|
$total = $conx->getTotal();
|
||||||
|
$inError = $conx->getInError();
|
||||||
|
$inQueue = $this->getInQueue();
|
||||||
|
$start = $this->start;
|
||||||
|
$max = $this->max;
|
||||||
|
$next = $start + $max;
|
||||||
|
$previous = $start - ($max);
|
||||||
|
|
||||||
|
if (count($genList) < $this->max) {
|
||||||
|
$next = $start;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($previous < 0) {
|
||||||
|
$previous = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
require __DIR__ . '/../../tpl/infos.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the settings page.
|
||||||
|
*
|
||||||
|
* This method sets the start and end index for the data to display based on the
|
||||||
|
* provided parameters. It retrieves the data list and total count from the
|
||||||
|
* `DataBase` class. It calculates the start and maximum index values for
|
||||||
|
* pagination. It generates a CSRF token using the `Page::genToken` method. It
|
||||||
|
* then requires the 'settings.php' template to display the settings page.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function showSettings() {
|
||||||
|
if (!empty($this->params->start)) {
|
||||||
|
$this->start = $this->params->start;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($this->params->end)) {
|
||||||
|
$this->end = $this->max + $this->start;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->start < 0) {
|
||||||
|
$this->start = 0;
|
||||||
|
}
|
||||||
|
$conx = new DataBase();
|
||||||
|
$genList = $conx->getList($this->start, $this->end);
|
||||||
|
$total = $conx->getTotal();
|
||||||
|
$start = $this->start;
|
||||||
|
$max = $this->max;
|
||||||
|
$token = Page::genToken();
|
||||||
|
|
||||||
|
require __DIR__ . '/../../tpl/settings.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets and saves user configuration options.
|
||||||
|
*
|
||||||
|
* This method checks if the configuration file exists and creates an empty one if
|
||||||
|
* it doesn't. It then loads the user configuration from the file using the `Config`
|
||||||
|
* class. It sets various configuration options based on the provided `$post`
|
||||||
|
* parameter, such as server settings, logging, PDF generation, icon size, cache
|
||||||
|
* expiration, maximum generations per batch, permit types, file format, password,
|
||||||
|
* key, and Chrome path. It saves the updated configuration to the file using the
|
||||||
|
* `Config` class. Finally, it loads the updated configuration from the file and
|
||||||
|
* returns it as an object.
|
||||||
|
*
|
||||||
|
* @param object $post The POST data containing the configuration options.
|
||||||
|
*
|
||||||
|
* @return object The updated configuration object.
|
||||||
|
*/
|
||||||
|
private function setOption(object $post): object {
|
||||||
|
if (!file_exists(__DIR__ . '/../../datas/config.json')) {
|
||||||
|
file_put_contents(__DIR__ . '/../../datas/config.json', json_encode([]));
|
||||||
|
unset($_SESSION['login']);
|
||||||
|
unset($_SESSION['firstRun']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$userConfig = __DIR__ . '/../../datas/config.json';
|
||||||
|
$uConfig = new Config($userConfig);
|
||||||
|
|
||||||
|
if (empty($post->onlyLocalServer)) {
|
||||||
|
$uConfig['onlyLocalServer'] = false;
|
||||||
|
} else {
|
||||||
|
$uConfig['onlyLocalServer'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($post->webPage)) {
|
||||||
|
$uConfig['webPage'] = false;
|
||||||
|
} else {
|
||||||
|
$uConfig['webPage'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($post->log)) {
|
||||||
|
$uConfig['log'] = false;
|
||||||
|
} else {
|
||||||
|
$uConfig['log'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($post->alwaysMakePdf)) {
|
||||||
|
$uConfig['alwaysMakePdf'] = false;
|
||||||
|
} else {
|
||||||
|
$uConfig['alwaysMakePdf'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($post->icoSize)) {
|
||||||
|
$uConfig['icoSize'] = (int)$post->icoSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($post->expireCache)) {
|
||||||
|
$uConfig['expireCache'] = (int)$post->expireCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($post->maxGenPerBatch)) {
|
||||||
|
$uConfig['maxGenPerBatch'] = (int)$post->maxGenPerBatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($post->permitType)) {
|
||||||
|
$uConfig['permitType'] = $post->permitType;
|
||||||
|
} else {
|
||||||
|
$uConfig['permitType'] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($post->fileFormat)) {
|
||||||
|
$uConfig['fileFormat'] = $post->fileFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($post->password)) {
|
||||||
|
$uConfig['password'] = password_hash($post->password, PASSWORD_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($post->key)) {
|
||||||
|
$uConfig['key'] = $post->key;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($post->chromePath)) {
|
||||||
|
$uConfig['chromePath'] = $post->chromePath;
|
||||||
|
} else {
|
||||||
|
$uConfig['chromePath'] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$uConfig->toFile($userConfig);
|
||||||
|
|
||||||
|
$newConfig = new Config($userConfig);
|
||||||
|
return (object)$newConfig->all();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the number of files in the queue directory.
|
||||||
|
*
|
||||||
|
* This method uses the `glob` function to search for JSON files in the queue
|
||||||
|
* directory. If any files are found, it returns the count of files. Otherwise,
|
||||||
|
* it returns 0.
|
||||||
|
*
|
||||||
|
* @return int The number of files in the queue directory.
|
||||||
|
*/
|
||||||
|
private function getInQueue(): int {
|
||||||
|
if (glob(__DIR__ . '/../../cache/queue/*.json') != false) {
|
||||||
|
$fileCount = count(glob(__DIR__ . '/../../cache/queue/*.json'));
|
||||||
|
} else {
|
||||||
|
$fileCount = 0;
|
||||||
|
}
|
||||||
|
return $fileCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the login page.
|
||||||
|
*
|
||||||
|
* This method generates a CSRF token using the `Page::genToken` method. It then
|
||||||
|
* requires the 'login.php' template to display the login page. Finally, it returns
|
||||||
|
* the generated HTML content as a string.
|
||||||
|
*
|
||||||
|
* @return string The generated HTML content for the login page.
|
||||||
|
*/
|
||||||
|
private function showLogin(): string {
|
||||||
|
ob_start();
|
||||||
|
$token = Page::genToken();
|
||||||
|
require_once __DIR__ . '/../../tpl/login.php';
|
||||||
|
$content = ob_get_contents();
|
||||||
|
ob_end_clean();
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
}
|
8
app/Controllers/GenHmac.php
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
namespace App\Controllers;
|
||||||
|
|
||||||
|
class GenHmac {
|
||||||
|
function index($params, $conf) {
|
||||||
|
echo hash_hmac('sha1', $params->url, $conf['key']);
|
||||||
|
}
|
||||||
|
}
|
16
app/Controllers/Home.php
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controllers;
|
||||||
|
|
||||||
|
use App\Utils\Hmac;
|
||||||
|
|
||||||
|
class Home {
|
||||||
|
public function index() {
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../../tpl/home.php';
|
||||||
|
$content = ob_get_contents();
|
||||||
|
ob_end_clean();
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
20
app/Controllers/Login.php
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controllers;
|
||||||
|
|
||||||
|
class Login {
|
||||||
|
static function isLogin() {
|
||||||
|
if (isset($_SESSION['login']) && $_SESSION['login'] === 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function login($password, $hash) {
|
||||||
|
if (password_verify($password, $hash)) {
|
||||||
|
$_SESSION['login'] = 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
13
app/Controllers/Logout.php
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controllers;
|
||||||
|
|
||||||
|
class Logout {
|
||||||
|
|
||||||
|
public function index() {
|
||||||
|
session_start();
|
||||||
|
session_destroy();
|
||||||
|
header("Location:/");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
38
app/Controllers/Result.php
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controllers;
|
||||||
|
|
||||||
|
use App\Soshot\GetThumb;
|
||||||
|
use App\Soshot\GetFav;
|
||||||
|
use App\Soshot\GetOg;
|
||||||
|
use App\Utils\Error;
|
||||||
|
|
||||||
|
class Result {
|
||||||
|
|
||||||
|
public function index($params, $conf) {
|
||||||
|
if (
|
||||||
|
!isset($params->type) ||
|
||||||
|
!isset($params->hmac) ||
|
||||||
|
!isset($params->url)
|
||||||
|
) {
|
||||||
|
$error = new Error();
|
||||||
|
$error->index((object)['status' => 400, 'message' => 'Missing parameters']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_array($params->type, $conf->permitType)) {
|
||||||
|
$error = new Error();
|
||||||
|
$error->index((object)['status' => 400, 'message' => 'Wrong type']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($params->type === 'fav') {
|
||||||
|
$favicon = new GetFav($params, $conf);
|
||||||
|
$favicon->show();
|
||||||
|
} elseif ($params->type === 'og') {
|
||||||
|
$favicon = new GetOg($params, $conf);
|
||||||
|
$favicon->show();
|
||||||
|
} else {
|
||||||
|
$result = new GetThumb($params, $conf);
|
||||||
|
$result->show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
273
app/DataBase/DataBase.php
Normal file
|
@ -0,0 +1,273 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\DataBase;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use PDO;
|
||||||
|
|
||||||
|
use function App\Soshot\n_print as SoshotN_print;
|
||||||
|
|
||||||
|
if (!function_exists('n_print')) {
|
||||||
|
function n_print($data, $name = '') {
|
||||||
|
print_r($data, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DataBase {
|
||||||
|
|
||||||
|
private $dataBase = __DIR__ . '/../../datas/soshot.sqlite';
|
||||||
|
private $db;
|
||||||
|
private $params = [
|
||||||
|
'id' => '',
|
||||||
|
'url' => '',
|
||||||
|
'complete' => '',
|
||||||
|
'full' => '',
|
||||||
|
'hd' => '',
|
||||||
|
'nhd' => '',
|
||||||
|
'thumb' => '',
|
||||||
|
'fav' => '',
|
||||||
|
'og' => '',
|
||||||
|
'pdf' => '',
|
||||||
|
'created' => ''
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new instance of the DataBase class.
|
||||||
|
*
|
||||||
|
* This method creates a new SQLite database connection and sets the default fetch
|
||||||
|
* mode to PDO::FETCH_ASSOC. It also sets the error mode to PDO::ERRMODE_EXCEPTION.
|
||||||
|
* If the database file does not exist, it creates a new table named 'soshot' with
|
||||||
|
* the specified schema. If the $params parameter is not empty, the setParams()
|
||||||
|
* method is called to set the object's properties.
|
||||||
|
*
|
||||||
|
* @param object $params An associative array of parameters to set the object's
|
||||||
|
* properties.
|
||||||
|
*
|
||||||
|
* @return DataBase The constructed DataBase object.
|
||||||
|
*/
|
||||||
|
function __construct($params = null) {
|
||||||
|
try {
|
||||||
|
if (!file_exists($this->dataBase)) {
|
||||||
|
$this->db = new PDO('sqlite:' . $this->dataBase);
|
||||||
|
$this->db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
|
||||||
|
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION, PDO::ERRMODE_WARNING); // ERRMODE_WARNING | ERRMODE_EXCEPTION | ERRMODE_SILENT
|
||||||
|
|
||||||
|
$this->db->query("CREATE TABLE IF NOT EXISTS soshot (
|
||||||
|
id string PRIMARY KEY NOT NULL,
|
||||||
|
url text,
|
||||||
|
complete tinyint,
|
||||||
|
full tinyint,
|
||||||
|
hd tinyint,
|
||||||
|
nhd tinuyint,
|
||||||
|
thumb tinyint,
|
||||||
|
fav tinyint,
|
||||||
|
og tinyint,
|
||||||
|
pdf tinyint,
|
||||||
|
created DATETIME
|
||||||
|
);");
|
||||||
|
} else {
|
||||||
|
$this->db = new PDO('sqlite:' . $this->dataBase);
|
||||||
|
$this->db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
|
||||||
|
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // ERRMODE_WARNING | ERRMODE_EXCEPTION | ERRMODE_SILENT
|
||||||
|
|
||||||
|
$this->updateTable();
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo $e->getMessage();
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($params)) {
|
||||||
|
$this->setParams($params);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function updateTable() {
|
||||||
|
$stmt = $this->db->prepare("SELECT * FROM soshot LIMIT 1;");
|
||||||
|
$stmt->execute();
|
||||||
|
$existingColumns = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (empty($existingColumns)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$existingColumns = array_keys($existingColumns);
|
||||||
|
|
||||||
|
$newColumns = array_diff(array_keys($this->params), $existingColumns);
|
||||||
|
|
||||||
|
foreach ($newColumns as $column) {
|
||||||
|
$sql = "ALTER TABLE soshot ADD `{$column}` tinyint";
|
||||||
|
$this->db->exec($sql);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the parameters for the database query.
|
||||||
|
*
|
||||||
|
* This method takes an object containing the parameters for the database query and
|
||||||
|
* sets the corresponding properties of the object.
|
||||||
|
*
|
||||||
|
* @param object $params An object containing the parameters for the database query.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setParams(object $params) {
|
||||||
|
$this->params = (object)$this->params;
|
||||||
|
$this->params->id = $params->hmac;
|
||||||
|
$this->params->url = $params->url;
|
||||||
|
$this->params->type = $params->type;
|
||||||
|
$this->params->created = date("Y-m-d H:i:s");
|
||||||
|
$this->params = $this->params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds or updates a record in the database.
|
||||||
|
*
|
||||||
|
* This method takes an update value and an optional type parameter. If the type
|
||||||
|
* parameter is not empty, it sets the type property of the object. It then checks
|
||||||
|
* if a record with the same ID already exists in the database using the testExit
|
||||||
|
* method. If a record exists, it calls the update method to update the existing
|
||||||
|
* record. If no record exists, it calls the insert method to insert a new record.
|
||||||
|
*
|
||||||
|
* @param int $update The update value to be inserted or updated in the database.
|
||||||
|
* @param string $type The type of the update.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addUpdate(int $update, string $type = '') {
|
||||||
|
if (!empty($type)) {
|
||||||
|
$this->params->type = $type;
|
||||||
|
}
|
||||||
|
if ($this->testExit($this->params->id)) {
|
||||||
|
$this->update($update);
|
||||||
|
} else {
|
||||||
|
$this->insert($update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts a new record into the database.
|
||||||
|
*
|
||||||
|
* This method prepares an SQL INSERT statement using the provided parameters, and
|
||||||
|
* executes it to insert a new record into the "soshot" table in the database.
|
||||||
|
*
|
||||||
|
* @param int $update The value to insert into the type column of the new record.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function insert(int $update) {
|
||||||
|
$stmt = $this->db->prepare("INSERT INTO soshot (id, url, " . $this->params->type . ", created) VALUES
|
||||||
|
(:id, :url, :" . $this->params->type . ", :created)");
|
||||||
|
//$stmt->debugDumpParams();
|
||||||
|
|
||||||
|
$result = $stmt->execute(array(
|
||||||
|
'id' => $this->params->id,
|
||||||
|
'url' => $this->params->url,
|
||||||
|
$this->params->type => $update,
|
||||||
|
'created' => $this->params->created
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a record in the database.
|
||||||
|
*
|
||||||
|
* This method prepares an SQL UPDATE statement using the provided parameters, and
|
||||||
|
* executes it to update a record in the "soshot" table in the database.
|
||||||
|
*
|
||||||
|
* @param string $update The value to update in the type column of the record.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function update(string $update) {
|
||||||
|
$stmt = $this->db->prepare("UPDATE soshot
|
||||||
|
SET " . $this->params->type . "=:" . $this->params->type . "
|
||||||
|
WHERE id=:id;");
|
||||||
|
|
||||||
|
$result = $stmt->execute([
|
||||||
|
':' . $this->params->type => $update,
|
||||||
|
':id' => $this->params->id
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a record with the given ID exists in the database.
|
||||||
|
*
|
||||||
|
* This method prepares an SQL SELECT statement using the provided ID, and executes
|
||||||
|
* it to retrieve the corresponding record from the "soshot" table in the database.
|
||||||
|
* If a record is found, the method returns true. Otherwise, it returns false.
|
||||||
|
*
|
||||||
|
* @param string $id The ID of the record to check.
|
||||||
|
*
|
||||||
|
* @return bool True if a record with the given ID exists in the database, false otherwise.
|
||||||
|
*/
|
||||||
|
private function testExit(string $id): bool {
|
||||||
|
$stmt = $this->db->prepare("SELECT id FROM soshot WHERE id=:id LIMIT 1;");
|
||||||
|
$stmt->execute(array(':id' => $id));
|
||||||
|
$result = $stmt->fetchAll(PDO::FETCH_OBJ);
|
||||||
|
if (!empty($result)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the total number of records in the "soshot" table.
|
||||||
|
*
|
||||||
|
* This method prepares an SQL SELECT statement to retrieve the total number of
|
||||||
|
* records in the "soshot" table, executes the statement, and returns the result.
|
||||||
|
*
|
||||||
|
* @return int The total number of records in the "soshot" table.
|
||||||
|
*/
|
||||||
|
public function getTotal(): int {
|
||||||
|
$stmt = $this->db->prepare("SELECT COUNT(id) AS nb FROM soshot;");
|
||||||
|
$stmt->execute();
|
||||||
|
$result = $stmt->fetch();
|
||||||
|
return $result['nb'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the number of records with errors in the "soshot" table.
|
||||||
|
*
|
||||||
|
* This method prepares an SQL SELECT statement to retrieve the number of records
|
||||||
|
* with errors in the "soshot" table, executes the statement, and returns the result.
|
||||||
|
*
|
||||||
|
* @return int The number of records with errors in the "soshot" table.
|
||||||
|
*/
|
||||||
|
public function getInError(): int {
|
||||||
|
$stmt = $this->db->prepare("SELECT COUNT(DISTINCT id) AS nb FROM soshot WHERE
|
||||||
|
complete = 2 OR
|
||||||
|
full = 2 OR
|
||||||
|
hd = 2 OR
|
||||||
|
nhd = 2 OR
|
||||||
|
thumb = 2 OR
|
||||||
|
fav = 2 OR
|
||||||
|
og = 2 OR
|
||||||
|
pdf = 2;");
|
||||||
|
$stmt->execute();
|
||||||
|
$result = $stmt->fetch();
|
||||||
|
return $result['nb'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a list of records from the "soshot" table.
|
||||||
|
*
|
||||||
|
* This method prepares an SQL SELECT statement to retrieve a list of records from
|
||||||
|
* the "soshot" table, based on the specified start and end indices, orders the
|
||||||
|
* results by the "created" column in descending order, executes the statement, and
|
||||||
|
* returns the result set as an array of objects.
|
||||||
|
*
|
||||||
|
* @param int $start The starting index of the record to retrieve.
|
||||||
|
* @param int $end The ending index of the record to retrieve.
|
||||||
|
*
|
||||||
|
* @return array An array of objects representing the records in the "soshot" table.
|
||||||
|
*/
|
||||||
|
public function getList(int $start, int $end) {
|
||||||
|
$stmt = $this->db->prepare("SELECT * FROM soshot ORDER BY created DESC limit :start, :end;");
|
||||||
|
$stmt->execute(array(':start' => $start, ':end' => $end));
|
||||||
|
$result = $stmt->fetchAll(PDO::FETCH_OBJ);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
71
app/Router.php
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use App\Utils\Error;
|
||||||
|
|
||||||
|
class Router {
|
||||||
|
private $routes;
|
||||||
|
private $params = [];
|
||||||
|
private $requestMethod;
|
||||||
|
private $path;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->routes = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addRoute($paths, $controller, $method = 'GET') {
|
||||||
|
|
||||||
|
if (!is_array($paths)) {
|
||||||
|
$paths = [$paths];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($paths as $path) {
|
||||||
|
$this->routes[$path] = [
|
||||||
|
'controller' => $controller,
|
||||||
|
'method' => $method
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function match() {
|
||||||
|
$path = $_SERVER['REQUEST_URI'];
|
||||||
|
$parts = parse_url($path);
|
||||||
|
$this->path = $parts['path'];
|
||||||
|
|
||||||
|
$this->requestMethod = $_SERVER['REQUEST_METHOD'];
|
||||||
|
|
||||||
|
if (isset($this->routes[$this->path])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render($conf) {
|
||||||
|
if ($this->requestMethod === 'GET') {
|
||||||
|
$this->params = (object)$_GET;
|
||||||
|
} elseif ($this->requestMethod === 'POST') {
|
||||||
|
$this->params = (object)$_POST;
|
||||||
|
} else {
|
||||||
|
$error = new Error();
|
||||||
|
$error->index((object)['status' => 405, 'message' => 'Method not allowed']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($conf->webPage === false && $this->path !== '/api') {
|
||||||
|
$error = new Error();
|
||||||
|
$error->index((object)['status' => 404, 'message' => 'The page that you have requested could not be found.']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->path === '/debug') {
|
||||||
|
require_once __DIR__ . '/../bin/thumbShoter.php';
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$className = '\\' . $this->routes[$this->path]['controller'];
|
||||||
|
$controllerInstance = new $className();
|
||||||
|
$content = $controllerInstance->index($this->params, $conf);
|
||||||
|
if (in_array($className, ['\App\Controllers\Home', '\App\Controllers\Backend']) && empty($this->params->showImg)) {
|
||||||
|
require_once __DIR__ . '/../tpl/bone.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
104
app/Soshot/GetFav.php
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Soshot;
|
||||||
|
|
||||||
|
use App\Soshot\GetThumb;
|
||||||
|
use App\Utils\ConvertIco;
|
||||||
|
use Embed\Embed;
|
||||||
|
use Zebra_Image;
|
||||||
|
|
||||||
|
class GetFav extends GetThumb {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a favicon for a given URL.
|
||||||
|
*
|
||||||
|
* This method takes the URL provided in the `demande` object and uses the Embed
|
||||||
|
* library to extract the favicon or icon from the website. If the favicon or icon
|
||||||
|
* cannot be extracted, a default error image is used. The favicon is then resized
|
||||||
|
* to the specified size and saved to the specified file path. If the favicon is in
|
||||||
|
* ICO or SVG format, it is converted to PNG format using the ConvertIco class. The
|
||||||
|
* method updates the database with the status of the favicon creation.
|
||||||
|
*
|
||||||
|
* @return bool True if the favicon was successfully created, false otherwise.
|
||||||
|
*/
|
||||||
|
public function makeFavicon() {
|
||||||
|
|
||||||
|
if (!is_dir($this->filePath)) {
|
||||||
|
mkdir($this->demande->filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$embed = new Embed();
|
||||||
|
$info = $embed->get($this->demande->url);
|
||||||
|
|
||||||
|
if (!is_null($info->favicon)) {
|
||||||
|
if (!$favicon = @file_get_contents($info->favicon)) {
|
||||||
|
copy(__DIR__ . '/../../src/images/error_fav.' . $this->fileFormat, $this->demande->requestImg);
|
||||||
|
$this->db->addUpdate(2, $this->type);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} elseif (!is_null($info->icon)) {
|
||||||
|
if (!$favicon = @file_get_contents($info->icon)) {
|
||||||
|
copy(__DIR__ . '/../../src/images/error_fav.' . $this->fileFormat, $this->demande->requestImg);
|
||||||
|
$this->db->addUpdate(2, $this->type);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
copy(__DIR__ . '/../../src/images/error_fav.' . $this->fileFormat, $this->demande->requestImg);
|
||||||
|
$this->db->addUpdate(2, $this->type);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_put_contents($this->demande->requestImg, $favicon);
|
||||||
|
|
||||||
|
$realMimeType = mime_content_type($this->demande->requestImg);
|
||||||
|
|
||||||
|
if ($realMimeType === 'image/vnd.microsoft.icon') {
|
||||||
|
ConvertIco::convert($this->demande, $this->conf->icoSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($realMimeType === 'image/svg+xml') {
|
||||||
|
ConvertIco::convert($this->demande, $this->conf->icoSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
$image = new Zebra_Image();
|
||||||
|
$image->source_path = $this->demande->requestImg;
|
||||||
|
$image->target_path = $this->demande->requestImg;
|
||||||
|
$image->preserve_time = false;
|
||||||
|
$image->enlarge_smaller_images = false;
|
||||||
|
$image->resize($this->conf->icoSize, $this->conf->icoSize, ZEBRA_IMAGE_CROP_TOPLEFT);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->db->addUpdate(2, $this->type);
|
||||||
|
copy(__DIR__ . '/../../src/images/error_fav.png', $this->demande->requestImg);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_file($this->demande->requestImg)) {
|
||||||
|
copy(__DIR__ . '/../../src/images/error_fav.png', $this->demande->requestImg);
|
||||||
|
$this->db->addUpdate(2, $this->type);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
$this->db->addUpdate(1, $this->type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the requested image.
|
||||||
|
*
|
||||||
|
* This method checks if the requested image file exists. If the requested image is
|
||||||
|
* a favicon and does not exist, it calls the makeFavicon method to generate it.
|
||||||
|
* Then, it sets the appropriate headers for the image and outputs its contents.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function show() {
|
||||||
|
if (!file_exists($this->requestImg) && $this->type === 'fav') {
|
||||||
|
$this->makeFavicon();
|
||||||
|
}
|
||||||
|
//echo '<img src="data:image/png;base64,'.base64_encode(file_get_contents($this->requestImg)).'">';
|
||||||
|
header("Content-type: image/$this->fileFormat");
|
||||||
|
header('Expires: ', gmdate('D, d M Y H:i:s', time()) . ' GMT');
|
||||||
|
echo file_get_contents($this->requestImg);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
89
app/Soshot/GetOg.php
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Soshot;
|
||||||
|
|
||||||
|
use App\Soshot\GetThumb;
|
||||||
|
use Embed\Embed;
|
||||||
|
use App\Utils\ConvertToPng;
|
||||||
|
|
||||||
|
class GetOg extends GetThumb {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the OG image for a given URL.
|
||||||
|
*
|
||||||
|
* This method retrieves the OG image from the provided URL using the Embed library.
|
||||||
|
* If the image cannot be retrieved, an error image is used instead. The image is
|
||||||
|
* then saved to the specified file path. If the image format is not PNG, it is
|
||||||
|
* converted to PNG using the ConvertToPng class. The method updates the database
|
||||||
|
* with the status of the OG image creation.
|
||||||
|
*
|
||||||
|
* @return bool True if the OG image was successfully created, false otherwise.
|
||||||
|
*/
|
||||||
|
public function makeOg() {
|
||||||
|
|
||||||
|
if (!is_dir($this->filePath)) {
|
||||||
|
mkdir($this->demande->filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$embed = new Embed();
|
||||||
|
$info = $embed->get($this->demande->url);
|
||||||
|
|
||||||
|
if (!is_null($info->image)) {
|
||||||
|
if (!$image = file_get_contents($info->image)) {
|
||||||
|
copy(__DIR__ . '/../../src/images/error_thumb.png', $this->demande->requestImg);
|
||||||
|
$this->db->addUpdate(2, $this->type);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
copy(__DIR__ . '/../../src/images/error_thumb.png', $this->demande->requestImg);
|
||||||
|
$this->db->addUpdate(2, $this->type);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($image)) {
|
||||||
|
copy(__DIR__ . '/../../src/images/error_thumb.png', $this->demande->requestImg);
|
||||||
|
$this->db->addUpdate(2, $this->type);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_put_contents($this->demande->requestImg, $image);
|
||||||
|
|
||||||
|
if (in_array(mime_content_type($this->demande->requestImg), ['image/jpg', 'image/jpeg', 'image/bmp', 'image/webp']) && $this->fileFormat === 'png') {
|
||||||
|
// @todo convertToWebp convertToJpg
|
||||||
|
ConvertToPng::convertToPng($this->demande);
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
copy(__DIR__ . '/../../src/images/error_thumb.png', $this->demande->requestImg);
|
||||||
|
$this->db->addUpdate(2, $this->type);
|
||||||
|
}
|
||||||
|
if (!is_file($this->demande->requestImg)) {
|
||||||
|
copy(__DIR__ . '/../../src/images/error_thumb.png', $this->demande->requestImg);
|
||||||
|
$this->db->addUpdate(2, $this->type);
|
||||||
|
} else {
|
||||||
|
$this->db->addUpdate(1, $this->type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the OG image for a given URL.
|
||||||
|
*
|
||||||
|
* This method checks if the OG image file exists and if the type is 'og'. If the
|
||||||
|
* file does not exist, it calls the makeOg() method to generate the OG image. It
|
||||||
|
* then sets the appropriate headers and outputs the contents of the OG image file.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function show() {
|
||||||
|
if (file_exists($this->requestImg) && $this->type === 'og') {
|
||||||
|
} else {
|
||||||
|
$this->makeOg();
|
||||||
|
}
|
||||||
|
|
||||||
|
//echo '<img src="data:' . mime_content_type($this->requestImg) . ';base64,' . base64_encode(file_get_contents($this->requestImg)) . '">';
|
||||||
|
header("Content-type: " . mime_content_type($this->requestImg));
|
||||||
|
header('Expires: ', gmdate('D, d M Y H:i:s', time()) . ' GMT');
|
||||||
|
echo file_get_contents($this->requestImg);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
163
app/Soshot/GetThumb.php
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Soshot;
|
||||||
|
|
||||||
|
use App\DataBase\DataBase;
|
||||||
|
use App\Utils\Hmac;
|
||||||
|
use App\Utils\Error;
|
||||||
|
use App\Utils\ResizeToDemande;
|
||||||
|
|
||||||
|
class GetThumb {
|
||||||
|
|
||||||
|
protected $params;
|
||||||
|
protected $type;
|
||||||
|
protected $filePath;
|
||||||
|
protected $receiveHmac;
|
||||||
|
protected $requestImg;
|
||||||
|
protected $queuePath = __DIR__ . '/../../cache/queue/';
|
||||||
|
protected $complete;
|
||||||
|
protected $conf;
|
||||||
|
protected $db;
|
||||||
|
protected $demande;
|
||||||
|
protected $fileFormat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new object.
|
||||||
|
*
|
||||||
|
* This method initializes the object properties based on the provided parameters
|
||||||
|
* and configuration. It first checks if the provided HMAC is valid using the
|
||||||
|
* Hmac class. If the HMAC is invalid, it returns an error message. It then sets
|
||||||
|
* the object properties for the request URL, HMAC, type, file format, file path,
|
||||||
|
* request image path, complete image path, and database connection.
|
||||||
|
*
|
||||||
|
* @param object $params The request parameters object containing the URL, HMAC, and type.
|
||||||
|
* @param object $conf The configuration object containing the file format and key.
|
||||||
|
*
|
||||||
|
* @return GetOg The constructed GetOg object.
|
||||||
|
*/
|
||||||
|
function __construct(object $params, object $conf) {
|
||||||
|
$hmac = new Hmac($conf->key);
|
||||||
|
$this->conf = $conf;
|
||||||
|
$this->receiveHmac = $params->hmac;
|
||||||
|
|
||||||
|
if ($hmac->checkHmac($this->receiveHmac, $params->url) === false) {
|
||||||
|
$message = (object) ['status' => 404, 'message' => 'Wrong Hmac'];
|
||||||
|
$error = new Error();
|
||||||
|
$error->index($message);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->params = $params;
|
||||||
|
$this->type = $params->type;
|
||||||
|
$this->fileFormat = $this->conf->fileFormat;
|
||||||
|
$this->filePath = $hmac->makeFilePath($this->receiveHmac);
|
||||||
|
if ($this->type === 'pdf') {
|
||||||
|
$this->requestImg = $this->filePath . $this->receiveHmac . '_' . $this->type . '.pdf';
|
||||||
|
} else {
|
||||||
|
$this->requestImg = $this->filePath . $this->receiveHmac . '_' . $this->type . '.' . $this->fileFormat;
|
||||||
|
}
|
||||||
|
$this->complete = __DIR__ . '/../../cache/img/' . substr($this->params->hmac, 0, 4) . '/' . $this->params->hmac . '_complete.' . $this->fileFormat;
|
||||||
|
|
||||||
|
$this->demande = (object)[
|
||||||
|
'url' => $this->params->url,
|
||||||
|
'hmac' => $this->params->hmac,
|
||||||
|
'filePath' => $this->filePath,
|
||||||
|
'requestImg' => $this->requestImg,
|
||||||
|
'type' => $this->params->type
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->db = new DataBase($this->demande);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the requested image or PDF.
|
||||||
|
*
|
||||||
|
* This method checks if the requested file exists and serves it with the appropriate
|
||||||
|
* headers. If the requested file does not exist, it checks if a complete image exists
|
||||||
|
* and resizes it to the requested type. If neither the requested file nor a complete
|
||||||
|
* image exists, it adds a job to the queue to generate the requested image and serves
|
||||||
|
* a placeholder image.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function show() {
|
||||||
|
if ($this->type === 'pdf' && file_exists($this->requestImg)) {
|
||||||
|
header("Content-type:application/pdf");
|
||||||
|
header('Expires: ', gmdate('D, d M Y H:i:s', time()) . ' GMT');
|
||||||
|
header("Content-Disposition:attachment;filename=\"archive.pdf\"");
|
||||||
|
readfile($this->requestImg);
|
||||||
|
echo file_get_contents($this->requestImg);
|
||||||
|
exit();
|
||||||
|
} else if (file_exists($this->requestImg) && in_array($this->type, ['complete', 'full', 'hd', 'nhd', 'thumb'])) {
|
||||||
|
header("Content-type: image/$this->fileFormat");
|
||||||
|
header('Expires: ', gmdate('D, d M Y H:i:s', time()) . ' GMT');
|
||||||
|
echo file_get_contents($this->requestImg);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
if (file_exists($this->complete) && in_array($this->type, ['complete', 'full', 'hd', 'nhd', 'thumb'])) {
|
||||||
|
if (ResizeToDemande::makeDemande((object)[
|
||||||
|
'complete' => $this->complete,
|
||||||
|
'filePath' => $this->requestImg,
|
||||||
|
'type' => $this->type
|
||||||
|
])) {
|
||||||
|
$this->db->addUpdate(1, $this->type);
|
||||||
|
header("Content-type: image/$this->fileFormat");
|
||||||
|
header('Expires: ', gmdate('D, d M Y H:i:s', time()) . ' GMT');
|
||||||
|
echo file_get_contents($this->requestImg);
|
||||||
|
exit();
|
||||||
|
} else {
|
||||||
|
// @todo log
|
||||||
|
}
|
||||||
|
exit();
|
||||||
|
} else {
|
||||||
|
$json = json_encode([
|
||||||
|
'type' => $this->params->type,
|
||||||
|
'hmac' => $this->params->hmac,
|
||||||
|
'url' => $this->params->url,
|
||||||
|
'filePath' => $this->requestImg,
|
||||||
|
'complete' => __DIR__ . '/../../cache/img/' . substr($this->params->hmac, 0, 4) . '/' . $this->params->hmac . '_complete.' . $this->fileFormat
|
||||||
|
]);
|
||||||
|
|
||||||
|
$completeDemande = (object)[
|
||||||
|
'url' => $this->params->url,
|
||||||
|
'hmac' => $this->params->hmac,
|
||||||
|
'filePath' => $this->filePath,
|
||||||
|
'requestImg' => $this->requestImg,
|
||||||
|
'type' => 'complete'
|
||||||
|
];
|
||||||
|
$complete = new DataBase($completeDemande);
|
||||||
|
$complete->addUpdate(3, $this->type);
|
||||||
|
|
||||||
|
if ($this->conf->alwaysMakePdf === true) {
|
||||||
|
$complete->addUpdate(3, 'pdf');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->addQueue($json);
|
||||||
|
|
||||||
|
header("Content-type: image/png");
|
||||||
|
header('Expires: ', gmdate('D, d M Y H:i:s', time()) . ' GMT');
|
||||||
|
if ($this->type === 'fav') {
|
||||||
|
echo file_get_contents(__DIR__ . '/../../src/images/error_fav.png');
|
||||||
|
} else {
|
||||||
|
echo file_get_contents(__DIR__ . '/../../src/images/' . $this->type . '_generation_in_progress.jpg');
|
||||||
|
}
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a JSON object to the queue file.
|
||||||
|
*
|
||||||
|
* @param string $json The JSON object to add to the queue file.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function addQueue($json) {
|
||||||
|
$file = $this->queuePath . $this->receiveHmac . '.json';
|
||||||
|
$this->db->addUpdate(3, $this->type);
|
||||||
|
if (!file_exists($file)) {
|
||||||
|
file_put_contents($file, $json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
297
app/Soshot/MakeThumb.php
Normal file
|
@ -0,0 +1,297 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Soshot;
|
||||||
|
|
||||||
|
use App\DataBase\DataBase;
|
||||||
|
use App\Utils\ResizeToDemande;
|
||||||
|
use HeadlessChromium\Browser;
|
||||||
|
use HeadlessChromium\BrowserFactory;
|
||||||
|
use HeadlessChromium\Page;
|
||||||
|
use HeadlessChromium\Exception\BrowserConnectionFailed;
|
||||||
|
use HeadlessChromium\Exception\OperationTimedOut;
|
||||||
|
use Noodlehaus\Config;
|
||||||
|
|
||||||
|
if (!function_exists('n_print')) {
|
||||||
|
function n_print($data, $name = '') {
|
||||||
|
print_r($data, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MakeThumb {
|
||||||
|
|
||||||
|
private $queuePath = __DIR__ . '/../../cache/queue/';
|
||||||
|
private $thumbPath = __DIR__ . '/../../cache/img/';
|
||||||
|
private $fileSocket = __DIR__ . '/../../cache/tmp/chrome-php-socket';
|
||||||
|
private $fileList = [];
|
||||||
|
private $maxGenPerBatch = 2;
|
||||||
|
private $demandes = [];
|
||||||
|
private $open = false;
|
||||||
|
private $conf;
|
||||||
|
private $db;
|
||||||
|
private $chromePath = '';
|
||||||
|
private $tmpDir = '/tmp/chrome_soshot';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of the class.
|
||||||
|
*
|
||||||
|
* This method loads the default and user configuration files using the `Config`
|
||||||
|
* class. It sets the maximum number of generations per batch, the Chrome path, and
|
||||||
|
* the queue path. It then retrieves the list of JSON files in the queue directory
|
||||||
|
* and decodes their contents to populate the `$demandes` property. Finally, it
|
||||||
|
* initializes the `DataBase` object and assigns it to the `$db` property.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function __construct() {
|
||||||
|
|
||||||
|
$this->conf = (object)Config::load([__DIR__ . '/../../datas/config.default.json', '?' . __DIR__ . '/../../datas/config.json'])->all();
|
||||||
|
$this->maxGenPerBatch = $this->conf->maxGenPerBatch;
|
||||||
|
|
||||||
|
$this->chromePath = $this->conf->chromePath;
|
||||||
|
|
||||||
|
foreach (array_slice(glob($this->queuePath . "*.json"), 0, $this->maxGenPerBatch) as $filename) {
|
||||||
|
$this->fileList[] = $filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->fileList as $demande) {
|
||||||
|
$this->demandes[] = json_decode(file_get_contents($demande));
|
||||||
|
}
|
||||||
|
$this->db = new DataBase();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes the demands in the queue.
|
||||||
|
*
|
||||||
|
* This method iterates over the list of demands in the `$demandes` property.
|
||||||
|
* For each demand, it sets the parameters for the `DataBase` object, writes the HMAC
|
||||||
|
* to a temporary file, and checks if the demand is already complete.
|
||||||
|
*
|
||||||
|
* If the demand is complete and not a PDF, it resizes the image to the demanded size using the
|
||||||
|
* `ResizeToDemande` class.
|
||||||
|
*
|
||||||
|
* If the demand is not complete, it generates the complete image using the `makeComplete` method.
|
||||||
|
*
|
||||||
|
* In either case, it deletes the demand from the queue using the `deleteQueue` method and
|
||||||
|
* updates the database using the `addUpdate` method.
|
||||||
|
*
|
||||||
|
* Finally, it sets the `$open` property to `true` to indicate that the queue is open.
|
||||||
|
* If the `$open` property is `true`, it generates a complete
|
||||||
|
* image for all demands in the queue using the `makeComplete` method.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function compute() {
|
||||||
|
foreach ($this->demandes as $demande) {
|
||||||
|
$this->db->setParams($demande);
|
||||||
|
file_put_contents('/tmp/soshot_queue', $demande->hmac);
|
||||||
|
|
||||||
|
// todo verif permit type
|
||||||
|
|
||||||
|
if ($this->testComplete($demande->complete) && $demande->type !== 'pdf') {
|
||||||
|
if (ResizeToDemande::makeDemande($demande)) {
|
||||||
|
$this->deleteQueue($demande->hmac);
|
||||||
|
$this->db->addUpdate(1, $demande->type);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->makeComplete($demande);
|
||||||
|
if ($demande->type !== 'pdf') {
|
||||||
|
if (!ResizeToDemande::makeDemande($demande)) {
|
||||||
|
// todo log
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->deleteQueue($demande->hmac);
|
||||||
|
$this->db->addUpdate(1, $demande->type);
|
||||||
|
|
||||||
|
$completeDemande = (object)[
|
||||||
|
'hmac' => $demande->hmac,
|
||||||
|
'url' => $demande->url,
|
||||||
|
'type' => 'complete'
|
||||||
|
];
|
||||||
|
$complete = new DataBase($completeDemande);
|
||||||
|
$complete->addUpdate(1, $demande->type);
|
||||||
|
}
|
||||||
|
$this->open = true;
|
||||||
|
|
||||||
|
if ($this->open === true) {
|
||||||
|
$this->makeComplete(null, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a demand from the queue.
|
||||||
|
*
|
||||||
|
* This method takes an HMAC as a parameter and deletes the corresponding JSON file
|
||||||
|
* from the queue directory using the `unlink` function.
|
||||||
|
*
|
||||||
|
* @param string $hmac The HMAC of the demand to delete.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function deleteQueue(string $hmac) {
|
||||||
|
if (file_exists($this->queuePath . $hmac . '.json')) {
|
||||||
|
unlink($this->queuePath . $hmac . '.json');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a file exists.
|
||||||
|
*
|
||||||
|
* This method takes a file path as a parameter and checks if the file exists using
|
||||||
|
* the `file_exists` function. It returns `true` if the file exists, and `false`
|
||||||
|
* otherwise.
|
||||||
|
*
|
||||||
|
* @param string $complete The file path to check.
|
||||||
|
*
|
||||||
|
* @return bool `true` if the file exists, `false` otherwise.
|
||||||
|
*/
|
||||||
|
private function testComplete(string $complete): bool {
|
||||||
|
if (file_exists($complete)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a complete image or PDF for a demand.
|
||||||
|
*
|
||||||
|
* This method takes a demand object as a parameter and generates a complete image
|
||||||
|
* or PDF for the demand.
|
||||||
|
*
|
||||||
|
* If the `$close` parameter is set to `true`, it closes the browser and deletes the socket file.
|
||||||
|
* The method first checks if a socket file exists and connects to an existing browser if it does.
|
||||||
|
*
|
||||||
|
* If not, it creates a new browser instance using the `BrowserFactory` class.
|
||||||
|
*
|
||||||
|
* It then navigates to the URL specified in the demand and takes a screenshot of the page.
|
||||||
|
*
|
||||||
|
* If the demand type is 'pdf' or the `alwaysMakePdf` configuration option is set to `true`, it also
|
||||||
|
* generates a PDF of the page.
|
||||||
|
*
|
||||||
|
* Finally, it saves the screenshot and PDF to the appropriate file paths and
|
||||||
|
* updates the database using the `addUpdate` method.
|
||||||
|
*
|
||||||
|
* If an `OperationTimedOut` exception is thrown, it logs the error, closes the browser,
|
||||||
|
* deletes the socket file, copies an error image to the file path, and updates the
|
||||||
|
* database with an error status.
|
||||||
|
*
|
||||||
|
* @param object|null $demande The demand object containing the URL, HMAC, file path, and type of the demand.
|
||||||
|
* @param bool $close Whether to close the browser and delete the socket file after
|
||||||
|
* generating the complete image or PDF. Defaults to `false`.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function makeComplete(object|null $demande, bool $close = false) {
|
||||||
|
if (file_exists($this->fileSocket)) {
|
||||||
|
$socket = \file_get_contents($this->fileSocket);
|
||||||
|
try {
|
||||||
|
$browser = BrowserFactory::connectToBrowser($socket);
|
||||||
|
} catch (BrowserConnectionFailed $e) {
|
||||||
|
$browser = $this->openBrowser();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$factory = new BrowserFactory($this->chromePath);
|
||||||
|
$browser = $factory->createBrowser([
|
||||||
|
'userAgent' => 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)',
|
||||||
|
'keepAlive' => true,
|
||||||
|
'headless' => true,
|
||||||
|
'windowSize' => [1920, 1080],
|
||||||
|
'userDataDir' => $this->tmpDir,
|
||||||
|
'customFlags' => [
|
||||||
|
'--disable-dev-shm-usage',
|
||||||
|
'--disable-gpu'
|
||||||
|
],
|
||||||
|
'noSandbox' => true,
|
||||||
|
//'connectionDelay' => 0.8, // add 0.8 second of delay between each instruction sent to chrome,
|
||||||
|
//'debugLogger' => 'php://stdout',
|
||||||
|
]);
|
||||||
|
\file_put_contents($this->fileSocket, $browser->getSocketUri(), LOCK_EX);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($close === true) {
|
||||||
|
$socket = \file_get_contents($this->fileSocket);
|
||||||
|
$browser = BrowserFactory::connectToBrowser($socket);
|
||||||
|
$browser->close();
|
||||||
|
unlink($this->fileSocket);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$dir = $this->thumbPath . substr($demande->hmac, 0, 4) . '/';
|
||||||
|
|
||||||
|
if (!is_dir($dir)) {
|
||||||
|
mkdir($dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$page = $browser->createPage();
|
||||||
|
$page->navigate($demande->url)->waitForNavigation(Page::LOAD, 15000);
|
||||||
|
sleep(4);
|
||||||
|
|
||||||
|
$page->screenshot([
|
||||||
|
'captureBeyondViewport' => true,
|
||||||
|
'clip' => $page->getFullPageClip(),
|
||||||
|
'format' => $this->conf->fileFormat,
|
||||||
|
])->saveToFile($demande->complete);
|
||||||
|
|
||||||
|
if ($demande->type === 'pdf' || $this->conf->alwaysMakePdf === true) {
|
||||||
|
if ($this->conf->alwaysMakePdf === true) {
|
||||||
|
$pdfFile = str_replace($demande->type, 'pdf', $demande->filePath);
|
||||||
|
$pdfFile = str_replace($this->conf->fileFormat, 'pdf', $pdfFile);
|
||||||
|
} else {
|
||||||
|
$pdfFile = $demande->filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
$page->pdf([
|
||||||
|
'printBackground' => true,
|
||||||
|
'displayHeaderFooter' => true,
|
||||||
|
'paperWidth' => 8.268,
|
||||||
|
'paperHeight' => 11.693,
|
||||||
|
'scale' => 1
|
||||||
|
])->saveToFile($pdfFile);
|
||||||
|
|
||||||
|
if ($this->conf->alwaysMakePdf === true) {
|
||||||
|
$this->db->addUpdate(1, 'pdf');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->db->addUpdate(1, 'complete');
|
||||||
|
} catch (OperationTimedOut $e) {
|
||||||
|
// todo log
|
||||||
|
$socket = \file_get_contents($this->fileSocket);
|
||||||
|
$browser = BrowserFactory::connectToBrowser($socket);
|
||||||
|
$browser->close();
|
||||||
|
unlink($this->fileSocket);
|
||||||
|
copy(__DIR__ . '/../../src/images/error_thumb.png', $demande->filePath);
|
||||||
|
$this->db->addUpdate(2, $demande->type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and opens a new browser instance.
|
||||||
|
*
|
||||||
|
* This method creates a new browser instance using the `BrowserFactory` class with
|
||||||
|
* the specified configuration options. It then saves the socket URI to a file and
|
||||||
|
* returns the browser instance.
|
||||||
|
*
|
||||||
|
* @return Browser The created browser instance.
|
||||||
|
*/
|
||||||
|
private function openBrowser(): Browser {
|
||||||
|
$factory = new BrowserFactory($this->chromePath);
|
||||||
|
|
||||||
|
// Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
|
||||||
|
$browser = $factory->createBrowser([
|
||||||
|
'userAgent' => 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)',
|
||||||
|
'keepAlive' => true,
|
||||||
|
'headless' => true,
|
||||||
|
'windowSize' => [1920, 1080],
|
||||||
|
'userDataDir' => $this->tmpDir,
|
||||||
|
'customFlags' => [
|
||||||
|
'--disable-dev-shm-usage',
|
||||||
|
'--disable-gpu'
|
||||||
|
],
|
||||||
|
'noSandbox' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
\file_put_contents($this->fileSocket, $browser->getSocketUri(), LOCK_EX);
|
||||||
|
return $browser;
|
||||||
|
}
|
||||||
|
}
|
28
app/Utils/ConvertIco.php
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Utils;
|
||||||
|
|
||||||
|
use Imagick;
|
||||||
|
|
||||||
|
class ConvertIco {
|
||||||
|
|
||||||
|
static function convert($demande, $size) {
|
||||||
|
|
||||||
|
rename($demande->requestImg, $demande->requestImg . '.ico');
|
||||||
|
$icoFile = $demande->requestImg . '.ico';
|
||||||
|
|
||||||
|
if (empty(Imagick::queryFormats("ICO"))) {
|
||||||
|
throw new \Exception('Unsupported format');
|
||||||
|
}
|
||||||
|
|
||||||
|
$im = new Imagick($icoFile);
|
||||||
|
$targetWidth = $targetHeight = $size;
|
||||||
|
|
||||||
|
if ($im->getImageWidth() <> $targetWidth || $im->getImageHeight() <> $targetHeight) {
|
||||||
|
$im->cropThumbnailImage($targetWidth, $targetHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
$im->writeImage($demande->requestImg);
|
||||||
|
unlink($icoFile);
|
||||||
|
}
|
||||||
|
}
|
17
app/Utils/ConvertStatus.php
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Utils;
|
||||||
|
|
||||||
|
class ConvertStatus {
|
||||||
|
static function convertToIcon($status) {
|
||||||
|
if ($status === 1) {
|
||||||
|
echo '<i class="fa fa-check w3-text-green" aria-hidden="true"></i>';
|
||||||
|
} elseif ($status === 2) {
|
||||||
|
echo '<i class="fa fa-check w3-text-red" aria-hidden="true"></i>';
|
||||||
|
} elseif ($status === 3) {
|
||||||
|
echo '<i class="fa fa-clock-o w3-text-orange" aria-hidden="true"></i>';
|
||||||
|
} else {
|
||||||
|
echo '<i class="fa fa-ban w3-text-red" aria-hidden="true"></i>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
app/Utils/ConvertToPng.php
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Utils;
|
||||||
|
|
||||||
|
class ConvertToPng {
|
||||||
|
|
||||||
|
static function convertToPng($demande) {
|
||||||
|
|
||||||
|
rename($demande->requestImg, $demande->requestImg . '.tmp');
|
||||||
|
$tmpFile = $demande->requestImg . '.tmp';
|
||||||
|
|
||||||
|
|
||||||
|
if (!imagepng(imagecreatefromstring(file_get_contents($tmpFile)), $demande->requestImg)) {
|
||||||
|
}
|
||||||
|
unlink($tmpFile);
|
||||||
|
}
|
||||||
|
}
|
11
app/Utils/Domains.php
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Utils;
|
||||||
|
|
||||||
|
class Domains {
|
||||||
|
|
||||||
|
static function getDomain($url) {
|
||||||
|
$sourceUrl = parse_url($url);
|
||||||
|
return $sourceUrl['host'];
|
||||||
|
}
|
||||||
|
}
|
14
app/Utils/Error.php
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
namespace App\Utils;
|
||||||
|
|
||||||
|
class Error {
|
||||||
|
|
||||||
|
private $errorType = [];
|
||||||
|
|
||||||
|
function index(object $error) {
|
||||||
|
http_response_code($error->status);
|
||||||
|
echo "<h1>404 Not Found</h1>";
|
||||||
|
echo "$error->message";
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
36
app/Utils/Hmac.php
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Utils;
|
||||||
|
|
||||||
|
class Hmac {
|
||||||
|
private $key;
|
||||||
|
private $basePath = __DIR__ . '/../../cache/img/';
|
||||||
|
private $filePath;
|
||||||
|
private $hashPath;
|
||||||
|
|
||||||
|
public function __construct($key) {
|
||||||
|
$this->key = $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function makeHmac($url) {
|
||||||
|
return hash_hmac('sha1', $url, $this->key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function makeHashPath($hashUrl) {
|
||||||
|
$this->hashPath = substr($hashUrl, 0, 4) . '/';
|
||||||
|
return $this->hashPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function makeFilePath($hmac) {
|
||||||
|
$this->filePath = $this->basePath . $this->makeHashPath($hmac);
|
||||||
|
return $this->filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkHmac($receiveHmac, $url) {
|
||||||
|
if ($receiveHmac === $this->makeHmac($url, $this->key)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
43
app/Utils/Page.php
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Utils;
|
||||||
|
|
||||||
|
class Page {
|
||||||
|
|
||||||
|
static function active($test, $attend, $class) {
|
||||||
|
if ($test === $attend) {
|
||||||
|
return $class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static function checked($test, $attend) {
|
||||||
|
if ($test === $attend) {
|
||||||
|
return 'checked';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static function selected($test, $attend) {
|
||||||
|
if (is_array($attend)) {
|
||||||
|
if (in_array($test, $attend)) {
|
||||||
|
return 'selected';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($test === $attend) {
|
||||||
|
return 'selected';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static function genToken() {
|
||||||
|
$token = sha1(uniqid(rand(), true) . '_' . mt_rand());
|
||||||
|
$_SESSION['token'] = $token;
|
||||||
|
return $token;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function verifToken($token) {
|
||||||
|
if ($token === $_SESSION['token']) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
37
app/Utils/ResizeToDemande.php
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Utils;
|
||||||
|
|
||||||
|
use Zebra_Image;
|
||||||
|
|
||||||
|
class ResizeToDemande {
|
||||||
|
|
||||||
|
static function makeDemande($demande) {
|
||||||
|
$image = new Zebra_Image();
|
||||||
|
$image->source_path = $demande->complete;
|
||||||
|
$image->target_path = $demande->filePath;
|
||||||
|
$image->preserve_time = false;
|
||||||
|
$image->enlarge_smaller_images = false;
|
||||||
|
|
||||||
|
if ($demande->type === 'full') {
|
||||||
|
$image->resize(1920, 1080, ZEBRA_IMAGE_CROP_TOPLEFT);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($demande->type === 'hd') {
|
||||||
|
$image->resize(1280, 720, ZEBRA_IMAGE_CROP_TOPLEFT);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($demande->type === 'nhd') {
|
||||||
|
$image->resize(640, 360, ZEBRA_IMAGE_CROP_TOPLEFT);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($demande->type === 'thumb') {
|
||||||
|
$image->resize(160, 90, ZEBRA_IMAGE_CROP_TOPLEFT);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
24
app/Utils/ShowImg.php
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Utils;
|
||||||
|
|
||||||
|
class ShowImg {
|
||||||
|
|
||||||
|
private $thumbPath = __DIR__ . '/../../cache/img/';
|
||||||
|
|
||||||
|
function __construct($type, $hmac, $fileFormat) {
|
||||||
|
|
||||||
|
$img = $this->thumbPath . substr($hmac, 0, 4) . '/' . $hmac . '_' . $type . '.'.$fileFormat;
|
||||||
|
|
||||||
|
header("Content-type: image/$fileFormat");
|
||||||
|
header('Expires: ', gmdate('D, d M Y H:i:s', time()) . ' GMT');
|
||||||
|
|
||||||
|
if (file_exists($img)) {
|
||||||
|
echo file_get_contents($img);
|
||||||
|
} else {
|
||||||
|
// todo not gen
|
||||||
|
echo file_get_contents(__DIR__ . '/../../src/images/error_thumb.png');
|
||||||
|
}
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,2 +0,0 @@
|
||||||
Allow from none
|
|
||||||
Deny from all
|
|
BIN
bin/000.png
Before Width: | Height: | Size: 1.2 KiB |
BIN
bin/404.png
Before Width: | Height: | Size: 1 KiB |
7
bin/cleanChromeCache.sh
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
FILE=/var/www/cache/tmp/chrome-php-socket
|
||||||
|
if [ ! -f "$FILE" ]; then
|
||||||
|
cd /tmp/chrome_soshot
|
||||||
|
rm -R Default
|
||||||
|
fi
|
|
@ -1,14 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
queueDir="../cache/cronTask/"
|
|
||||||
|
|
||||||
for file in $queueDir*.hash
|
|
||||||
do
|
|
||||||
if [ -f $file ];
|
|
||||||
then
|
|
||||||
while read site hashUrl thumbSize sizeNameDir onlyThumb
|
|
||||||
do
|
|
||||||
bash thumb_server.sh "$site" "$hashUrl" "$thumbSize" "$sizeNameDir" "$onlyThumb" "0"
|
|
||||||
rm $file
|
|
||||||
done < $file
|
|
||||||
fi
|
|
||||||
done
|
|
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.9 KiB |
15
bin/mon.sh
|
@ -1,15 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
while [ 1 ]
|
|
||||||
do
|
|
||||||
clear
|
|
||||||
echo "#################################################"
|
|
||||||
echo "#################################################"
|
|
||||||
echo "#### Total request thumbshot : " $(ps -e | grep -v grep | grep thumb_server.sh | wc -l) && # count thumb_server.sh process
|
|
||||||
echo "#### Number of thumbshot are made now : " $(ps -e | grep -v grep | grep phantomjs | wc -l) && # count phantomjs process
|
|
||||||
echo "#### Number of test error now : " $(ps -e | grep -v grep | grep curl | wc -l) # count thumb_server.sh => curl process
|
|
||||||
echo "#################################################"
|
|
||||||
echo "######### PRESS CTRL+C FOR STOP MONITOR #########"
|
|
||||||
echo "#################################################"
|
|
||||||
|
|
||||||
sleep 3 # 3 second between two refresh
|
|
||||||
done
|
|
|
@ -1,26 +0,0 @@
|
||||||
var page = new WebPage(),
|
|
||||||
address, output, size;
|
|
||||||
|
|
||||||
if (phantom.args.length < 2) {
|
|
||||||
//console.log('Usage: rasterize.js URL filename');
|
|
||||||
phantom.exit();
|
|
||||||
} else {
|
|
||||||
address = phantom.args[0];
|
|
||||||
output = phantom.args[1];
|
|
||||||
page.settings.userAgent = 'SoShot Agent';
|
|
||||||
page.viewportSize = { width: 1280, height: 1024 };
|
|
||||||
|
|
||||||
page.open(address, function (status) {
|
|
||||||
if (status !== 'success') {
|
|
||||||
//console.log('Unable to load the address!');
|
|
||||||
page.close();
|
|
||||||
phantom.exit();
|
|
||||||
} else {
|
|
||||||
window.setTimeout(function () {
|
|
||||||
page.render(output);
|
|
||||||
page.close();
|
|
||||||
phantom.exit();
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
16
bin/thumbShoter.php
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
namespace App\Bin;
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
|
use App\Soshot\MakeThumb;
|
||||||
|
|
||||||
|
if(file_exists(__DIR__ . '/../cache/tmp/chrome-php-socket')){
|
||||||
|
echo 'Work in progress';
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$makeThumb = new MakeThumb;
|
||||||
|
$makeThumb->compute();
|
|
@ -1,131 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
# inspirated source http://www.cambus.net/creating-thumbnails-using-phantomjs-and-imagemagick/ for global idea
|
|
||||||
# https://gist.github.com/rsvp/1171304 for httpstatus code
|
|
||||||
# All info at http://forge.leslibres.org/projects/soshot
|
|
||||||
|
|
||||||
site=$1 # url for thumbshot
|
|
||||||
hashUrl=$2 # hash(url) is the name of final image
|
|
||||||
thumbSize=$3 # size of thumb widthxheight ex : 190x90
|
|
||||||
sizeNameDir=$4 # name of size dir
|
|
||||||
onlyThumb=$5 # make only thumbshot no full size image
|
|
||||||
waitForResult=$6 # if true we try to make soon as possible or add to queue
|
|
||||||
|
|
||||||
##########################################
|
|
||||||
##### DON'T EDIT THIS PARAM USE #####
|
|
||||||
##### cache/config/serverOptions.php #####
|
|
||||||
##### for overwrite options #####
|
|
||||||
##########################################
|
|
||||||
maxThread=1 # max parralle work. For me the best is nb core - 1
|
|
||||||
timeOut=60 # default time out, after this time the site are declared in error
|
|
||||||
log=false # log all generation success and error
|
|
||||||
randomSleep=`echo $((RANDOM%20))`
|
|
||||||
optimizeTool='' # external tool form optimize png
|
|
||||||
maxQueue=50 # max process in queue
|
|
||||||
currentProcess=$(ps -e | grep -v grep | grep thumb_ | wc -l)
|
|
||||||
firstLevel=${hashUrl:0:2}
|
|
||||||
secondLevel=${hashUrl:2:2}
|
|
||||||
startPath=$sizeNameDir/$firstLevel/$secondLevel/
|
|
||||||
getArch=$(getconf LONG_BIT)
|
|
||||||
echo $getArch
|
|
||||||
if [ $site == "manual" ]
|
|
||||||
then
|
|
||||||
site=$(cat "../cache/tmp/manual.txt")
|
|
||||||
echo '' > "../cache/tmp/manual.txt"
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p "../cache/img/$startPath"
|
|
||||||
|
|
||||||
if [ -f "../cache/config/serverOptions.php" ]
|
|
||||||
then
|
|
||||||
source "../cache/config/serverOptions.php"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ $currentProcess -gt $maxQueue ]
|
|
||||||
then
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! $waitForResult ]
|
|
||||||
then
|
|
||||||
sleep `echo $((RANDOM%20))`
|
|
||||||
while [[ `ps -e | grep -v grep | grep phantomjs | wc -l` -ge $maxThread ]]
|
|
||||||
do
|
|
||||||
sleep `echo $((RANDOM%20))`
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
start_time=`date +%s`
|
|
||||||
if [ $getArch -eq 64 ]
|
|
||||||
then
|
|
||||||
timeout $timeOut ./phantomjs_x64 --disk-cache=false --local-storage-path=bin/ --ignore-ssl-errors=true rasterize.js "$site" "../cache/tmp/$hashUrl.png"
|
|
||||||
else
|
|
||||||
timeout $timeOut ./phantomjs_x86 --disk-cache=false --local-storage-path=bin/ --ignore-ssl-errors=true rasterize.js "$site" "../cache/tmp/$hashUrl.png"
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
if [ ! -f "../cache/tmp/$hashUrl.png" ]
|
|
||||||
then
|
|
||||||
echo 0
|
|
||||||
errorCode=`echo $(curl -k --write-out %{http_code} --silent -S --connect-timeout $timeOut \--no-keepalive --output /dev/null $site)`
|
|
||||||
if [ $site == 'manual' ]
|
|
||||||
then
|
|
||||||
errorCode='manual'
|
|
||||||
fi
|
|
||||||
case $errorCode in
|
|
||||||
000) cp "../bin/000.png" "../cache/img/$startPath""$hashUrl""_thumb.png" && cp "../bin/000.png" "../cache/img/$startPath""$hashUrl.png" ;;
|
|
||||||
200) cp "../bin/error.png" "../cache/img/$startPath""$hashUrl""_thumb.png" && cp "../bin/error.png" "../cache/img/$startPath""$hashUrl.png" ;;
|
|
||||||
404) cp "../bin/404.png" "../cache/img/$startPath""$hashUrl""_thumb.png" && cp "../bin/404.png" "../cache/img/$startPath""$hashUrl.png" ;;
|
|
||||||
*) cp "..bin/error.png" "../cache/img/$startPath""$hashUrl""_thumb.png" && cp "../bin/error.png" "../cache/img/$startPath""$hashUrl.png" ;;
|
|
||||||
esac
|
|
||||||
if $log
|
|
||||||
then
|
|
||||||
end_time=`date +%s`
|
|
||||||
logDate=`date +'[%a %d %b %Y] [%H:%M:%S]'`
|
|
||||||
if [ $errorCode == 200 ]
|
|
||||||
then
|
|
||||||
echo "none --- "$site" --- "$hashUrl" --- "$thumbSize" --- "$sizeNameDir" --- "$onlyThumb" --- true --- "$errorCode > "../cache/logs/retry/"$hashUrl".log"
|
|
||||||
else
|
|
||||||
echo "none --- "$site" --- "$hashUrl" --- "$thumbSize" --- "$sizeNameDir" --- "$onlyThumb" --- true --- "$errorCode > "../cache/logs/other/"$hashUrl".log"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
if [ $onlyThumb == 1 ]
|
|
||||||
then
|
|
||||||
convert "../cache/tmp/$hashUrl.png" -crop 1280x1024+0+0 -filter Lanczos -thumbnail "$thumbSize" "../cache/img/$startPath""$hashUrl""_thumb.png"
|
|
||||||
if [ $optimizeTool ]
|
|
||||||
then
|
|
||||||
eval $optimizeTool "../cache/img/$startPath""$hashUrl""_thumb.png"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
convert "../cache/tmp/$hashUrl.png" -crop 1280x1024+0+0 "../cache/img/$startPath""$hashUrl.png"
|
|
||||||
convert "../cache/img/$startPath""$hashUrl.png" -filter Lanczos -thumbnail "$thumbSize" "../cache/img/$startPath""$hashUrl""_thumb.png"
|
|
||||||
if [ $optimizeTool ]
|
|
||||||
then
|
|
||||||
eval $optimizeTool "../cache/img/$startPath""$hashUrl""_thumb.png" "../cache/img/$startPath""$hashUrl.png"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
rm "../cache/tmp/$hashUrl.png"
|
|
||||||
|
|
||||||
if [ $onlyThumb ]
|
|
||||||
then
|
|
||||||
if [[ -f "../cache/img/$startPath""$hashUrl""_thumb.png" ]]
|
|
||||||
then
|
|
||||||
echo 1
|
|
||||||
else
|
|
||||||
echo 0
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
if [[ -f "../cache/img/$startPath""$hashUrl""_thumb.png" && -f "../cache/img/$startPath""$hashUrl.png" ]]
|
|
||||||
then
|
|
||||||
echo 1
|
|
||||||
else
|
|
||||||
echo 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if $log
|
|
||||||
then
|
|
||||||
end_time=`date +%s`
|
|
||||||
logDate=`date +'[%a %d %b %Y] [%H:%M:%S]'`
|
|
||||||
echo $logDate `expr $end_time - $start_time`s >> '../cache/logs/success.txt'
|
|
||||||
fi
|
|
0
bin/index.html → cache/img/.gitkeep
vendored
0
cache/index.html → cache/queue/.gitkeep
vendored
0
cache/tmp/.gitkeep
vendored
Normal file
14
composer.json
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"require": {
|
||||||
|
"embed/embed": "^4.4.8",
|
||||||
|
"hassankhan/config": "^3.1",
|
||||||
|
"chrome-php/chrome": "^1.9",
|
||||||
|
"stefangabos/zebra_image": "^2.8.2",
|
||||||
|
"guzzlehttp/psr7": "^2.5"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"App\\": "app/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
datas/config.default.json
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"key": "",
|
||||||
|
"onlyLocalServer": true,
|
||||||
|
"webPage": false,
|
||||||
|
"log": true,
|
||||||
|
"alwaysMakePdf": false,
|
||||||
|
"permitType": [],
|
||||||
|
"expireCache": 12,
|
||||||
|
"maxGenPerBatch": 5,
|
||||||
|
"icoSize": 48,
|
||||||
|
"chromePath" : "",
|
||||||
|
"fileFormat" : "jpeg"
|
||||||
|
}
|
|
@ -1,2 +0,0 @@
|
||||||
Allow from none
|
|
||||||
Deny from all
|
|
250
inc/admin.php
|
@ -1,250 +0,0 @@
|
||||||
<?php
|
|
||||||
$acceptParam['log'] = array(
|
|
||||||
'suspect',
|
|
||||||
'success',
|
|
||||||
'other',
|
|
||||||
'retry'
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isset($ui['pwd'])) {
|
|
||||||
checkAdmin($ui['pwd']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($ui['log']) && !in_array($ui['log'], $acceptParam['log']) || $_SESSION['login'] !== true) {
|
|
||||||
die('Tell me, Mr Anderson, what good is a phone call if you\'re unable to speak ?');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($ui['log']) && empty($ui['ac'])) {
|
|
||||||
$ui['log'] = 'success';
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearLog($log) {
|
|
||||||
if (file_exists('cache/logs/'.$log.'.txt') && is_file('cache/logs/'.$log.'.txt')) {
|
|
||||||
file_put_contents('cache/logs/'.$log.'.txt', '');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseSuccessLog() {
|
|
||||||
if (file_exists('cache/logs/success.txt')) {
|
|
||||||
$file = fopen('cache/logs/success.txt', "r");
|
|
||||||
$res = '';
|
|
||||||
if (!empty($file)) {
|
|
||||||
while (!feof($file)) {
|
|
||||||
$currentLine = trim(fgets($file));
|
|
||||||
$currentLine = explode(' ', $currentLine);
|
|
||||||
$currentLine = str_replace('s', '', $currentLine);
|
|
||||||
if (!empty($currentLine[5])) {
|
|
||||||
$res['genTime'][] = $currentLine[5];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$res['genTime'] = array();
|
|
||||||
}
|
|
||||||
fclose($file);
|
|
||||||
return $res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseFailLog($log) {
|
|
||||||
if (empty($log) || ($log !== 'suspect' && $log !== 'retry' && $log !== 'other')) {
|
|
||||||
die('Are you sure about this.');
|
|
||||||
}
|
|
||||||
$res = '';
|
|
||||||
$list = glob('cache/logs/'.$log.'/*.log');
|
|
||||||
if (!empty($list)) {
|
|
||||||
foreach ($list as $value) {
|
|
||||||
$line = file_get_contents($value);
|
|
||||||
$line = trim($line);
|
|
||||||
$line = explode(' --- ', $line);
|
|
||||||
if (!empty($line[7])) {
|
|
||||||
$res[] = array(
|
|
||||||
$line[1],
|
|
||||||
$line[2],
|
|
||||||
$line[4],
|
|
||||||
'Error '.$line[7].' - '
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$res[] = array(
|
|
||||||
$line[1],
|
|
||||||
$line[2],
|
|
||||||
$line[4],
|
|
||||||
''
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $res;
|
|
||||||
}
|
|
||||||
|
|
||||||
function manualGen($hash, $log) {
|
|
||||||
$line = file_get_contents('cache/logs/'.$log.'/'.$hash.'.log');
|
|
||||||
$line = trim($line);
|
|
||||||
$line = explode(' --- ', $line);
|
|
||||||
$hash = sha1($GLOBALS['config']['salt'].rawurldecode($line[1]));
|
|
||||||
file_put_contents('cache/tmp/manual.txt', $line[1]);
|
|
||||||
chdir('bin/');
|
|
||||||
exec('bash thumb_server.sh manual '.escapeshellarg($hash).' '.escapeshellarg($line[3]).' '.escapeshellarg($line[4]).' 0 1', $result);
|
|
||||||
chdir('../');
|
|
||||||
if ((int)$result[0] === 1) {
|
|
||||||
$res['success'] = 1;
|
|
||||||
$res['filePath'] = pathForFile($line[4], $hash).'.png';
|
|
||||||
return $res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function delAndRegen($hash, $size) {
|
|
||||||
$path = pathForFile($size, $hash);
|
|
||||||
if (file_exists($path.'.png')) {
|
|
||||||
unlink($path.'.png');
|
|
||||||
}
|
|
||||||
if (file_exists($path.'_thumb.png')) {
|
|
||||||
unlink($path.'_thumb.png');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function tryRootDomain($hash, $log) {
|
|
||||||
$line = file_get_contents('cache/logs/'.$log.'/'.$hash.'.log');
|
|
||||||
$line = trim($line);
|
|
||||||
$line = explode(' --- ', $line);
|
|
||||||
$hash = sha1($GLOBALS['config']['salt'].rawurldecode($line[1]));
|
|
||||||
$parts = parse_url($line[1]);
|
|
||||||
$url = $parts['scheme'].'://'.trim($parts['host']);
|
|
||||||
file_put_contents('cache/tmp/manual.txt', $url);
|
|
||||||
chdir('bin/');
|
|
||||||
exec('bash thumb_server.sh manual '.escapeshellarg($hash).' '.escapeshellarg($line[3]).' '.escapeshellarg($line[4]).' 0 1', $result);
|
|
||||||
chdir('../');
|
|
||||||
if ((int)$result[0] === 1) {
|
|
||||||
$res['success'] = 1;
|
|
||||||
$res['filePath'] = pathForFile($line[4], $hash).'.png';
|
|
||||||
$res['base64'] = 'data:image/png;base64,'.base64_encode(file_get_contents($res['filePath']));
|
|
||||||
return $res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function keepThisThumb($hash, $log) {
|
|
||||||
unlink('cache/logs/'.$log.'/'.$hash.'.log');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($ui['clear']) && (int)$ui['clear'] === 1) {
|
|
||||||
clearLog($ui['log']);
|
|
||||||
}
|
|
||||||
if (isset($ui['ac']) && $ui['ac'] === 'delete' && isset($ui['thumb']) && !empty($ui['thumb'])) {
|
|
||||||
$imgUrl = pathForFile('m', $ui['thumb']);
|
|
||||||
if (file_exists($_SERVER['DOCUMENT_ROOT'].'/'.$imgUrl.'.png')) {
|
|
||||||
unlink($_SERVER['DOCUMENT_ROOT'].'/'.$imgUrl.'.png');
|
|
||||||
}
|
|
||||||
if (file_exists($imgUrl.'_thumb.png')) {
|
|
||||||
unlink($_SERVER['DOCUMENT_ROOT'].'/'.$imgUrl.'_thumb.png');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
echo '<div class="hMenu">
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<a href="?p=admin&log=success">Success</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="?p=admin&log=suspect">Suspect</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="?p=admin&log=retry">Retry</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="?p=admin&log=other">Other error</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="?p=admin&ac=delete">Delete thumbshot</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="?logout=1">Logout</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div id="log">';
|
|
||||||
if (isset($ui['ac']) && $ui['ac'] === 'delete') {
|
|
||||||
if (isset($ui['deleteUrl']) && testValidUrl($ui['deleteUrl']) !== true) {
|
|
||||||
unset($ui['deleteUrl']);
|
|
||||||
echo '<div>Not a valid url.</div>';
|
|
||||||
}
|
|
||||||
echo '
|
|
||||||
<form method="post" action="?">
|
|
||||||
<p>
|
|
||||||
<label>Url </label>';
|
|
||||||
if (empty($ui['deleteUrl'])) {
|
|
||||||
echo '<input type="text" name="deleteUrl"/>';
|
|
||||||
} else {
|
|
||||||
echo '<input type="text" name="deleteUrl" value="', $ui['deleteUrl'], '"/>';
|
|
||||||
}
|
|
||||||
echo '</p>
|
|
||||||
<p>
|
|
||||||
<input type="hidden" name="p" value="admin"/>
|
|
||||||
<input type="hidden" name="ac" value="delete"/>
|
|
||||||
<input type="submit"/>
|
|
||||||
</p>
|
|
||||||
</form>';
|
|
||||||
if (!empty($ui['deleteUrl'])) {
|
|
||||||
$ui['deleteUrl'] = trim(rawurldecode($ui['deleteUrl']));
|
|
||||||
$ui['deleteUrl'] = rtrim($ui['deleteUrl'], '/');
|
|
||||||
$hashUrl = sha1($GLOBALS['config']['salt'].$ui['deleteUrl']);
|
|
||||||
$imgUrl = pathForFile('m', $hashUrl);
|
|
||||||
echo '</div>
|
|
||||||
<div id="result">
|
|
||||||
<p>
|
|
||||||
<a href="', $imgUrl, '.png"><img src="', $imgUrl, '_thumb.png"/></a>
|
|
||||||
</p>
|
|
||||||
<a href="?p=admin&ac=delete&thumb=', $hashUrl, '">Delete this thumb</a>
|
|
||||||
</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if (isset($ui['log']) && $ui['log'] === 'success') {
|
|
||||||
$logs = parseSuccessLog();
|
|
||||||
if (!empty($logs)) {
|
|
||||||
$nbThumb = 0;
|
|
||||||
$nbThumb = count($logs['genTime']);
|
|
||||||
echo 'Total request : ', $nbThumb, '</br>';
|
|
||||||
echo 'Moyenne per request : ', round(array_sum($logs['genTime']) / $nbThumb, 2), 's (min (', min($logs['genTime']), 's) max (', max($logs['genTime']), 's))</br>';
|
|
||||||
} else {
|
|
||||||
echo 'No result';
|
|
||||||
}
|
|
||||||
echo '<p class="clear" ><a href="?p=admin&log=success&clear=1">Clear success log</a></p>';
|
|
||||||
}
|
|
||||||
if (isset($ui['log']) && ($ui['log'] === 'suspect' || $ui['log'] === 'retry' || $ui['log'] === 'other')) {
|
|
||||||
if (isset($ui['hash']) && !empty($ui['hash'])) {
|
|
||||||
$ui['hash'] = validHash($ui['hash']);
|
|
||||||
if (isset($ui['root']) && (int)$ui['root'] === 1) {
|
|
||||||
$success = tryRootDomain($ui['hash'], $ui['log']);
|
|
||||||
}
|
|
||||||
if (isset($ui['cache']) && (int)$ui['cache'] === 1) {
|
|
||||||
$success['filePath'] = pathForFile($ui['s'], $ui['hash']).'.png';
|
|
||||||
$success['base64'] = 'data:image/png;base64,'.base64_encode(file_get_contents($success['filePath']));
|
|
||||||
if (!file_exists($success['filePath'])) {
|
|
||||||
$success['base64'] = 'data:image/png;base64,'.base64_encode(file_get_contents('bin/error.png'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isset($ui['keep']) && (int)$ui['keep'] === 1) {
|
|
||||||
keepThisThumb($ui['hash'], $ui['log']);
|
|
||||||
}
|
|
||||||
if (isset($ui['mGen']) && (int)$ui['mGen'] === 1)
|
|
||||||
$success = manualGen($ui['hash'], $ui['log']);
|
|
||||||
}
|
|
||||||
$logs = parseFailLog($ui['log']);
|
|
||||||
if (!empty($logs)) {
|
|
||||||
echo '<ul>';
|
|
||||||
foreach ($logs as $value) {
|
|
||||||
echo '<li>', htmlentities(strip_tags($value['3'])), htmlentities(strip_tags($value['0'])), '</br><a href="?p=admin&log=', $ui['log'], '&hash=', $value['1'], '&mGen=1#result">manuel launch</a> <a href="', htmlentities(strip_tags($value['0'])), '">view site</a> <a href="?p=admin&log=', $ui['log'], '&hash=', $value['1'], '&cache=1&s=', $value['2'], '#result">view image in cache</a></li>';
|
|
||||||
}
|
|
||||||
echo '</ul>';
|
|
||||||
} else {
|
|
||||||
echo 'No result';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
echo '</div>';
|
|
||||||
if (!empty($success)) {
|
|
||||||
echo '<div id="result">';
|
|
||||||
echo '<img src="', $success['base64'], '" style="width:100%;"/>';
|
|
||||||
echo '<p><input type="text" value="http://', $_SERVER['SERVER_NAME'], '/', $success['filePath'], '" onclick="this.select()"/></p>';
|
|
||||||
echo '<a href="?p=admin&log=', $ui['log'], '&hash=', $ui['hash'], '&root=1">Try with root domain</a> / ';
|
|
||||||
echo '<a href="?p=admin&log=', $ui['log'], '&hash=', $ui['hash'], '&keep=1">Keep this thumbshot</a>';
|
|
||||||
echo '</div>';
|
|
||||||
}
|
|
||||||
?>
|
|
|
@ -1,41 +0,0 @@
|
||||||
<form method="post">
|
|
||||||
<p>
|
|
||||||
<input type="url" placeholder="<?php echo $defUrl; ?>" value="<?php echo $defUrl; ?>" name="url"/>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<label>Size</label>
|
|
||||||
<select name="s">
|
|
||||||
<?php
|
|
||||||
foreach ($GLOBALS['config']['thumbSize'] as $key => $value) {
|
|
||||||
if ($value === $width) {
|
|
||||||
echo '<option value="', $key, '" selected="selected">', $value, '</option>';
|
|
||||||
} else {
|
|
||||||
echo '<option value="', $key, '">', $value, '</option>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</select>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<label>Force refresh</label>
|
|
||||||
<input type="checkbox" value="1" name="fr" />
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<input type="hidden" name="token" value="<?php echo genToken(); ?>" />
|
|
||||||
<input type="submit" value="Generate"/>
|
|
||||||
</p>
|
|
||||||
<p class="info">
|
|
||||||
<a href="http://forge.leslibres.org/projects/soshot">Homepage</a> <a href="http://knah-tsaeb.org/contact-page/">Contact</a>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
<?php
|
|
||||||
if (!empty($success)) {
|
|
||||||
echo '<div id="result">';
|
|
||||||
echo '<h3>This image will be removed in ', $GLOBALS['config']['expireCache'], 'h</h3>';
|
|
||||||
echo '<img src="data:image/png;base64,',base64_encode(file_get_contents($success['thumb'])), '"/>';
|
|
||||||
echo '<p><label>Thumbshot</label><input type="text" value="http://', $_SERVER['SERVER_NAME'], '/', $success['thumb'], '" onclick="this.select()"/>';
|
|
||||||
if ($GLOBALS['config']['onlyThumb'] === false) {
|
|
||||||
echo '<p><label>1280x1024</label><input type="text" value="http://', $_SERVER['SERVER_NAME'], '/', $success['thumb'], '" onclick="this.select()"/></p>';
|
|
||||||
}
|
|
||||||
echo '</div>';
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
<?php
|
|
||||||
if(file_exists('cache/config/genConf.php') && $GLOBALS['config']['pwd'] !== 'install'){
|
|
||||||
die ('Seriously Dude, Where\'s My Car ?');
|
|
||||||
}
|
|
||||||
|
|
||||||
if(isset($ui['passOne'])){
|
|
||||||
savePass($ui['passOne'], $ui['passTwo'], $ui['token']);
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
<div class="message">
|
|
||||||
This is the key for generate thumbnail whith GET method. Save it !
|
|
||||||
<p class="alert">
|
|
||||||
<?php
|
|
||||||
echo $serverKey;
|
|
||||||
?>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<form action="?" method="post">
|
|
||||||
<p>
|
|
||||||
<label>Password</label>
|
|
||||||
<input type="password" name="passOne" />
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<label>Confirm</label>
|
|
||||||
<input type="password" name="passTwo" />
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<input type="hidden" name="token" value="<?php echo genToken();?>"/>
|
|
||||||
<input type="hidden" name="p" value="install"/>
|
|
||||||
<input type="submit">
|
|
||||||
</p>
|
|
||||||
</form>
|
|
|
@ -1,11 +0,0 @@
|
||||||
<form action="?" method="post">
|
|
||||||
<p>
|
|
||||||
<label>Password</label>
|
|
||||||
<input type="password" name="pwd" />
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<input type="hidden" name="token" value="<?php echo genToken();?>"/>
|
|
||||||
<input type="hidden" name="p" value="admin" />
|
|
||||||
<input type="submit">
|
|
||||||
</p>
|
|
||||||
</form>
|
|
608
index.php
|
@ -1,608 +0,0 @@
|
||||||
<?php
|
|
||||||
session_start();
|
|
||||||
|
|
||||||
if (empty($_SESSION['login'])) {
|
|
||||||
$_SESSION['login'] = false;
|
|
||||||
}
|
|
||||||
// change if you want no inpact
|
|
||||||
date_default_timezone_set('Europe/Paris');
|
|
||||||
// soshot only accept request by 127.0.0.1
|
|
||||||
$GLOBALS['config']['onlyLocalServer'] = false;
|
|
||||||
// No form for post url only acces by GET method
|
|
||||||
$GLOBALS['config']['NoWebPage'] = false;
|
|
||||||
// After 3 critical error ban user
|
|
||||||
$GLOBALS['config']['maxErrorBeforeBan'] = 3;
|
|
||||||
// Ban for 60 minutes
|
|
||||||
$GLOBALS['config']['banTime'] = 60;
|
|
||||||
// default url for form
|
|
||||||
$GLOBALS['config']['defaultUrl'] = 'https://duckduckgo.com/';
|
|
||||||
// default size for thumbnail
|
|
||||||
$GLOBALS['config']['defaultThumbSize'] = '120x90';
|
|
||||||
// generate only thumbnail or generate thumbnail + 1280x1024 image
|
|
||||||
$GLOBALS['config']['onlyThumb'] = false;
|
|
||||||
// list of available size for thumb
|
|
||||||
$GLOBALS['config']['thumbSize'] = array(
|
|
||||||
's' => '120x90',
|
|
||||||
'm' => '200x160',
|
|
||||||
'l' => '300x240',
|
|
||||||
'xl' => '400x320',
|
|
||||||
'xxl' => '500x400'
|
|
||||||
);
|
|
||||||
// Remove image older than 12 hours
|
|
||||||
$GLOBALS['config']['expireCache'] = 12;
|
|
||||||
// Disable exec command and use cron task
|
|
||||||
$GLOBALS['config']['disableExec'] = true;
|
|
||||||
// Enable log for success, suspect, error
|
|
||||||
$GLOBALS['config']['log'] = false;
|
|
||||||
|
|
||||||
if (file_exists('cache/config/options.php')) {
|
|
||||||
require 'cache/config/options.php';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (get_magic_quotes_gpc()) {
|
|
||||||
function stripslashes_deep($value) {
|
|
||||||
$value = is_array($value) ? array_map('stripslashes_deep', $value) : stripslashes($value);
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
$_POST = array_map('stripslashes_deep', $_POST);
|
|
||||||
$_GET = array_map('stripslashes_deep', $_GET);
|
|
||||||
$_COOKIE = array_map('stripslashes_deep', $_COOKIE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($_POST)) {
|
|
||||||
$ui = $_POST;
|
|
||||||
$ui['request'] = 'form';
|
|
||||||
$ui['iw'] = 1;
|
|
||||||
unset($_POST);
|
|
||||||
}
|
|
||||||
if (!empty($_GET)) {
|
|
||||||
$ui = $_GET;
|
|
||||||
$ui['request'] = 'api';
|
|
||||||
unset($_GET);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($ui['request'])) {
|
|
||||||
$ui['request'] = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($ui['p'])) {
|
|
||||||
$ui['p'] = 'index';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($ui['fr'])) {
|
|
||||||
$ui['fr'] = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($ui['logout']) && (int)$ui['logout'] === 1) {
|
|
||||||
session_destroy();
|
|
||||||
header("Location:?");
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!file_exists('cache/config/genConf.php')) {
|
|
||||||
$serverKey = install();
|
|
||||||
$ui['p'] = 'install';
|
|
||||||
}
|
|
||||||
|
|
||||||
require 'cache/config/genConf.php';
|
|
||||||
|
|
||||||
if ($GLOBALS['config']['pwd'] === 'install' && $ui['p'] !== 'install') {
|
|
||||||
reloadInstall();
|
|
||||||
header("Location:?");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (($GLOBALS['config']['onlyLocalServer'] === true && $_SERVER['REMOTE_ADDR'] !== '127.0.0.1') || checkIfBan() === true) {
|
|
||||||
header("HTTP/1.0 404 Not Found");
|
|
||||||
echo "<h1>404 Not Found</h1>";
|
|
||||||
echo "The page that you have requested could not be found.";
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($GLOBALS['config']['NoWebPage'] === true && empty($_GET)) {
|
|
||||||
header("HTTP/1.0 404 Not Found");
|
|
||||||
echo "<h1>404 Not Found</h1>";
|
|
||||||
echo "The page that you have requested could not be found.";
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
function testExistImg($file) {
|
|
||||||
if ($GLOBALS['config']['onlyThumb'] === true) {
|
|
||||||
if (file_exists($file.'_thumb.png')) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (file_exists($file.'_thumb.png') && file_exists($file.'.png')) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run the bash script for generate thumbnail
|
|
||||||
*
|
|
||||||
* @author Knah Tsaeb
|
|
||||||
* @date 2013-02-12
|
|
||||||
* @param $url (string) url for thumbshot
|
|
||||||
* @param $hashUrl (hash) hash($url)
|
|
||||||
* @param $width (string) size of thumbnail 190x90
|
|
||||||
* @param $onlyThumb (bool)
|
|
||||||
* @param $waitForResult (bool)
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
function launchScript($url, $hashUrl, $width, $size, $onlyThumb, $waitForResult = false) {
|
|
||||||
$hashUrl = escapeshellarg($hashUrl);
|
|
||||||
$url = escapeshellarg($url);
|
|
||||||
$width = escapeshellarg($width);
|
|
||||||
if ($GLOBALS['config']['onlyThumb'] === true) {
|
|
||||||
$onlyThumb = 1;
|
|
||||||
} else {
|
|
||||||
$onlyThumb = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($GLOBALS['config']['disableExec'] === false) {
|
|
||||||
chdir('bin/');
|
|
||||||
if ($waitForResult === false) {
|
|
||||||
exec('bash thumb_server.sh '.$url.' '.$hashUrl.' '.$width.' '.$size.' '.$onlyThumb.' > /dev/null &', $result);
|
|
||||||
} else {
|
|
||||||
exec('bash thumb_server.sh '.$url.' '.$hashUrl.' '.$width.' '.$size.' '.$onlyThumb.' 1', $result);
|
|
||||||
}
|
|
||||||
chdir('../');
|
|
||||||
} else {
|
|
||||||
makeQueueFile($url, $hashUrl, $width, $size, $onlyThumb);
|
|
||||||
$result = 0;
|
|
||||||
}
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeQueueFile($url, $hashUrl, $width, $size, $onlyThumb) {
|
|
||||||
$url = str_replace('\'', '', $url);
|
|
||||||
$hashUrl = str_replace('\'', '', $hashUrl);
|
|
||||||
$width = str_replace('\'', '', $width);
|
|
||||||
if (!file_exists('cache/cronTask/'.$hashUrl.'.hash')) {
|
|
||||||
$data = $url.' '.$hashUrl.' '.$width.' '.$size.' '.$onlyThumb. "\n";
|
|
||||||
file_put_contents('cache/cronTask/'.$hashUrl.'.hash', $data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function testValidUrl($url) {
|
|
||||||
$url = trim($url);
|
|
||||||
if (filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED)) {
|
|
||||||
$url = parse_url($url);
|
|
||||||
if (!in_array($url['scheme'], array(
|
|
||||||
'http',
|
|
||||||
'https'
|
|
||||||
))) {
|
|
||||||
return array('msg' => 'Url must be start by http or https.');
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return array('msg' => 'Not a valid url.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function genToken() {
|
|
||||||
$token = sha1(uniqid(rand(), true).'_'.mt_rand());
|
|
||||||
$_SESSION['token'] = $token;
|
|
||||||
$_SESSION['tokenTime'] = time();
|
|
||||||
return $token;
|
|
||||||
}
|
|
||||||
|
|
||||||
function verifToken($token) {
|
|
||||||
if ($token !== $_SESSION['token'] || $_SESSION['tokenTime'] <= time() - 24000) {
|
|
||||||
ban();
|
|
||||||
die('So Long, and Thanks for All the Fish.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkIfBan() {
|
|
||||||
require 'cache/logs/banUser.php';
|
|
||||||
$userIp = $_SERVER['REMOTE_ADDR'];
|
|
||||||
if (isset($banList[$userIp]) && $banList[$userIp]['nbBan'] >= $GLOBALS['config']['maxErrorBeforeBan'] && $banList[$userIp]['lastBan'] + $GLOBALS['config']['banTime'] > time()) {
|
|
||||||
return true;
|
|
||||||
} elseif (isset($banList[$userIp]) && $banList[$userIp]['lastBan'] + $GLOBALS['config']['banTime'] < time()) {
|
|
||||||
unban();
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function ban() {
|
|
||||||
require 'cache/logs/banUser.php';
|
|
||||||
$userIp = $_SERVER['REMOTE_ADDR'];
|
|
||||||
if (isset($banList[$userIp])) {
|
|
||||||
$banList[$userIp]['lastBan'] = time();
|
|
||||||
$banList[$userIp]['nbBan']++;
|
|
||||||
} else {
|
|
||||||
$banList[$userIp]['lastBan'] = time();
|
|
||||||
$banList[$userIp]['nbBan'] = 1;
|
|
||||||
}
|
|
||||||
file_put_contents('cache/logs/banUser.php', "<?php\n\$banList=".var_export($banList, true).";\n?>");
|
|
||||||
}
|
|
||||||
|
|
||||||
function unBan() {
|
|
||||||
require 'cache/logs/banUser.php';
|
|
||||||
$userIp = $_SERVER['REMOTE_ADDR'];
|
|
||||||
unset($banList[$userIp]);
|
|
||||||
file_put_contents('cache/logs/banUser.php', "<?php\n\$banList=".var_export($banList, true).";\n?>");
|
|
||||||
}
|
|
||||||
|
|
||||||
function install() {
|
|
||||||
if (!is_writable('cache')) {
|
|
||||||
die('Make dir "cache" writable');
|
|
||||||
}
|
|
||||||
if (!is_dir('cache/config') && !mkdir('cache/config', 0705)) {
|
|
||||||
die('Error on create dir "cache/config".');
|
|
||||||
}
|
|
||||||
if (!is_dir('cache/img') && !mkdir('cache/img', 0705)) {
|
|
||||||
die('Error on create dir "cache/img".');
|
|
||||||
}
|
|
||||||
if (!is_dir('cache/logs') && !mkdir('cache/logs', 0705)) {
|
|
||||||
die('Error on create dir "cache/logs".');
|
|
||||||
}
|
|
||||||
if (!is_dir('cache/logs/suspect') && !mkdir('cache/logs/suspect', 0705)) {
|
|
||||||
die('Error on create dir "cache/logs/suspect".');
|
|
||||||
}
|
|
||||||
if (!is_dir('cache/logs/retry') && !mkdir('cache/logs/retry', 0705)) {
|
|
||||||
die('Error on create dir "cache/logs/retry".');
|
|
||||||
}
|
|
||||||
if (!is_dir('cache/logs/other') && !mkdir('cache/logs/other', 0705)) {
|
|
||||||
die('Error on create dir "cache/logs/other".');
|
|
||||||
}
|
|
||||||
if (!is_dir('cache/tmp') && !mkdir('cache/tmp', 0705)) {
|
|
||||||
die('Error on create dir "cache/tmp".');
|
|
||||||
}
|
|
||||||
if (!is_dir('cache/cronTask') && !mkdir('cache/cronTask', 0705)) {
|
|
||||||
die('Error on create dir "cache/cronTask".');
|
|
||||||
}
|
|
||||||
$salt = sha1(uniqid(rand(), true).'_'.mt_rand());
|
|
||||||
$serverKey = substr(sha1(uniqid(rand(), true).'_'.mt_rand().$salt), 0, 12);
|
|
||||||
$encryptServerKey = sha1($serverKey.$salt);
|
|
||||||
|
|
||||||
if (!is_file('cache/config/genConf.php')) {
|
|
||||||
file_put_contents('cache/config/genConf.php', "<?php\n\$GLOBALS['config']['serverKey'] = '$encryptServerKey';\n\$GLOBALS['config']['salt'] = '$salt';\n\$GLOBALS['config']['pwd'] = 'install';\n?>");
|
|
||||||
}
|
|
||||||
if (!is_file('cache/config/serverOptions.php')) {
|
|
||||||
touch('cache/config/serverOptions.php');
|
|
||||||
}
|
|
||||||
if (!is_file('cache/index.html')) {
|
|
||||||
touch('cache/index.html');
|
|
||||||
}
|
|
||||||
if (!is_file('cache/config/options.php')) {
|
|
||||||
file_put_contents('cache/config/options.php', "<?php\n\n?>");
|
|
||||||
}
|
|
||||||
if (!is_file('cache/logs/banUser.php')) {
|
|
||||||
file_put_contents('cache/logs/banUser.php', "<?php\n\n?>");
|
|
||||||
}
|
|
||||||
$GLOBALS['config']['serverKey'] = $encryptServerKey;
|
|
||||||
$GLOBALS['config']['salt'] = $salt;
|
|
||||||
$GLOBALS['config']['pwd'] = 'install';
|
|
||||||
return $serverKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkInstall() {
|
|
||||||
if (!is_file('.htaccess')) {
|
|
||||||
file_put_contents('.htaccess', "AddDefaultCharset UTF-8\nOptions -Indexes\nDirectoryIndex index.php index.html\nFileETag none\nSetOutputFilter DEFLATE\n");
|
|
||||||
}
|
|
||||||
if (!is_file('cache/logs/.htaccess')) {
|
|
||||||
file_put_contents('cache/logs/.htaccess', "Allow from none\nDeny from all\n");
|
|
||||||
}
|
|
||||||
if (!is_file('cache/config/.htaccess')) {
|
|
||||||
file_put_contents('cache/config/.htaccess', "Allow from none\nDeny from all\n");
|
|
||||||
}
|
|
||||||
if (!is_file('cache/cronTask/.htaccess')) {
|
|
||||||
file_put_contents('cache/config/.htaccess', "Allow from none\nDeny from all\n");
|
|
||||||
}
|
|
||||||
if (!is_file('bin/.htaccess')) {
|
|
||||||
file_put_contents('bin/.htaccess', "Allow from none\nDeny from all\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeOlderThan($dir = 'cache/img/shortLive') {
|
|
||||||
if (is_dir($dir)) {
|
|
||||||
$objects = scandir($dir);
|
|
||||||
foreach ($objects as $object) {
|
|
||||||
if ($object !== '.' && $object !== '..' && $object) {
|
|
||||||
if (filetype($dir.'/'.$object) === 'dir') {
|
|
||||||
removeOlderThan($dir.'/'.$object);
|
|
||||||
} else {
|
|
||||||
if (fileatime($dir.'/'.$object) < time() - 3600 * $GLOBALS['config']['expireCache']) {
|
|
||||||
unlink($dir.'/'.$object);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
reset($objects);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function pathForFile($size, $hashUrl) {
|
|
||||||
$startPath = substr($hashUrl, 0, 2).'/'.substr($hashUrl, 2, 2).'/';
|
|
||||||
$file = 'cache/img/'.$size.'/'.$startPath.$hashUrl;
|
|
||||||
return $file;
|
|
||||||
}
|
|
||||||
|
|
||||||
function validHash($hash) {
|
|
||||||
if (empty($hash)) {
|
|
||||||
die('You talking to me');
|
|
||||||
}
|
|
||||||
if (!preg_match('/^[0-9a-f]{40}$/i', $hash)) {
|
|
||||||
die('I don\'t understand biiip bip bip biiiip bip bip bip biiiiiiip biip ...');
|
|
||||||
}
|
|
||||||
return $hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkAdmin($pwd) {
|
|
||||||
$pwd = sha1($GLOBALS['config']['salt'].$pwd.$GLOBALS['config']['serverKey']);
|
|
||||||
if (validHash($pwd) !== $GLOBALS['config']['pwd']) {
|
|
||||||
ban();
|
|
||||||
die('1, 2, 3, 4, 5 ? That\'s amazing ! I\'ve got the same combination on my luggage !');
|
|
||||||
}
|
|
||||||
$_SESSION['login'] = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function savePass($passOne, $passTwo, $token) {
|
|
||||||
verifToken($token);
|
|
||||||
if ($passOne !== $passTwo || empty($passOne) || empty($passTwo)) {
|
|
||||||
reloadInstall();
|
|
||||||
} else {
|
|
||||||
$GLOBALS['config']['pwd'] = sha1($GLOBALS['config']['salt'].$passOne.$GLOBALS['config']['serverKey']);
|
|
||||||
$confServerKey = $GLOBALS['config']['serverKey'];
|
|
||||||
$confSalt = $GLOBALS['config']['salt'];
|
|
||||||
$confPwd = $GLOBALS['config']['pwd'];
|
|
||||||
$confFile = '
|
|
||||||
<?php
|
|
||||||
$GLOBALS[\'config\'][\'serverKey\'] = \''.$confServerKey.'\';
|
|
||||||
$GLOBALS[\'config\'][\'salt\'] = \''.$confSalt.'\';
|
|
||||||
$GLOBALS[\'config\'][\'pwd\'] = \''.$confPwd.'\';
|
|
||||||
?>';
|
|
||||||
file_put_contents('cache/config/genConf.php', $confFile);
|
|
||||||
}
|
|
||||||
header("Location:?");
|
|
||||||
}
|
|
||||||
|
|
||||||
function reloadInstall() {
|
|
||||||
array_map('unlink', glob("cache/config/*"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Améliore la sortie print
|
|
||||||
*
|
|
||||||
* @author Tatane http://www.tatane.info/index.php/print_rn
|
|
||||||
* @author http://www.blog.cactuscrew.com/77-print_rn.html
|
|
||||||
* @param $data (array) tableau ou variable à examiner
|
|
||||||
* @param $name (string) nom a afficher
|
|
||||||
* @return false affiche les clef valeur du tableau $data
|
|
||||||
* @example n_print($array, 'Tableau de valeur');
|
|
||||||
*/
|
|
||||||
function n_print($data, $name = '') {
|
|
||||||
$aBackTrace = debug_backtrace();
|
|
||||||
echo '<h2>', $name, '</h2>';
|
|
||||||
echo '<fieldset style="border: 1px solid orange; padding: 5px;color: #333; background-color: #fff;">';
|
|
||||||
echo '<legend style="border:1px solid orange;padding: 1px;background-color:#eee;color:orange;">
|
|
||||||
', basename($aBackTrace[0]['file']), ' ligne => ', $aBackTrace[0]['line'], '
|
|
||||||
</legend>';
|
|
||||||
echo '<pre>', htmlentities(print_r($data, 1)), '</pre>';
|
|
||||||
echo '
|
|
||||||
</fieldset>
|
|
||||||
<br />
|
|
||||||
';
|
|
||||||
}
|
|
||||||
|
|
||||||
function printThumbShot($file) {
|
|
||||||
ob_end_clean();
|
|
||||||
header("Content-type: image/png");
|
|
||||||
header('Expires: ', gmdate('D, d M Y H:i:s', time()).' GMT');
|
|
||||||
echo file_get_contents($file.'_thumb.png');
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPage($page) {
|
|
||||||
$page = htmlspecialchars($page);
|
|
||||||
switch ($page) {
|
|
||||||
case 'login' :
|
|
||||||
return 'inc/login.php';
|
|
||||||
break;
|
|
||||||
case 'install' :
|
|
||||||
return 'inc/install.php';
|
|
||||||
break;
|
|
||||||
case 'admin' :
|
|
||||||
return 'inc/admin.php';
|
|
||||||
break;
|
|
||||||
default :
|
|
||||||
return 'inc/index.php';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function testIfImg($url) {
|
|
||||||
$ext = strtolower(pathinfo($url, PATHINFO_EXTENSION));
|
|
||||||
if ($ext === 'jpg' || $ext === 'jpeg' || $ext === 'png' || $ext === 'gif') {
|
|
||||||
return $ext;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeImgThumb($url, $ext, $hashUrl, $width, $path, $onlyThumb) {
|
|
||||||
if (!function_exists('imagecreatefromjpeg')) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$image = file_get_contents($url);
|
|
||||||
$fullSize = file_put_contents('cache/tmp/'.$hashUrl.'.'.$ext, $image);
|
|
||||||
if ($ext === 'jpg' || $ext === 'jpeg') {
|
|
||||||
$tmpImg = imagecreatefromjpeg('cache/tmp/'.$hashUrl.'.'.$ext);
|
|
||||||
}
|
|
||||||
if ($ext === 'png') {
|
|
||||||
$tmpImg = imagecreatefrompng('cache/tmp/'.$hashUrl.'.'.$ext);
|
|
||||||
}
|
|
||||||
if ($ext === 'gif') {
|
|
||||||
$tmpImg = imagecreatefromgif('cache/tmp/'.$hashUrl.'.'.$ext);
|
|
||||||
}
|
|
||||||
if (!$tmpImg) {
|
|
||||||
unlink('cache/tmp/'.$hashUrl.'.'.$ext);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$w = imagesx($tmpImg);
|
|
||||||
$h = imagesy($tmpImg);
|
|
||||||
$ystart = 0;
|
|
||||||
$yheight = $h;
|
|
||||||
if ($h > $w) { $ystart = ($h / 2) - ($w / 2);
|
|
||||||
$yheight = $w / 2;
|
|
||||||
}
|
|
||||||
$nh = min(floor(($h * $width) / $w), $width);
|
|
||||||
$im2 = imagecreatetruecolor($width, $nh);
|
|
||||||
imagecopyresampled($im2, $tmpImg, 0, 0, 0, $ystart, $width, $nh, $w, $yheight);
|
|
||||||
$tempname = 'cache/tmp/'.$hashUrl.'_TEMP.png';
|
|
||||||
imagepng($im2, $tempname, 9);
|
|
||||||
if (!is_dir($path)) {
|
|
||||||
mkdir($path, 0775, true);
|
|
||||||
}
|
|
||||||
imagedestroy($tmpImg);
|
|
||||||
imagedestroy($im2);
|
|
||||||
unlink('cache/tmp/'.$hashUrl.'.'.$ext);
|
|
||||||
rename($tempname, $path.$hashUrl.'_thumb.png');
|
|
||||||
if (file_exists($path.$hashUrl.'_thumb.png')) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
$image = file_get_contents('http://www.url.com/image.jpg');
|
|
||||||
file_put_contents('/images/image.jpg', $image); //save the image on your server
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
checkInstall();
|
|
||||||
removeOlderThan();
|
|
||||||
|
|
||||||
if (empty($defUrl)) {
|
|
||||||
$defUrl = $GLOBALS['config']['defaultUrl'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($ui['s']) && array_key_exists($ui['s'], $GLOBALS['config']['thumbSize'])) {
|
|
||||||
$width = $GLOBALS['config']['thumbSize'][$ui['s']];
|
|
||||||
} else {
|
|
||||||
$width = $GLOBALS['config']['defaultThumbSize'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate or return img
|
|
||||||
if (!empty($ui['request']) && $ui['p'] !== 'install' && $ui['p'] !== 'login' && $ui['p'] !== 'admin') {
|
|
||||||
if (empty($ui['url'])) {
|
|
||||||
die('You see in this world there\'s two kinds of people, my friend. Those with loaded guns, and those who dig. You dig.');
|
|
||||||
}
|
|
||||||
$ui['url'] = trim(rawurldecode($ui['url']));
|
|
||||||
$ui['url'] = rtrim($ui['url'], '/');
|
|
||||||
$hashUrl = sha1($GLOBALS['config']['salt'].$ui['url']);
|
|
||||||
$startPath = substr($hashUrl, 0, 2).'/'.substr($hashUrl, 2, 2).'/';
|
|
||||||
$file = 'cache/img/'.$ui['s'].'/'.$startPath.$hashUrl;
|
|
||||||
$testUrl = testValidUrl($ui['url']);
|
|
||||||
$defUrl = $ui['url'];
|
|
||||||
if ($ui['request'] === 'form') {
|
|
||||||
$file = 'cache/img/shortLive/'.$startPath.$hashUrl;
|
|
||||||
$file = 'cache/img/shortLive/'.$startPath.$hashUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (testExistImg($file) === true && (int)$ui['fr'] !== 1) {
|
|
||||||
if ($ui['request'] === 'api') {
|
|
||||||
printThumbShot($file);
|
|
||||||
}
|
|
||||||
if ($ui['request'] === 'form') {
|
|
||||||
$success = array(
|
|
||||||
'normal' => $file.'.png',
|
|
||||||
'thumb' => $file.'_thumb.png'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ($ui['request'] === 'api') {
|
|
||||||
if (empty($ui['key']) || empty($ui['url'])) {
|
|
||||||
die('Are you Ken ?');
|
|
||||||
}
|
|
||||||
if (sha1($ui['key'].$GLOBALS['config']['salt']) !== $GLOBALS['config']['serverKey']) {
|
|
||||||
ban();
|
|
||||||
die('I take a chips and give it to Godzilla. I give high kick in Chuck Norris face and I go to ... Humm .... Ehh .... Arg ....... KAMOULOX ! Well done Jean Pierre.');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
verifToken($ui['token']);
|
|
||||||
}
|
|
||||||
if ($testUrl !== true) {
|
|
||||||
if (!file_exists('cache/logs/'.$hashUrl.'log')) {
|
|
||||||
file_put_contents('cache/logs/suspect/'.$hashUrl.'.log', $_SERVER['REMOTE_ADDR'].' --- '.$ui['url'].' --- '.$hashUrl.' --- '.$width.' --- '.$ui['s'].' --- '.$GLOBALS['config']['onlyThumb'].' --- true'."\n");
|
|
||||||
}
|
|
||||||
if ($ui['request'] === 'api') {
|
|
||||||
printThumbShot('bin/error');
|
|
||||||
} else {
|
|
||||||
$success = array(
|
|
||||||
'normal' => 'bin/error.png',
|
|
||||||
'thumb' => 'bin/error_thumb.png'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$ext = testIfImg($ui['url']);
|
|
||||||
if ($ext !== false) {
|
|
||||||
$genWidth = explode("x", $width);
|
|
||||||
if ($ui['request'] === 'api') {
|
|
||||||
$makeImg = makeImgThumb($ui['url'], $ext, $hashUrl, $genWidth[0], 'cache/img/'.$ui['s'].'/'.$startPath, $GLOBALS['config']['onlyThumb']);
|
|
||||||
} else {
|
|
||||||
$makeImg = makeImgThumb($ui['url'], $ext, $hashUrl, $genWidth[0], 'cache/img/shortLive/'.$startPath, $GLOBALS['config']['onlyThumb']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($makeImg === true) {
|
|
||||||
$GLOBALS['config']['disableExec'] = true;
|
|
||||||
if ($ui['request'] === 'api') {
|
|
||||||
printThumbShot($file);
|
|
||||||
} else {
|
|
||||||
$success = array(
|
|
||||||
'normal' => $file.'.png',
|
|
||||||
'thumb' => $file.'_thumb.png'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((isset($ui['iw']) && (int)$ui['iw'] === 1 && $ui['request'] === 'api')) {
|
|
||||||
$res = launchScript($defUrl, $hashUrl, $width, $ui['s'], $GLOBALS['config']['onlyThumb'], true);
|
|
||||||
} elseif (isset($ui['iw']) && (int)$ui['iw'] === 1 && $ui['request'] === 'form') {
|
|
||||||
$res = launchScript($defUrl, $hashUrl, $width, 'shortLive', $GLOBALS['config']['onlyThumb'], true);
|
|
||||||
} else {
|
|
||||||
$res = launchScript($defUrl, $hashUrl, $width, $ui['s'], $GLOBALS['config']['onlyThumb'], false);
|
|
||||||
}
|
|
||||||
if ($ui['request'] === 'api') {
|
|
||||||
$file = 'bin/loadingGen';
|
|
||||||
printThumbShot($file);
|
|
||||||
} else {
|
|
||||||
if ($GLOBALS['config']['disableExec'] === true) {
|
|
||||||
$file = 'bin/loadingGen';
|
|
||||||
}
|
|
||||||
$success = array(
|
|
||||||
'normal' => $file.'.png',
|
|
||||||
'thumb' => $file.'_thumb.png'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>SoShot</title>
|
|
||||||
<meta name="description" content="Personal webshot">
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<?php
|
|
||||||
require getPage($ui['p']);
|
|
||||||
?>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
12
public/.htaccess
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
AddDefaultCharset UTF-8
|
||||||
|
Options -Indexes
|
||||||
|
DirectoryIndex index.php index.html
|
||||||
|
FileETag none
|
||||||
|
SetOutputFilter DEFLATE
|
||||||
|
|
||||||
|
|
||||||
|
RewriteEngine On
|
||||||
|
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
RewriteRule ^(.*)$ index.php [QSA,L]
|
12
public/assets/css/fork-awesome.min.css
vendored
Normal file
1
public/assets/css/fork-awesome.min.css.map
Normal file
110
public/assets/css/style.css
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
html {
|
||||||
|
font-family: arial, sans-serif;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 2em;
|
||||||
|
background-color: #ffffffcb;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.w3-sidebar {
|
||||||
|
z-index: 3;
|
||||||
|
width: 250px;
|
||||||
|
top: 43px;
|
||||||
|
height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #8AC007;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover,
|
||||||
|
.title:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:visited {
|
||||||
|
color: #8AC007;
|
||||||
|
}
|
||||||
|
|
||||||
|
.centerBlock {
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.homeForm {
|
||||||
|
width: 49.9%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.w3-bar {
|
||||||
|
background-color: #333333 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mySidebar {
|
||||||
|
top: inherit;
|
||||||
|
background-color: initial !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mySidebar a:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.w3-bar-block .w3-bar-item {
|
||||||
|
padding: 4px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.w3-bar {
|
||||||
|
background-color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#myFooter {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.w3-main {
|
||||||
|
margin: 64px auto 46px 270px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.soshot-main {
|
||||||
|
padding-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
color: #009688;
|
||||||
|
}
|
||||||
|
|
||||||
|
form label {
|
||||||
|
width: 48% !important;
|
||||||
|
display: inline-block;
|
||||||
|
text-align: right;
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.w3-input,
|
||||||
|
.w3-select {
|
||||||
|
display: inline-block;
|
||||||
|
width: 48%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width:768px) {
|
||||||
|
|
||||||
|
#mySidebar {
|
||||||
|
background-color: #ffffffcb !important;
|
||||||
|
width: 100%;
|
||||||
|
height: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
1994
public/assets/css/w3.css
Normal file
BIN
public/assets/fonts/forkawesome-webfont.eot
Normal file
3232
public/assets/fonts/forkawesome-webfont.svg
Normal file
After Width: | Height: | Size: 547 KiB |
BIN
public/assets/fonts/forkawesome-webfont.ttf
Normal file
BIN
public/assets/fonts/forkawesome-webfont.woff
Normal file
BIN
public/assets/fonts/forkawesome-webfont.woff2
Normal file
BIN
public/assets/img/wikipedia_logiciel_libre.png
Normal file
After Width: | Height: | Size: 331 KiB |
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 99 KiB |
68
public/index.php
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
<?php
|
||||||
|
require_once '../vendor/autoload.php';
|
||||||
|
|
||||||
|
ini_set('display_errors', '1');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Améliore la sortie print
|
||||||
|
*
|
||||||
|
* @author Tatane http://www.tatane.info/index.php/print_rn
|
||||||
|
* @author http://www.blog.cactuscrew.com/77-print_rn.html
|
||||||
|
* @param $data (array) tableau ou variable à examiner
|
||||||
|
* @param $name (string) nom a afficher
|
||||||
|
* @return false affiche les clef valeur du tableau $data
|
||||||
|
* @example n_print($array, 'Tableau de valeur');
|
||||||
|
*/
|
||||||
|
function n_print($data, $name = '') {
|
||||||
|
$aBackTrace = debug_backtrace();
|
||||||
|
echo '<h2>', $name, '</h2>';
|
||||||
|
echo '<fieldset style="border: 1px solid orange; padding: 5px;color: #333; background-color: #fff;">';
|
||||||
|
echo '<legend style="border:1px solid orange;padding: 1px;background-color:#eee;color:orange;">
|
||||||
|
', basename($aBackTrace[0]['file']), ' ligne => ', $aBackTrace[0]['line'], '
|
||||||
|
</legend>';
|
||||||
|
echo '<pre>', htmlentities(print_r($data, 1)), '</pre>';
|
||||||
|
echo '
|
||||||
|
</fieldset>
|
||||||
|
<br />
|
||||||
|
';
|
||||||
|
}
|
||||||
|
|
||||||
|
use App\Router;
|
||||||
|
use App\Utils\Error;
|
||||||
|
use Noodlehaus\Config;
|
||||||
|
|
||||||
|
$router = new Router();
|
||||||
|
|
||||||
|
$router->addRoute(['/', '/index.php', '/index'], 'App\Controllers\Home', 'GET');
|
||||||
|
$router->addRoute('/api', 'App\Controllers\Result', 'GET');
|
||||||
|
$router->addRoute('/hmac', 'App\Controllers\GenHmac', 'GET');
|
||||||
|
//$router->addRoute('/debug', 'App\Bin\ThumbShoter', 'GET');
|
||||||
|
$router->addRoute('/backend', 'App\Controllers\Backend', 'GET');
|
||||||
|
$router->addRoute('/logout', 'App\Controllers\Logout', 'GET');
|
||||||
|
|
||||||
|
$conf = (object)Config::load([__DIR__ . '/../datas/config.default.json', '?' . __DIR__ . '/../datas/config.json'])->all();
|
||||||
|
|
||||||
|
if (!file_exists(__DIR__ . '/../datas/config.json')) {
|
||||||
|
session_start();
|
||||||
|
$conf->webPage = true;
|
||||||
|
$_SESSION['login'] = 1;
|
||||||
|
$_SESSION['firstRun'] = 1;
|
||||||
|
$_SESSION['webPage'] = 1;
|
||||||
|
if ($_SERVER['REQUEST_URI'] !== '/backend?page=settings') {
|
||||||
|
header("Location:/backend?page=settings");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($conf->onlyLocalServer === true && $_SERVER['REMOTE_ADDR'] !== '127.0.0.1') {
|
||||||
|
$error = new Error();
|
||||||
|
$error->index((object)['status' => 404, 'message' => 'The page that you have requested could not be found.']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($router->match()) {
|
||||||
|
$router->render($conf);
|
||||||
|
} else {
|
||||||
|
$error = new Error();
|
||||||
|
$error->index((object)['status' => 404, 'message' => 'The page that you have requested could not be found.']);
|
||||||
|
}
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
BIN
src/images/error_fav.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
BIN
src/images/full_generation_in_progress.jpg
Normal file
After Width: | Height: | Size: 99 KiB |
113
src/images/generation_in_progress.svg
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="1920"
|
||||||
|
height="1080"
|
||||||
|
viewBox="0 0 507.99994 285.74997"
|
||||||
|
version="1.1"
|
||||||
|
id="svg5"
|
||||||
|
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||||
|
sodipodi:docname="generation_in_progress.svg"
|
||||||
|
inkscape:export-filename="generation_in_progress.jpg"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview7"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="0.5946522"
|
||||||
|
inkscape:cx="914.82046"
|
||||||
|
inkscape:cy="412.00554"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1150"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="24"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1" />
|
||||||
|
<defs
|
||||||
|
id="defs2">
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="linearGradient1515">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop1511" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop1513" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient1515"
|
||||||
|
id="linearGradient1517"
|
||||||
|
x1="0.55058265"
|
||||||
|
y1="22.141287"
|
||||||
|
x2="41.490334"
|
||||||
|
y2="19.506355"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer3"
|
||||||
|
inkscape:label="Calque 3"
|
||||||
|
style="fill:#ff0000" />
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer2"
|
||||||
|
inkscape:label="Calque 2" />
|
||||||
|
<g
|
||||||
|
inkscape:label="Calque 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<g
|
||||||
|
id="g2134"
|
||||||
|
transform="matrix(11.965877,0,0,11.819126,0.86251738,2.3240735)">
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#ffffff;stroke:none;stroke-width:0.16;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.4"
|
||||||
|
id="rect1746"
|
||||||
|
width="42.55217"
|
||||||
|
height="24.264963"
|
||||||
|
x="-0.078654662"
|
||||||
|
y="-0.19663666" />
|
||||||
|
<rect
|
||||||
|
style="opacity:0.375168;fill:url(#linearGradient1517);fill-opacity:1;stroke:none;stroke-width:0.16;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.4"
|
||||||
|
id="rect779"
|
||||||
|
width="41.097057"
|
||||||
|
height="2.7922401"
|
||||||
|
x="0.51125532"
|
||||||
|
y="19.427702" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:7.56121px;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.18903"
|
||||||
|
x="1.2073866"
|
||||||
|
y="6.6335373"
|
||||||
|
id="text113"
|
||||||
|
transform="scale(1.0116028,0.98853033)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan111"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.56121px;font-family:C059;-inkscape-font-specification:'C059, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.18903"
|
||||||
|
x="1.2073866"
|
||||||
|
y="6.6335373">Generation</tspan><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.56121px;font-family:C059;-inkscape-font-specification:'C059, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.18903"
|
||||||
|
x="1.2073866"
|
||||||
|
y="16.447044"
|
||||||
|
id="tspan115">in progress</tspan></text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 4 KiB |
BIN
src/images/hd_generation_in_progress.jpg
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
src/images/thumb_generation_in_progress.jpg
Normal file
After Width: | Height: | Size: 5.1 KiB |
125
style.css
|
@ -1,125 +0,0 @@
|
||||||
html {
|
|
||||||
padding: 1em;
|
|
||||||
font-family: arial, sans-serif;
|
|
||||||
font-weight: bold;
|
|
||||||
line-height: 2em;
|
|
||||||
background-color: #dedede;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
color:#8AC007;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:visited {
|
|
||||||
color : #8AC007;
|
|
||||||
}
|
|
||||||
|
|
||||||
form, #result, #header, .message, .hMenu, #log {
|
|
||||||
padding: 1em;
|
|
||||||
text-align: center;
|
|
||||||
width: 60%;
|
|
||||||
margin: 1em auto;
|
|
||||||
background-color: #ffffff;
|
|
||||||
border-radius: 5px;
|
|
||||||
box-shadow: 0 0 8px rgba(0, 0, 0, 0.50), 0 85px 180px 0 #FFFFFF, 0 12px 8px -5px rgba(0, 0, 0, 0.95);
|
|
||||||
}
|
|
||||||
|
|
||||||
input, select {
|
|
||||||
height: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#result input[type=text] {
|
|
||||||
width: 80%;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=url] {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#result label {
|
|
||||||
margin-right: 1em;
|
|
||||||
width: 15%;
|
|
||||||
display: inline-block;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
form .info {
|
|
||||||
text-align: right;
|
|
||||||
margin: 0 0;
|
|
||||||
line-height: 0.8em;
|
|
||||||
font-size: 0.8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#result, #header {
|
|
||||||
text-align: center;
|
|
||||||
padding: 1em;
|
|
||||||
margin: 1em auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#result img {
|
|
||||||
box-shadow: 3px 3px 3px #cdcdcd;
|
|
||||||
border: 1px solid #cdcdcd;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message {
|
|
||||||
background-color: orange;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hMenu li {
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 1.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hMenu li:after {
|
|
||||||
content: " / ";
|
|
||||||
}
|
|
||||||
|
|
||||||
.hMenu li:last-child:after {
|
|
||||||
content: "";
|
|
||||||
}
|
|
||||||
|
|
||||||
.clear {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
#log li {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 320px) and (max-width: 800px) {
|
|
||||||
|
|
||||||
html {
|
|
||||||
padding: 0.1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
form, #result {
|
|
||||||
width: 100%;
|
|
||||||
padding: 0.1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=url] {
|
|
||||||
width: 99%;
|
|
||||||
height: 2em;
|
|
||||||
}
|
|
||||||
input[type=submit] {
|
|
||||||
width: 99%;
|
|
||||||
height: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
width: 99%;
|
|
||||||
height: 2em;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
19
tpl/bone.php
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>SoShot</title>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="description" content="Personal webshot">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="stylesheet" href="assets/css/w3.css">
|
||||||
|
<link rel="stylesheet" href="https://www.w3schools.com/lib/w3-theme-black.css">
|
||||||
|
<link rel="stylesheet" href="assets/css/fork-awesome.min.css">
|
||||||
|
<link rel="stylesheet" href="assets/css/style.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<?= $content; ?>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
6
tpl/footer.php
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
</div>
|
||||||
|
<footer id="myFooter" class="w3-bottom">
|
||||||
|
<div class="w3-container w3-theme-l1">
|
||||||
|
<p>Powered by <a href="https://www.w3schools.com/w3css/default.asp" target="_blank">w3.css</a></p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
10
tpl/home.php
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<div class="w3-row-padding centerBlock homeForm">
|
||||||
|
<h1 class="w3-center">SoShot</h1>
|
||||||
|
<p>Per<b>so</b>nal web<b>shot</b>er</p>
|
||||||
|
<div>
|
||||||
|
<img src="assets/img/wikipedia_logiciel_libre.png" alt="Thumbshot of french wikipedia home page" width="720px">
|
||||||
|
</div>
|
||||||
|
<p class="w3-center">
|
||||||
|
<a href="https://forge.leslibres.org/Knah-Tsaeb/Soshot">Homepage</a> | <a href="https://knah-tsaeb.org/contact-page/">Contact</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
143
tpl/infos.php
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Utils\ConvertStatus;
|
||||||
|
use App\Utils\Domains;
|
||||||
|
|
||||||
|
//require_once 'nav.php';
|
||||||
|
?>
|
||||||
|
<div class="w3-row soshot-main w3-margin-bottom">
|
||||||
|
<div class="w3-container">
|
||||||
|
<h1 class="w3-text-teal">Infos</h1>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Total : <?= $total; ?></li>
|
||||||
|
<li>Queue : <?= $inQueue; ?></li>
|
||||||
|
<li>Error : <?= $inError; ?></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<table class="w3-table-all w3-hoverable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td>Domain</td>
|
||||||
|
<td>Created</td>
|
||||||
|
<td>Complete</td>
|
||||||
|
<td>Full</td>
|
||||||
|
<td>HD</td>
|
||||||
|
<td>nHD</td>
|
||||||
|
<td>Thumb</td>
|
||||||
|
<td>Favicon</td>
|
||||||
|
<td>Og</td>
|
||||||
|
<td>PDF</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($genList as $value) : ?>
|
||||||
|
<tr>
|
||||||
|
<td><a href="#" class="linkDetail" data-hmac="<?= $value->id; ?>" data-href="<?= $value->url; ?>"><?= Domains::getDomain($value->url); ?></a></td>
|
||||||
|
<td><?= $value->created; ?></td>
|
||||||
|
<td><?= ConvertStatus::convertToIcon($value->complete); ?></td>
|
||||||
|
<td><?= ConvertStatus::convertToIcon($value->full); ?></td>
|
||||||
|
<td><?= ConvertStatus::convertToIcon($value->hd); ?></td>
|
||||||
|
<td><?= ConvertStatus::convertToIcon($value->nhd); ?></td>
|
||||||
|
<td><?= ConvertStatus::convertToIcon($value->thumb); ?></td>
|
||||||
|
<td><?= ConvertStatus::convertToIcon($value->fav); ?></td>
|
||||||
|
<td><?= ConvertStatus::convertToIcon($value->og); ?></td>
|
||||||
|
<td><?= ConvertStatus::convertToIcon($value->pdf); ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="w3-center w3-margin-top">
|
||||||
|
<div class="w3-bar">
|
||||||
|
<a href="?page=infos&start=<?= $previous; ?>" class="w3-teal w3-button">« Previous</a>
|
||||||
|
<a href="?page=infos&start=<?= $next; ?>" class="w3-teal w3-button">Next » </a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="detail" class="w3-modal">
|
||||||
|
<div class="w3-modal-content w3-animate-opacity">
|
||||||
|
<div class="w3-container">
|
||||||
|
<span onclick="document.getElementById('detail').style.display='none'" class="w3-button w3-display-topright">×</span>
|
||||||
|
<p>
|
||||||
|
<img src="" id="fav" height="32px">
|
||||||
|
<a href="" id="titlePreview" target="_blanck"></a>
|
||||||
|
</p>
|
||||||
|
<img class="w3-border w3c-margin" src="" id="complete" width="45%">
|
||||||
|
<img class="w3-border w3c-margin" src="" id="full" width="35%">
|
||||||
|
<img class="w3-border w3c-margin" src="" id="hd" width="30%">
|
||||||
|
<img class="w3-border w3c-margin" src="" id="og" width="25%">
|
||||||
|
<img class="w3-border w3c-margin" src="" id="nhd" width="20%">
|
||||||
|
<img class="w3-border w3c-margin" src="" id="thumb" width="15%">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- END MAIN -->
|
||||||
|
<script>
|
||||||
|
// Get the Sidebar
|
||||||
|
var mySidebar = document.getElementById("mySidebar");
|
||||||
|
|
||||||
|
// Get the DIV with overlay effect
|
||||||
|
var overlayBg = document.getElementById("myOverlay");
|
||||||
|
|
||||||
|
// Toggle between showing and hiding the sidebar, and add overlay effect
|
||||||
|
function w3_open() {
|
||||||
|
if (mySidebar.style.display === 'block') {
|
||||||
|
mySidebar.style.display = 'none';
|
||||||
|
overlayBg.style.display = "none";
|
||||||
|
} else {
|
||||||
|
mySidebar.style.display = 'block';
|
||||||
|
overlayBg.style.display = "block";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the sidebar with the close button
|
||||||
|
function w3_close() {
|
||||||
|
mySidebar.style.display = "none";
|
||||||
|
overlayBg.style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
const el = document.querySelectorAll('.linkDetail');
|
||||||
|
el.forEach(element => {
|
||||||
|
element.addEventListener("click", (el) => {
|
||||||
|
let $hmac = el.srcElement.dataset.hmac;
|
||||||
|
let $href = el.srcElement.dataset.href;
|
||||||
|
let $title = document.getElementById("titlePreview");
|
||||||
|
let $complete = document.getElementById("complete");
|
||||||
|
let $full = document.getElementById("full");
|
||||||
|
let $hd = document.getElementById("hd");
|
||||||
|
let $nhd = document.getElementById("nhd");
|
||||||
|
let $thumb = document.getElementById("thumb");
|
||||||
|
let $og = document.getElementById("og");
|
||||||
|
let $fav = document.getElementById("fav");
|
||||||
|
|
||||||
|
$title.href = $href;
|
||||||
|
$title.textContent = $href;
|
||||||
|
$complete.src = "?type=complete&showImg=" + $hmac;
|
||||||
|
$full.src = "?type=full&showImg=" + $hmac;
|
||||||
|
$hd.src = "?type=hd&showImg=" + $hmac;
|
||||||
|
$nhd.src = "?type=nhd&showImg=" + $hmac;
|
||||||
|
$thumb.src = "?type=thumb&showImg=" + $hmac;
|
||||||
|
$og.src = "?type=og&showImg=" + $hmac;
|
||||||
|
$fav.src = "?type=fav&showImg=" + $hmac;
|
||||||
|
document.getElementById('detail').style.display = 'block';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function showDetail(el) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the modal
|
||||||
|
var modal = document.getElementById('detail');
|
||||||
|
|
||||||
|
// When the user clicks anywhere outside of the modal, close it
|
||||||
|
window.onclick = function(event) {
|
||||||
|
if (event.target == modal) {
|
||||||
|
modal.style.display = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
11
tpl/login.php
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<div class="w3-center w3-container w3-half w3-display-middle">
|
||||||
|
<i class="fa fa-5x fa-lock" aria-hidden="true"></i>
|
||||||
|
|
||||||
|
<form class="w3-container" action="?" method="post">
|
||||||
|
<div class="w3-section">
|
||||||
|
<input class="w3-input w3-border" type="password" placeholder="Password" name="loginPassword" required>
|
||||||
|
<input type="hidden" name="token" value="<?= $token; ?>" />
|
||||||
|
<button class="w3-button w3-block w3-teal w3-section w3-padding" style="width:30%; margin:0 auto" type="submit">Login</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
28
tpl/nav.php
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Utils\Page;
|
||||||
|
?>
|
||||||
|
<div class="w3-top w3-round">
|
||||||
|
<div class="w3-bar w3-theme w3-top w3-left-align w3-large">
|
||||||
|
<a class="w3-bar-item w3-button w3-right w3-hide-large w3-hover-white w3-large w3-theme-l1" href="javascript:void(0)" onclick="w3_open()"><i class="fa fa-bars"></i></a>
|
||||||
|
<a href="#" class="w3-bar-item w3-button w3-theme-l1 title"><i class="fa fa-camera" aria-hidden="true"></i> SoShot</a>
|
||||||
|
<?php if ($this->runningJob) : ?><i class="fa fa-refresh fa-spin w3-text-red"></i><?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<nav class="w3-sidebar w3-bar-block w3-collapse w3-large w3-theme-l5 w3-round" id="mySidebar">
|
||||||
|
<a href="javascript:void(0)" onclick="w3_close()" class="w3-right w3-xlarge w3-padding-large w3-hover-black w3-hide-large" title="Close Menu">
|
||||||
|
<i class="fa fa-remove"></i>
|
||||||
|
</a>
|
||||||
|
<a class="w3-bar-item w3-button <?= Page::active($params->page, 'infos', 'w3-gray') ?>" href="?page=infos"> <i class="fa fa-info"></i> Infos</a>
|
||||||
|
<a class="w3-bar-item w3-button <?= Page::active($params->page, 'settings', 'w3-gray') ?>" href="?page=settings"><i class="fa fa-cog"></i> Settings</a>
|
||||||
|
<a class="w3-bar-item w3-button w3-margin-top <?= Page::active($params->page, 'logout', 'w3-gray') ?>" href="/logout"><i class="fa fa-sign-out"></i> Logout</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Overlay effect when opening sidebar on small screens -->
|
||||||
|
<div class="w3-overlay w3-hide-large" onclick="w3_close()" style="cursor:pointer" title="close side menu" id="myOverlay"></div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Main content: shift it to the right by 250 pixels when the sidebar is visible -->
|
||||||
|
<div class="w3-main w3-animate-right">
|
82
tpl/settings.php
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Utils\Page;
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="w3-row soshot-main w3-margin-bottom">
|
||||||
|
<div class="w3-container">
|
||||||
|
<h1 class="w3-text-teal"><?= $this->title; ?></h1>
|
||||||
|
<form class="w3-container" method="post">
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<label>Accept only request from 127.0.0.1</label>
|
||||||
|
<input class="w3-check" type="checkbox" name="onlyLocalServer" value="1" <?= Page::checked($this->conf->onlyLocalServer, true); ?>>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label>Use web GUI</label>
|
||||||
|
<input class="w3-check" type="checkbox" name="webPage" value="1" <?= Page::checked($this->conf->webPage, true); ?>>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label>Use log</label>
|
||||||
|
<input class="w3-check" type="checkbox" name="log" value="1" <?= Page::checked($this->conf->log, true); ?>>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label>Always make PDF</label>
|
||||||
|
<input class="w3-check" type="checkbox" name="alwaysMakePdf" value="1" <?= Page::checked($this->conf->alwaysMakePdf, true); ?>>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label>File format (png, jpeg)</label>
|
||||||
|
<select class="w3-select w3-border" name="fileFormat" size="2">
|
||||||
|
<option value="png" <?= Page::selected('png', $this->conf->fileFormat); ?>>png</option>
|
||||||
|
<option value="jpeg" <?= Page::selected('jpeg', $this->conf->fileFormat); ?>>jpeg</option>
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label>Favicon size</label>
|
||||||
|
<input class="w3-input w3-border" type="number" name="icoSize" value="<?= $this->conf->icoSize; ?>">
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label>Permit type of webshot</label>
|
||||||
|
<select class="w3-select w3-border" name="permitType[]" multiple size="7">
|
||||||
|
<option value="fav" <?= Page::selected('fav', $this->conf->permitType); ?>>Favicon (<?= $this->conf->icoSize; ?>x<?= $this->conf->icoSize; ?>)</option>
|
||||||
|
<option value="og" <?= Page::selected('og', $this->conf->permitType); ?>>Og image</option>
|
||||||
|
<option value="pdf" <?= Page::selected('pdf', $this->conf->permitType); ?>>PDF</option>
|
||||||
|
<option value="thumb" <?= Page::selected('thumb', $this->conf->permitType); ?>>ThumbShot (160x90)</option>
|
||||||
|
<option value="nhd" <?= Page::selected('nhd', $this->conf->permitType); ?>>nHD (640x360)</option>
|
||||||
|
<option value="hd" <?= Page::selected('hd', $this->conf->permitType); ?>>HD (1080x720)</option>
|
||||||
|
<option value="full" <?= Page::selected('full', $this->conf->permitType); ?>>Full HD (1920x1080)</option>
|
||||||
|
<option value="complete" <?= Page::selected('complete', $this->conf->permitType); ?>>Complete (1920x height of page)</option>
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label>Cache expiration (for web GUI) in hour</label>
|
||||||
|
<input class="w3-input w3-border" type="number" name="expireCache" value="<?= $this->conf->expireCache; ?>">
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label>Maximum work per batch</label>
|
||||||
|
<input class="w3-input w3-border" type="number" name="maxGenPerBatch" value="<?= $this->conf->maxGenPerBatch; ?>">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<label>Api key (<span class="w3-red">change key will be invalid all previous generation</span>)</label>
|
||||||
|
<input class="w3-input w3-border" type="text" name="key" value="<?= $this->conf->key; ?>">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<label>Password</label>
|
||||||
|
<input class="w3-input w3-border" type="password" name="password" value="" <?= $this->passwordRequired; ?>>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<label>Chrome path (if empty, SoShot use default value "chrome or chromium")</label>
|
||||||
|
<input class="w3-input w3-border" type="text" name="chromePath" value="<?= $this->conf->chromePath; ?>">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<input type="hidden" name="page" value="settings">
|
||||||
|
<input type="hidden" name="token" value="<?= $token; ?>">
|
||||||
|
<button type="submit" class="w3-btn w3-padding w3-teal" style="width:120px">Save</button>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|