general cleanup
All checks were successful
Build and publish the docker image / build (push) Successful in 18s

Signed-off-by: ngn <ngn@ngn.tf>
This commit is contained in:
ngn 2025-01-19 06:42:44 +03:00
parent 033e4cb959
commit ce81a54de1
Signed by: ngn
GPG Key ID: A3654DF5AD9F641D
147 changed files with 90 additions and 2222 deletions

View File

@ -0,0 +1,30 @@
name: Build and publish the docker image
on:
push:
branches: ["custom"]
env:
REGISTRY: git.ngn.tf
IMAGE: ${{gitea.repository}}
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: "https://github.com/actions/checkout@v4"
- name: Login to container repo
uses: "https://github.com/docker/login-action@v1"
with:
registry: ${{env.REGISTRY}}
username: ${{gitea.actor}}
password: ${{secrets.PACKAGES_TOKEN}}
- name: Build image
run: |
hash=${{gitea.sha}}
docker build . --tag ${{env.REGISTRY}}/${{env.IMAGE}}:latest --tag ${{env.REGISTRY}}/${{env.IMAGE}}:${hash::7}
docker push ${{env.REGISTRY}}/${{env.IMAGE}}:${hash::7}
docker push ${{env.REGISTRY}}/${{env.IMAGE}}:latest

View File

@ -1,17 +1,24 @@
FROM alpine:latest
RUN apk update
RUN apk upgrade
RUN apk add php apache2-ssl php83-fileinfo php83-openssl \
php83-iconv php83-common php83-dom php83-sodium \
php83-curl curl php83-pecl-apcu php83-apache2 \
imagemagick php83-pecl-imagick php-mbstring \
imagemagick-webp imagemagick-jpeg
COPY ./docker/httpd.conf /etc/apache2/httpd.conf
COPY ./docker/init.sh /
WORKDIR /var/www/html
COPY ./src ./4get
WORKDIR /var/www/html/4get
RUN apk update && apk upgrade
RUN apk add php apache2-ssl php83-fileinfo php83-openssl php83-iconv php83-common php83-dom php83-sodium php83-curl curl php83-pecl-apcu php83-apache2 imagemagick php83-pecl-imagick php-mbstring imagemagick-webp imagemagick-jpeg
COPY ./docker/apache/ /etc/apache2/
COPY . .
COPY ./docker/gen_config.php .
RUN chmod 777 /var/www/html/4get/icons
RUN chmod +x /init.sh
EXPOSE 80
EXPOSE 443
ENV FOURGET_PROTO=http
CMD ["./docker/docker-entrypoint.sh"]
CMD ["/init.sh"]

View File

@ -1,60 +1,5 @@
## <a href="https://4get.ca/donate">Donate to the project here!</a>
# [ngn.tf] | 4get
# 4get search
**4get** is a proxy search engine that doesn't suck.
![](https://git.ngn.tf/ngn/4get/actions/workflows/build.yml/badge.svg)
## About 4get
https://4get.ca/about
## Official instance
https://4get.ca , or visit the official instance list: https://4get.ca/instances
_NOT to be confused with 4get.ch, 4get.lol and friends! I **don't** host these._
## Totally unbiased comparison between alternatives
| | 4get | searx(ng) | libreY | araa | hearch.co |
|----------------------------|-------------------------|-----------|-------------|-----------|-------------------|
| RAM usage | 200-400mb~ | 2GB~ | 200-400mb~ | 2GB~ | idk |
| Does it suck | no (debunked by snopes) | yes | yes | a little | better than searx |
| Does it work | ye | sometimes | sometimes | sometimes | yes |
## Features
1. Rotating proxies on a per-scraper basis
2. Search filters, which SearxNG lacks for the most part
3. Bot protection that *actually* filters out the bots (when configured)
4. Interface doesn't require javascript
5. Favicon fetcher with caching support & image proxy
6. Bunch of other shits
tl;dr 4get is the best way to browse for shit.
# Supported websites
| Web | Images | Videos | News | Music | Autocompleter |
|------------|--------------|------------|------------|------------|---------------|
| DuckDuckGo | DuckDuckGo | YouTube | DuckDuckGo | Soundcloud | Brave |
| Brave | Brave | DuckDuckGo | Brave | | DuckDuckGo |
| Yandex | Yandex | Brave | Google | | Yandex |
| Google | Google | Yandex | Startpage | | Google |
| Startpage | Startpage | Google | Qwant | | Startpage |
| Qwant | Qwant | Startpage | Mojeek | | Kagi |
| Ghostery | Yep | Qwant | | | Qwant |
| Yep | Solofield | Solofield | | | Ghostery |
| Greppr | Pinterest | | | | Yep |
| Crowdview | 500px | | | | Marginalia |
| Mwmbl | VSCO | | | | YouTube |
| Mojeek | Imgur | | | | Soundcloud |
| Solofield | FindThatMeme | | | | |
| Marginalia | | | | | |
| wiby | | | | | |
| Curlie | | | | | |
# Installation
Refer to the <a href="https://git.lolcat.ca/lolcat/4get/src/branch/master/docs/">documentation index</a>. I recommend following the <a href="https://git.lolcat.ca/lolcat/4get/src/branch/master/docs/apache2.md">apache2 guide</a>.
## Contact
Shit breaks all the time but I repair it all the time too. Email me here: <b>will (at) lolcat.ca</b> or create an issue.
## License
AGPL
A fork of the [4get](https://git.lolcat.ca/lolcat/4get) project, with my personal changes.

357
api.txt
View File

@ -1,357 +0,0 @@
44
4444444 44
44444444 44444 444
44444444 444444 444444444
44444 44444444 444444444
444444444 4444444
4444444444 444444
4444444444444
444444444444444444
444444444444444
44444444
4444
44
+ Welcome to the 4get API documentation +
+ Terms of use
Do NOT misuse the API. Misuses can include... ::
1. Serp SEO scanning
2. Intensive scraping
3. Any other activity that isn't triggered by a human
4. Illegal activities in Canada
5. Constant "test" queries while developping your program
(please cache the API responses!)
Examples of good uses of the API ::
1. A chatroom bot that presents users with search results
2. Personal use
3. Any other activity that is initiated by a human
If you wish to engage in the activities listed under "misuses", feel
free to download the source code of the project and running 4get
under your own terms. Please respect the terms of use listed here so
that this website may be available to all in the far future.
P.s fuck whoever botted my site for months on end, choke on my dick
lol!!!!
Get your instance running here ::
https://git.lolcat.ca/lolcat/4get
Thanks!
+ Passes
Depending of the instance, you may need to provide a "pass" token
in the cookies of your request. These can be obtained from solving
a captcha which will allow you to make 100 requests in the next 24
hours. In the future, you will be able to ask the serber maintainer
for a "pass" which will allow you to bypass the captcha requirement.
The captcha doesn't need javascript to work.
+ Decode the data
All payloads returned by the API are encoded in the JSON format. If
you don't know how to tackle the problem, maybe programming is not
for you.
All of the endpoints use the GET method.
+ Check if an API call was successful
All API responses come with an array index named "status". If the
status is something else than the string "ok", something went wrong.
You can supply the content of the "status" string back to your
application to inform the user of what went wrong.
The HTTP code will be 429 if your pass is invalid. It is set to 200
otherwise.
+ Get the next page of results
All API responses come with an array index named "npt". To get the
next page of results, you must make another API call with &npt.
Example ::
+ First API call
/api/v1/web?s=higurashi
+ Second API call
/api/v1/web?npt=ddg1._rJ2hWmYSjpI2hsXWmYajJx < ... >
You shouldn't specify the search term, only the &npt parameter
suffices.
The first part of the token before the dot (ddg1) refers to an
array position on the serber's memory. The second part is an
encryption key used to decode the data at that position. This way,
it is impossible to supply invalid pagination data and it is
impossible for a 4get operator to peek at the private data of the
user after a request has been made.
The tokens will expire as soon as they are used or after a 15
minutes inactivity period, whichever comes first.
+ Beware of null values!
Most fields in the API responses can return "null". You don't need
to worry about unset values.
+ API Parameters
To construct a valid request, you can use the 4get web interface
to craft a valid request, and replace "/web" with "/api/v1/web".
+ "date" and "time" parameters
"date" always refer to a calendar date.
"time" always refer to the duration of some media.
They are both integers that uses seconds as its unit. The "date"
parameter specifies the number of seconds that passed since January
1st 1970.
______ __ _ __
/ ____/___ ____/ /___ ____ (_)___ / /______
/ __/ / __ \/ __ / __ \/ __ \/ / __ \/ __/ ___/
/ /___/ / / / /_/ / /_/ / /_/ / / / / / /_(__ )
/_____/_/ /_/\__,_/ .___/\____/_/_/ /_/\__/____/
/_/
+ /ami4get
Tells you basic information about the 4get instance. CORS requests
are allowed on this endpoint.
+ /api/v1/web
+ &extendedsearch
When using the ddg(DuckDuckGo) scraper, you may make use of the
&extendedsearch parameter. If you need rich answer data from
additional sources like StackOverflow, music lyrics sites, etc.,
you need to specify the value of (string)"true".
The default value is "false" for API calls.
+ Parse the "spelling"
The array index named "spelling" contains 3 indexes ::
spelling:
type: "including"
using: "4chan"
correction: '"4cha"'
The "type" may be any of these 3 values. When rendering the
autocorrect text inside your application, it should look like
what follows right after the parameter value ::
no_correction <Empty>
including Including results for %using%. Did you mean
%correction%?
not_many Not many results for %using%. Did you mean
%correction%?
As of right now, the "spelling" is only available on
"/api/v1/web".
+ Parse the "answer"
The array index named "answer" may contain a list of multiple
answers. The array index "description" contains a linear list of
nodes that can help you construct rich formatted data inside of
your application. The structure is similar to the one below:
answer:
0:
title: "Higurashi"
description:
0:
type: "text"
value: "Higurashi is a great show!"
1:
type: "quote"
value: "Source: my ass"
Each "description" node contains an array index named "type".
Here is a list of them:
text
+ title
italic
+ quote
+ code
inline_code
link
+ image
+ audio
Each individual node prepended with a "+" should be prepended by
a newline when constructing the rendered description object.
There are some nodes that differ from the type-value format.
Please parse them accordingly ::
+ link
type: "link"
url: "https://lolcat.ca"
value: "Visit my website!"
+ image
type: "image"
url: "https://lolcat.ca/static/pixels.png"
+ audio
type: "audio"
url: "https://lolcat.ca/static/whatever.mp3"
The array index named "table" is an associative array. You can
loop over the data using this PHP code, for example ::
foreach($table as $website_name => $url){ // ...
The rest of the JSON is pretty self explanatory.
+ /api/v1/images
All images are contained within "image". The structure looks like
below ::
image:
0:
title: "My awesome Higurashi image"
source:
0:
url: "https://lolcat.ca/static/profile_pix.png"
width: 400
height: 400
1:
url: "https://lolcat.ca/static/pixels.png"
width: 640
height: 640
2:
url: "https://tse1.mm.bing.net/th?id=OIP.VBM3BQg
euf0-xScO1bl1UgHaGG"
width: 194
height: 160
The last image of the "source" array is always the thumbnail, and is
a good fallback to use when other sources fail to load. There can be
more than 1 source; this is especially true when using the Yandex
scraper, but beware of captcha rate limits.
+ /api/v1/videos
The "time" parameter for videos may be set to "_LIVE". For live
streams, the amount of people currently watching is passed in
"views".
+ /api/v1/news
Just make a request to "/api/v1/news?s=elon+musk". The payload
has nothing special about it and is very self explanatory, just like
the endpoint above.
+ /api/v1/music
Each entry under "song" contains a array index called "stream" that
looks like this ::
endpoint: sc
url: https://api-v2.soundcloud <...>
When the endpoint is something else than "linear", you MUST use
the specified endpoint. Otherwise, you are free to handle that
json+m3u8 crap yourself. If the endpoint is equal to "linear", the
URL should return a valid HTTP audio stream. To access the endpoint,
you must add the following prefix in your request, like so:
https://4get.ca/audio/<endpoint>?s=<url>
+ /favicon
Get the favicon for a website. The only parameter is "s", and must
include the protocol for fetching in case the favicon is not cached
yet.
Example ::
/favicon?s=https://lolcat.ca
If we had to revert to using Google's favicon cache, it will throw
an error in the X-Error header field. If Google's favicon cache
also failed to return an image, or if you're too retarded to specify
a valid domain name, a default placeholder image will be returned
alongside the "404" HTTP error code.
+ /proxy
Get a proxied image. Useful if you don't want to leak your user's IP
address. The parameters are "i" for the image link and "s" for the
size.
Acceptable "s" parameters:
portrait 90x160
landscape 160x90
square 90x90
thumb 236x180
cover 207x270
original <Original resolution>
You can also ommit the "s" parameter if you wish to view the
original image. When an error occurs, an "X-Error" header field
is set.
+ /audio/linear
Get a proxied audio file. Does not support "Range" headers, as it's
only used to proxy small files (hence why it's called linear DUH)
The parameter is "s" for the audio link.
+ /audio/sc
Get a proxied audio file for SoundCloud. Does not support downloads
trough WGET or CURL, since it returns 30kb~160kb "206 Partial
Content" parts, due to technical limitations that comes with
converting m3u8 playlists to seekable audio files. If you use this
endpoint, you must support these 206 codes and also handle the
initial 302 HTTP redirect. I used this method as I didn't want to
store information about your request needlessly. This method also
allows noJS users to access the files.
The parameter is "s" for the SoundCloud JSON m3u8 abomination. It
does not support "normal" SoundCloud URLs at this time.
+ /audio/spotify
Get a proxied Spotify audio file. Accepts a track ID for the "s"
parameter. Will only allow you to fetch the 30 second preview since
I don't feel like fucking with cookies and accounts every fucking
living moment of my life. You must handle the initial 302 redirect
to the /audio/linear endpoint.
+ Appendix
If you have any questions or need clarifications, please send an
email my way to will at lolcat.ca

View File

@ -1,39 +0,0 @@
<?php
chdir("../../");
header("Content-Type: application/json");
include "data/config.php";
if(config::API_ENABLED === false){
echo json_encode(["status" => "The server administrator disabled the API!"]);
return;
}
include "lib/frontend.php";
$frontend = new frontend();
/*
Captcha
*/
include "lib/bot_protection.php";
$null = null;
new bot_protection($null, $null, $null, "images", false);
[$scraper, $filters] = $frontend->getscraperfilters(
"images",
isset($_GET["scraper"]) ? $_GET["scraper"] : null
);
$get = $frontend->parsegetfilters($_GET, $filters);
try{
echo json_encode(
$scraper->image($get),
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_IGNORE
);
}catch(Exception $e){
echo json_encode(["status" => $e->getMessage()]);
}

View File

@ -1,39 +0,0 @@
<?php
chdir("../../");
header("Content-Type: application/json");
include "data/config.php";
if(config::API_ENABLED === false){
echo json_encode(["status" => "The server administrator disabled the API!"]);
return;
}
include "lib/frontend.php";
$frontend = new frontend();
/*
Captcha
*/
include "lib/bot_protection.php";
$null = null;
new bot_protection($null, $null, $null, "music", false);
[$scraper, $filters] = $frontend->getscraperfilters(
"music",
isset($_GET["scraper"]) ? $_GET["scraper"] : null
);
$get = $frontend->parsegetfilters($_GET, $filters);
try{
echo json_encode(
$scraper->music($get),
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_IGNORE
);
}catch(Exception $e){
echo json_encode(["status" => $e->getMessage()]);
}

View File

@ -1,39 +0,0 @@
<?php
chdir("../../");
header("Content-Type: application/json");
include "data/config.php";
if(config::API_ENABLED === false){
echo json_encode(["status" => "The server administrator disabled the API!"]);
return;
}
include "lib/frontend.php";
$frontend = new frontend();
/*
Captcha
*/
include "lib/bot_protection.php";
$null = null;
new bot_protection($null, $null, $null, "news", false);
[$scraper, $filters] = $frontend->getscraperfilters(
"news",
isset($_GET["scraper"]) ? $_GET["scraper"] : null
);
$get = $frontend->parsegetfilters($_GET, $filters);
try{
echo json_encode(
$scraper->news($get),
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_IGNORE
);
}catch(Exception $e){
echo json_encode(["status" => $e->getMessage()]);
}

View File

@ -1,39 +0,0 @@
<?php
chdir("../../");
header("Content-Type: application/json");
include "data/config.php";
if(config::API_ENABLED === false){
echo json_encode(["status" => "The server administrator disabled the API!"]);
return;
}
include "lib/frontend.php";
$frontend = new frontend();
/*
Captcha
*/
include "lib/bot_protection.php";
$null = null;
new bot_protection($null, $null, $null, "videos", false);
[$scraper, $filters] = $frontend->getscraperfilters(
"videos",
isset($_GET["scraper"]) ? $_GET["scraper"] : null
);
$get = $frontend->parsegetfilters($_GET, $filters);
try{
echo json_encode(
$scraper->video($get),
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_IGNORE
);
}catch(Exception $e){
echo json_encode(["status" => $e->getMessage()]);
}

View File

@ -1,52 +0,0 @@
<?php
chdir("../../");
header("Content-Type: application/json");
include "data/config.php";
if(config::API_ENABLED === false){
echo json_encode(["status" => "The server administrator disabled the API!"]);
return;
}
include "lib/frontend.php";
$frontend = new frontend();
/*
Captcha
*/
include "lib/bot_protection.php";
$null = null;
new bot_protection($null, $null, $null, "web", false);
[$scraper, $filters] = $frontend->getscraperfilters(
"web",
isset($_GET["scraper"]) ? $_GET["scraper"] : null
);
$get = $frontend->parsegetfilters($_GET, $filters);
if(
isset($_GET["extendedsearch"]) &&
$_GET["extendedsearch"] == "yes"
){
$get["extendedsearch"] = "yes";
}else{
$get["extendedsearch"] = "no";
}
try{
echo
json_encode(
$scraper->web($get),
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_IGNORE
);
}catch(Exception $e){
echo json_encode(["status" => $e->getMessage()]);
}

View File

@ -1,203 +0,0 @@
<?php
if(
isset($_GET["v"]) === false ||
is_array($_GET["v"]) === true ||
preg_match(
'/^c[0-9]+\.[A-Za-z0-9_]{20}$/',
$_GET["v"]
) === 0
){
http_response_code(401);
header("Content-Type: text/plain");
echo "Fuck my feathered cloaca";
die();
}
//header("Content-Type: image/jpeg");
include "data/config.php";
if(config::BOT_PROTECTION !== 1){
header("Content-Type: text/plain");
echo "The IQ test is disabled";
die();
}
$grid = apcu_fetch($_GET["v"]);
if($grid !== false){
// captcha already generated
http_response_code(304); // not modified
die();
}
header("Content-Type: image/jpeg");
header("Last-Modified: Thu, 01 Oct 1970 00:00:00 GMT");
// ** generate captcha data
// get the positions for the answers
// will return between 3 and 6 answer positions
$range = range(0, 15);
$answer_pos = [];
array_splice($range, 0, 1);
$picks = random_int(3, 6);
for($i=0; $i<$picks; $i++){
$answer_pos_tmp =
array_splice(
$range,
random_int(
0,
14 - $i
),
1
);
$answer_pos[] = $answer_pos_tmp[0];
}
// choose a dataset
$c = count(config::CAPTCHA_DATASET);
$choosen = config::CAPTCHA_DATASET[random_int(0, $c - 1)];
$choices = [];
for($i=0; $i<$c; $i++){
if(config::CAPTCHA_DATASET[$i][0] == $choosen[0]){
continue;
}
$choices[] = config::CAPTCHA_DATASET[$i];
}
// generate grid data
$grid = [];
for($i=0; $i<16; $i++){
if(in_array($i, $answer_pos)){
$grid[] = $choosen;
}else{
$grid[] = $choices[random_int(0, count($choices) - 1)];
}
}
// store grid data for form validation on captcha_gen.php
apcu_store(
$_GET["v"],
$answer_pos,
60 // we give user 1 minute to solve
);
// generate image
if(random_int(0,1) === 0){
$theme = [
"bg" => "#ebdbb2",
"fg" => "#1d2021"
];
}else{
$theme = [
"bg" => "#1d2021",
"fg" => "#ebdbb2"
];
}
$im = new Imagick();
$im->newImage(400, 427, $theme["bg"]);
$im->setImageBackgroundColor($theme["bg"]);
$im->setImageFormat("jpg");
$noise = [
imagick::NOISE_GAUSSIAN,
imagick::NOISE_LAPLACIAN
];
$distort = [
imagick::DISTORTION_AFFINE,
imagick::DISTORTION_SHEPARDS
];
$i = 0;
for($y=0; $y<4; $y++){
for($x=0; $x<4; $x++){
$tmp = new Imagick("./data/captcha/" . $grid[$i][0] . "/" . random_int(1, $grid[$i][1]) . ".png");
// convert transparency correctly
$tmp->setImageBackgroundColor("black");
$tmp->setImageAlphaChannel(Imagick::ALPHACHANNEL_REMOVE);
// randomly mirror
if(random_int(0,1) === 1){
$tmp->flopImage();
}
// distort $tmp
$tmp->distortImage(
$distort[random_int(0,1)],
[
0, 0,
random_int(-15, 15), random_int(-15, 15),
100, 0,
random_int(80, 120), random_int(-15, 15),
100, 100,
random_int(80, 120), random_int(80, 120),
0, 100,
random_int(-15, 15), random_int(80, 120)
],
false
);
$tmp->addNoiseImage($noise[random_int(0, 1)]);
// append image
$im->compositeImage($tmp->getImage(), Imagick::COMPOSITE_DEFAULT, $x * 100, ($y * 100) + 27);
$i++;
}
}
// add text
$draw = new ImagickDraw();
$draw->setFontSize(20);
$draw->setFillColor($theme["fg"]);
//$draw->setTextAntialias(false);
$draw->setFont("./data/fonts/captcha.ttf");
$text = "Pick " . $picks . " images of " . str_replace("_", " ", $choosen[0]);
$pos = 200 - ($im->queryFontMetrics($draw, $text)["textWidth"] / 2);
for($i=0; $i<strlen($text); $i++){
$im->annotateImage(
$draw,
$pos,
20,
random_int(-15, 15),
$text[$i]
);
$pos += $im->queryFontMetrics($draw, $text[$i])["textWidth"];
}
$im->setFormat("jpeg");
$im->setImageCompressionQuality(90);
echo $im->getImageBlob();

View File

@ -0,0 +1,12 @@
services:
fourget:
container_name: 4get
image: git.ngn.tf/ngn/4get
environment:
- FOURGET_SERVER_NAME=example.com
ports:
- 80:80
volumes:
- ./banners:/var/www/html/4get/banner
- ./captcha:/var/www/html/4get/data/captcha
restart: unless-stopped

View File

@ -1,19 +0,0 @@
# example docker-compose.yaml
version: "3.7"
services:
fourget:
image: luuul/4get:latest
restart: unless-stopped
environment:
- FOURGET_SERVER_NAME=4get.ca
ports:
- "80:80"
- "443:443"
volumes:
- /etc/letsencrypt/live/domain.tld:/etc/4get/certs
# mount custom banners and captcha
- ./banners:/var/www/html/4get/banner
- ./captcha:/var/www/html/4get/data/captcha

View File

@ -1,19 +0,0 @@
LoadModule ssl_module modules/mod_ssl.so
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
SSLRandomSeed startup file:/dev/urandom 512
SSLRandomSeed connect builtin
Listen 443
SSLCipherSuite HIGH:MEDIUM:!MD5:!RC4:!3DES:!ADH
SSLProxyCipherSuite HIGH:MEDIUM:!MD5:!RC4:!3DES:!ADH
SSLHonorCipherOrder on
SSLProtocol all -SSLv3
SSLProxyProtocol all -SSLv3
SSLPassPhraseDialog builtin
SSLSessionCache "shmcb:/var/cache/mod_ssl/scache(512000)"
SSLSessionCacheTimeout 300

View File

@ -1,102 +0,0 @@
ServerTokens OS
ServerRoot /var/www
ServerSignature On
ServerName localhost
DocumentRoot "/var/www/html/4get"
LogLevel warn
CustomLog /dev/null common
ErrorLog /dev/null
<VirtualHost *:443>
SSLEngine on
SSLCertificateFile /etc/4get/certs/fullchain.pem
SSLCertificateKeyFile /etc/4get/certs/privkey.pem
</VirtualHost>
<Directory "/var/www/html/4get">
RewriteEngine On
RewriteCond %{THE_REQUEST} ^\w+\ /(.*)\.php(\?.*)?\ HTTP/
RewriteRule ^ http://%{HTTP_HOST}/%1 [R=301]
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule .* $0.php
Options FollowSymLinks
AllowOverride None
Require all granted
</Directory>
# deny access to private resources
<Directory "/var/www/html/4get/data">
Require all denied
<Files "*">
Require all denied
</Files>
</Directory>
LoadModule rewrite_module modules/mod_rewrite.so
LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
LoadModule authn_file_module modules/mod_authn_file.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authz_host_module modules/mod_authz_host.so
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
LoadModule authz_user_module modules/mod_authz_user.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule access_compat_module modules/mod_access_compat.so
LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule reqtimeout_module modules/mod_reqtimeout.so
LoadModule filter_module modules/mod_filter.so
LoadModule mime_module modules/mod_mime.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule env_module modules/mod_env.so
LoadModule headers_module modules/mod_headers.so
LoadModule setenvif_module modules/mod_setenvif.so
LoadModule version_module modules/mod_version.so
LoadModule unixd_module modules/mod_unixd.so
LoadModule status_module modules/mod_status.so
LoadModule autoindex_module modules/mod_autoindex.so
LoadModule dir_module modules/mod_dir.so
LoadModule alias_module modules/mod_alias.so
LoadModule negotiation_module modules/mod_negotiation.so
<IfModule unixd_module>
User apache
Group apache
</IfModule>
<Directory />
AllowOverride none
Require all denied
</Directory>
<IfModule dir_module>
DirectoryIndex index.html
</IfModule>
<Files ".ht*">
Require all denied
</Files>
<IfModule headers_module>
RequestHeader unset Proxy early
</IfModule>
<IfModule mime_module>
TypesConfig /etc/apache2/mime.types
AddType application/x-compress .Z
AddType application/x-gzip .gz .tgz
</IfModule>
<IfModule mime_magic_module>
MIMEMagicFile /etc/apache2/magic
</IfModule>
IncludeOptional /etc/apache2/conf.d/*.conf

View File

@ -1,25 +0,0 @@
#!/bin/sh
set -e
# remove quotes from variable if present
FOURGET_PROTO="${FOURGET_PROTO%\"}"
FOURGET_PROTO="${FOURGET_PROTO#\"}"
# make lowercase
FOURGET_PROTO=`echo $FOURGET_PROTO | awk '{print tolower($0)}'`
if [ "$FOURGET_PROTO" = "https" ]; then
echo "Using https configuration"
cp /etc/apache2/https.conf /etc/apache2/httpd.conf
else
echo "Using http configuration"
cp /etc/apache2/http.conf /etc/apache2/httpd.conf
fi
php ./docker/gen_config.php
echo "4get is running"
exec httpd -DFOREGROUND

View File

@ -62,16 +62,16 @@ $output = $output . "class config {\n";
foreach(($merged_config) as $key => $val){
if(!in_array($key, $special_keys)) {
$stored_value = $val;
// conversion between arrays and comma separated env value.
// Handle case when original type of field is array and there is a type mismatch when a comma separted string is passed,
// conversion between arrays and comma separated env value.
// Handle case when original type of field is array and there is a type mismatch when a comma separted string is passed,
// then split on comma if string (and not numeric, boolean, null, etc)
//
//
// except in the case where the inital value in default config is null or boolean. Assuming null and boolean
// in default config will be never be assigned an array
if(gettype($from_config[$key]) != gettype($val) && !is_numeric($val) && !is_null($from_config[$key]) && gettype($from_config[$key]) != "boolean") {
$stored_value = explode(",", $val);
}
}
$output = $output . "\tconst " . $key . " = " . type_to_string($stored_value) . ";\n";
continue;

8
docker/init.sh Normal file
View File

@ -0,0 +1,8 @@
#!/bin/sh
set -e
php ./gen_config.php
rm ./gen_config.php
echo "Starting up apache2"
exec httpd -DFOREGROUND

View File

@ -1,18 +0,0 @@
FROM alpine:edge
RUN apk add --no-cache curl tor
EXPOSE 9050
HEALTHCHECK --interval=60s --timeout=15s --start-period=20s \
CMD curl -x socks5h://127.0.0.1:9050 'https://check.torproject.org/api/ip' | grep -qm1 -E '"IsTor"\s*:\s*true'
# default owner is tor, but running as root to avoid docker volume mount issue
RUN chown -R root:root /var/lib/tor
VOLUME ["/var/lib/tor/4get"]
COPY ./torrc /etc/tor/torrc
ENTRYPOINT ["/usr/bin/tor"]

View File

@ -1 +0,0 @@
SocksPort 0.0.0.0:9050

View File

@ -1,195 +0,0 @@
# Sample Apache2 configuration
This is the apache2 configuration file used on the 4get.ca official instance, in hopes that it's useful to you!
Looking for the apache2 guide? <a href="https://git.lolcat.ca/lolcat/4get/src/branch/master/docs/apache2.md">go here.</a>.
```xml
<VirtualHost *:443>
ServerName www.4get.ca
SSLEngine On
SSLCertificateFile /etc/letsencrypt/live/4get.ca/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/4get.ca/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/4get.ca/chain.pem
RedirectMatch 301 ^(.*)$ https://4get.ca$1
</VirtualHost>
<VirtualHost *:443>
ServerName 4get.ca
ServerAdmin will@lolcat.ca
DocumentRoot /var/www/4get
SSLEngine On
SSLOptions +StdEnvVars
#ErrorLog ${APACHE_LOG_DIR}/error.log
AddOutputFilterByType DEFLATE application/json
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/css
SSLCertificateFile /etc/letsencrypt/live/4get.ca/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/4get.ca/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/4get.ca/chain.pem
<Directory /var/www/4get>
Options -MultiViews
AllowOverride All
Require all granted
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^\.]+)$ $1.php [NC,L]
</Directory>
# deny access to private resources
<Directory /var/www/4get/data/>
Order Deny,allow
Deny from all
</Directory>
</VirtualHost>
<VirtualHost *:443>
ServerName www.lolcat.ca
SSLEngine On
SSLCertificateFile /etc/letsencrypt/live/4get.ca/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/4get.ca/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/4get.ca/chain.pem
RedirectMatch 301 ^(.*)$ https://lolcat.ca$1
</VirtualHost>
<VirtualHost *:443>
ServerName lolcat.ca
ServerAdmin will@lolcat.ca
DocumentRoot /var/www/lolcat
SSLEngine On
SSLOptions +StdEnvVars
#ErrorLog ${APACHE_LOG_DIR}/error.log
AddOutputFilterByType DEFLATE application/json
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/css
SSLCertificateFile /etc/letsencrypt/live/4get.ca/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/4get.ca/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/4get.ca/chain.pem
<Directory /var/www/lolcat>
Options -MultiViews
AllowOverride All
Require all granted
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^\.]+)$ $1.php [NC,L]
</Directory>
</VirtualHost>
<VirtualHost *:443>
ServerName www.nyym.co
SSLEngine On
SSLCertificateFile /etc/letsencrypt/live/nyym.co/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/nyym.co/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/nyym.co/chain.pem
RedirectMatch 301 ^(.*)$ https://nyym.co$1
</VirtualHost>
<VirtualHost *:443>
ServerName nyym.co
ServerAdmin will@lolcat.ca
DocumentRoot /var/www/nyym
SSLEngine On
SSLOptions +StdEnvVars
#ErrorLog ${APACHE_LOG_DIR}/error.log
AddOutputFilterByType DEFLATE application/json
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/css
SSLCertificateFile /etc/letsencrypt/live/nyym.co/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/nyym.co/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/nyym.co/chain.pem
<Directory /var/www/nyym>
Options -MultiViews
AllowOverride All
Require all granted
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^\.]+)$ $1.php [NC,L]
</Directory>
</VirtualHost>
<VirtualHost *:443>
ServerName git.lolcat.ca
SSLEngine On
SSLOptions +StdEnvVars
#ErrorLog ${APACHE_LOG_DIR}/error.log
AddOutputFilterByType DEFLATE application/json
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/css
SSLCertificateFile /etc/letsencrypt/live/4get.ca/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/4get.ca/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/4get.ca/chain.pem
ProxyPreserveHost On
ProxyRequests off
AllowEncodedSlashes NoDecode
ProxyPass / http://localhost:3000/ nocanon
</VirtualHost>
<VirtualHost *:443>
ServerName live.lolcat.ca
ServerAdmin will@lolcat.ca
DocumentRoot /var/www/live
SSLEngine On
SSLOptions +StdEnvVars
#ErrorLog ${APACHE_LOG_DIR}/error.log
AddOutputFilterByType DEFLATE application/json
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/css
SSLCertificateFile /etc/letsencrypt/live/4get.ca/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/4get.ca/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/4get.ca/chain.pem
</VirtualHost>
```

View File

@ -1,216 +0,0 @@
# Install guide for Apache2 webserver
Welcome to the new and revamped 4get install manual for apache2. Even if you already have services running on an existing installation of apache2, you should still be able to adapt this guide to your needs.
For starters, login as `root`.
Then, install the following dependencies:
```sh
apt update
apt upgrade
apt install php-mbstring apache2 certbot php-imagick imagemagick php-curl curl php-apcu git libapache2-mod-php
```
Enable the required modules:
```sh
a2enmod ssl
a2enmod rewrite
```
And enable these optional ones, which might be useful to you later on. The `proxy` module is useful for setting up reverse proxies to services like gitea, and `headers` is useful to tweak global header values:
```sh
a2enmod proxy
a2enmod headers
```
Now, restart apache2:
```sh
service apache2 restart
```
Just for good measure, please check if your webserver is running. Access it through HTTP, not HTTPS. You should see the apache2 default landing page.
## 000-default.conf
Now, edit the following file: `/etc/apache2/sites-available/000-default.conf`, remove everything and carefully add each rule specified here, while making sure to replace my domains with your own:
1. The `VirtualHost` here instructs apache2 to redirect all **HTTP** traffic that specify an unknown `Host` header be redirected to a specific domain of your choice. Configuring this is not required but highly recommended.
```xml
<VirtualHost *:80>
# no domain = go to 4get.ca
RedirectMatch 301 ^(.*)$ https://4get.ca$1
</VirtualHost>
```
2. This instruction tells apache2 to redirect all HTTP traffic on `Host` lolcat.ca to the HTTPS version of the site. You should add a rule like this for all of your services explicitly.
```xml
<VirtualHost *:80>
ServerName lolcat.ca
RedirectMatch 301 ^(.*)$ https://lolcat.ca$1
</VirtualHost>
```
3. Subdomains won't be matched by the above rule, so I recommend you also add them to be more explicit:
```xml
<VirtualHost *:80>
ServerName www.lolcat.ca
RedirectMatch 301 ^(.*)$ https://lolcat.ca$1
</VirtualHost>
```
... Etc, for every service you own.
4. And finally, append this configuration if you wish to host a tor or i2p access point. This configuration should not be binded to SSL(443) as Let's Encrypt does not let you create certificates for onion sites:
```xml
<VirtualHost *:80>
# tor site
ServerName 4getwebfrq5zr4sxugk6htxvawqehxtdgjrbcn2oslllcol2vepa23yd.onion
# compress
AddOutputFilterByType DEFLATE application/json
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/css
DocumentRoot /var/www/4get
Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^\.]+)$ $1.php [NC,L]
# deny access to private resources
<Directory /var/www/4get/data/>
Order Deny,allow
Deny from all
</Directory>
</VirtualHost>
```
To make the above snippet work, please refer to our <a href="https://git.lolcat.ca/lolcat/4get/src/branch/master/docs/tor.md">tor site guide</a>.
## default-ssl.conf
Now, edit the file `/etc/apache2/sites-available/default-ssl.conf`, remove everything and, again, add each rule while modifying the relevant fields:
First, append the following redirect rule to point traffic from `www.4get.ca` to `4get.ca`:
```xml
<VirtualHost *:443>
ServerName www.4get.ca
SSLEngine On
SSLCertificateFile /etc/letsencrypt/live/4get.ca/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/4get.ca/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/4get.ca/chain.pem
RedirectMatch 301 ^(.*)$ https://4get.ca$1
</VirtualHost>
```
This ruleset tells apache2 where 4get is located (`/var/www/4get`), ensures that `4get.ca/settings` resolves to `4get.ca/settings.php` internally and that we deny access to `/data/*`, which may contain files you might want to keep private. `StdEnvVArs+` will make it so that PHP can view if the connection uses HTTPS, and which cipher was used. Useful for basic bot protection.
Make sure to replace `4get.ca` with your own domain under the `SSLCertificate*` directives!
```xml
<VirtualHost *:443>
ServerName 4get.ca
ServerAdmin will@lolcat.ca
DocumentRoot /var/www/4get
SSLEngine On
SSLOptions +StdEnvVars
#ErrorLog ${APACHE_LOG_DIR}/error.log
AddOutputFilterByType DEFLATE application/json
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/css
SSLCertificateFile /etc/letsencrypt/live/4get.ca/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/4get.ca/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/4get.ca/chain.pem
<Directory /var/www/4get>
Options -MultiViews
AllowOverride All
Require all granted
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^\.]+)$ $1.php [NC,L]
</Directory>
# deny access to private resources
<Directory /var/www/4get/data/>
Order Deny,allow
Deny from all
</Directory>
</VirtualHost>
```
By default, the first rule dictates where traffic should be redirected to in case the client specifies an unknown domain name. Don't forget your webserver's other rules! For a complete real-world example, please <a href="https://git.lolcat.ca/lolcat/4get/src/branch/master/docs/apache2-example.md">check out my real-world config file I use on 4get.ca</a>.
## security.conf
If you enabled the `headers` module, you can head over to `/etc/apache2/conf-enabled/security.conf` and edit:
```sh
ServerTokens Prod # instead off Full
```
and
```sh
ServerSignature Off #instead of On
```
This will ensure that the `Server` header apache2 returns is minimal and doesn't leak information like your host system's OS or apache2 version.
You can also uncomment `Header set X-Content-Type-Options: "nosniff"` and `Header set Content-Security-Policy "frame-ancestors 'self';"` respectively.
## charset.conf
Head over to `/etc/apache2/conf-enabled/charset.conf` and uncomment `AddDefaultCharset UTF-8`.
## other-vhost-access-log.conf
Since none of our configuration files contains any `CustomLog` directives, all we need to do to disable logging entirely is comment out the `CustomLog` directive located in `/etc/apache2/conf-enabled/other-vhost-access-log.conf`. Only error logs will remain if you configured them.
## Symlink everything
Now comes the most important part of the setup. Run
```sh
ln -s /etc/apache2/sites-available/default-ssl.conf /etc/apache2/sites-enabled/default-ssl.conf
```
Otherwise apache2 will ignore our SSL configuration. Handy, huh?
# Setup SSL
Great, now we've configured the webserver, but we still don't have our security certificate. Let's generate one!
First, stop `apache2`.
```sh
service apache2 stop
```
Now, run `certbot`, and specify all of your domains by prepending `-d` every time. Make sure the first domain you specify is your main domain, and the same domain you specified in the configuration above! We use ECDSA encryption here as it's better than RSA.
```sh
certbot certonly --standalone --key-type ecdsa -d 4get.ca -d www.4get.ca -d lolcat.ca -d www.lolcat.ca
```
Certbot should ask you a few questions, just play along. At the end of the setup, certbot should tell you about the location of the certificates. Double check to make sure they correspond to the paths we specified in `default-ssl.conf`. Your certificates should now update every 2-3 months automatically.
After this is complete, create a directory in `/var/www/4get`.
Now, start `apache2`.
```sh
service apache2 start
```
Congratulations! You now have a... 404 error on your webserver, if everything went well. Now's the time to make sure all of our redirect rules work!
# Import the fun junk
Run these commands:
```
cd /var/www/4get
git clone https://git.lolcat.ca/lolcat/4get
chmod 777 -R icons/
```
... And try accessing your webserver. You should now have a working 4get instance!
Please make sure to check out how to further <a href="https://git.lolcat.ca/lolcat/4get/src/branch/master/docs/configure.md">configure 4get</a> to your liking!

View File

@ -1,58 +0,0 @@
# Install guide for Caddy webserver
1. Install dependencies:
`sudo apt install caddy php8.2-dom php8.2-imagick imagemagick php8.2-curl curl php8.2-apcu git`
2. Clone this repository where you want to host this from:
`cd /var/www && sudo git clone https://git.konakona.moe/diowo/4get`
3. Set permission on the `icons` directory inside `4get`
`cd /var/www/4get/ && sudo chmod 777 -R icons/`
4. Add an entry for 4get on your Caddyfile at `/etc/caddy/Caddyfile`
```sh
4get.konakona.moe {
root * /var/www/4get
file_server
encode gzip
php_fastcgi unix//var/run/php/php8.2-fpm.sock {
index index.php
}
redir /{path}.php{query} 301
try_files {path} {path}.php
}
```
Caddy deals with SSL certificates automatically so you don't have to mess with anything. Also if needed, a sample of my Caddyfile can be found [here](https://git.konakona.moe/diowo/misc/src/branch/master/etc/caddy/Caddyfile).
5. Restart Caddy
`sudo systemctl restart caddy`
# Encryption setup
I'm schizoid (as you should) so I'm gonna setup 4096bit key encryption. To complete this step, you need a domain or subdomain in your possession. Make sure that the DNS shit for your domain has propagated properly before continuing, because certbot is a piece of shit that will error out the ass once you reach 5 attempts under an hour.
## Encryption setup on Apache
```sh
certbot --apache --rsa-key-size 4096 -d www.yourdomain.com -d yourdomain.com
```
When it asks to choose a vhost, choose the option with "HTTPS" listed. Don't setup HTTPS for tor, we don't need it (it doesn't even work anyways with let's encrypt)
Edit `000-default-le-ssl.conf`
Add this at the end:
```xml
<Directory /var/www/html/4get>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule (.*) $1.php [L]
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
```

View File

@ -1,68 +0,0 @@
# 4get configuation options
Welcome! This guide assumes that you have a working 4get instance. This will help you configure your instance to the best it can be!
# Files location
1. The main configuration file is located at `data/config.php`
2. The proxies are located in `data/proxies/*.txt`
3. The captcha imagesets are located in `data/captcha/your_image_set/*.png`
4. The captcha font is located in `data/fonts/captcha.ttf`
# Cloudflare bypass (TLS check)
**Note: this only allows you to bypass the browser integrity checks. Captchas & javascript challenges will not be bypassed.**
Configuring this lets you fetch images sitting behind Cloudflare and allows you to scrape the **Yep** & the **Mwmbl** search engines. Please be aware that APT will fight against you and will re-install the openSSL-version of curl constantly when updating.
First, follow these instructions. Only install the Firefox modules:
https://github.com/lwthiker/curl-impersonate/blob/main/INSTALL.md#native-build
Once you did this, you should be able to run the following inside your terminal:
```sh
$ curl_ff117 --version
curl 8.1.1 (x86_64-pc-linux-gnu) libcurl/8.1.1 NSS/3.92 zlib/1.2.13 brotli/1.0.9 zstd/1.5.4 libidn2/2.3.3 nghttp2/1.56.0
Release-Date: 2023-05-23
Protocols: dict file ftp ftps gopher gophers http https imap imaps mqtt pop3 pop3s rtsp smb smbs smtp smtps telnet tftp ws wss
Features: alt-svc AsynchDNS brotli HSTS HTTP2 HTTPS-proxy IDN IPv6 Largefile libz NTLM NTLM_WB SSL threadsafe UnixSockets zstd
```
Now, after compiling, you should have a `libcurl-impersonate-ff.so` sitting somewhere. Mine (on my debian install) is located at `/usr/local/lib/libcurl-impersonate-ff.so`.
Find the `libcurl.so.4` file used by your current installation of curl. For me, this file is located at `/usr/lib/x86_64-linux-gnu/libcurl.so.4`
Now comes the sketchy part: replace `libcurl.so.4` with `libcurl-impersonate-ff.so`. You can do this in the following way:
```sh
sudo rm /usr/lib/x86_64-linux-gnu/libcurl.so.4
sudo cp /usr/local/lib/libcurl-impersonate-ff.so /usr/lib/x86_64-linux-gnu/libcurl.so.4
```
Make sure to restart your webserver and/or PHP daemon, otherwise it will keep using the old library. You should now be able to bypass Cloudflare's shitty checks!!
# Robots.txt
Make sure you configure this right to optimize your search engine presence! Head over to `/robots.txt` and change the 4get.ca domain to your own domain.
# Server listing
To be listed on https://4get.ca/instances , you must contact *any* of the people in the server list and ask them to add you to their list of instances in their configuration. The instance list is distributed, and I don't have control over it.
If you see spammy entries in your instances list, simply remove the instance from your list that pushes the offending entries.
# Proxies
4get supports rotating proxies for scrapers! Configuring one is really easy.
1. Head over to the **proxies** folder. Give it any name you want, like `myproxy`, but make sure it has the `txt` extension.
2. Add your proxies to the file. Examples:
```conf
# format -> <protocol>:<address>:<port>:<username>:<password>
# protocol list:
# raw_ip, http, https, socks4, socks5, socks4a, socks5_hostname
socks5:1.1.1.1:juicy:cloaca00
http:1.3.3.7::
raw_ip::::
```
3. Go to the **main configuration file**. Then, find which website you want to setup a proxy for.
4. Modify the value `false` with `"myproxy"`, with quotes included and the semicolon at the end.
Done! The scraper you chose should now be using the rotating proxies. When asking for the next page of results, it will use the same proxy to avoid detection!
## Important!
If you ever test out a `socks5` proxy locally on your machine and find out it works but doesn't on your server, try supplying the `socks5_hostname` protocol instead. Hopefully this tip can save you 3 hours of your life!

View File

@ -1,152 +0,0 @@
#### Install guide for Docker
When using docker container any environment variables prefixed with `FOURGET_` will be added to the generated config located at `/var/www/html/4get/data/config.php`
When lists of data is expected in [data/config.php](../data/config.php), such as `INSTANCES`, you can pass in a comma separated string via environment variable.
Example:
`FOURGET_INSTANCES="https://4get.ca,https://domain.tld"`
#### Special environment variables
| Name | value | Example |
| - | - | - |
| FOURGET_PROTO | "http" or "https" | "https" |
#### Important directories
| Mountpoint | Description |
| - | - |
| /etc/4get/certs | SSL certificate directory |
| /var/www/html/4get/banner | Custom Banners directory |
| /var/www/html/4get/data/captcha | Captcha dataset |
the certificate directory `/etc/4get/certs` expects files named `fullchain.pem` and `privkey.pem`
The captcha dataset should have a subdirectory for each category. In each category, images should be named from 1.png to X.png, and be 100x100 in size.
example directory structure:
```
captcha/
birds/
1.png
2.png
3.png
anime/
1.png
2.png
```
For more information on configuration view [data/config.php](../data/config.php)
#### Usage
You can start 4get with
```
docker run -d -p 80:80 -e FOURGET_SERVER_NAME="4get.ca" -e FOURGET_PROTO="http" luuul/4get:latest
```
...Or with SSL:
```
docker run -d -p 443:443 -e FOURGET_SERVER_NAME="4get.ca" -e FOURGET_PROTO="https" -v /etc/letsencrypt/live/domain.tld:/etc/4get/certs luuul/4get:latest
```
#### With Docker Compose
Replace relevant values and start with `docker compose up -d`
##### HTTP
```
# docker-compose.yaml
version: "3.7"
services:
fourget:
image: luuul/4get:latest
restart: unless-stopped
environment:
- FOURGET_PROTO=http
- FOURGET_SERVER_NAME=4get.ca
ports:
- "80:80"
```
##### HTTPS
```
# docker-compose.yaml
version: "3.7"
services:
fourget:
image: luuul/4get:latest
restart: unless-stopped
environment:
- FOURGET_PROTO=https
- FOURGET_SERVER_NAME=4get.ca
ports:
- "80:80"
- "443:443"
volumes:
- /etc/letsencrypt/live/domain.tld:/etc/4get/certs
```
##### Captcha Enabled
Set `FOURGET_BOT_PROTECTION=1` and mount a directory containing captcha files to `/var/www/html/4get/data/captcha`
```
# docker-compose.yaml
version: "3.7"
services:
fourget:
image: luuul/4get:latest
restart: unless-stopped
environment:
- FOURGET_PROTO=http
- FOURGET_SERVER_NAME=4get.ca
- FOURGET_BOT_PROTECTION=1
ports:
- "80:80"
volumes:
- ./captcha:/var/www/html/4get/data/captcha
```
##### Custom Banners
```
# docker-compose.yaml
version: "3.7"
services:
fourget:
image: luuul/4get:latest
restart: unless-stopped
environment:
- FOURGET_PROTO=http
- FOURGET_SERVER_NAME=4get.ca
ports:
- "80:80"
volumes:
- ./banners:/var/www/html/4get/banner
```
##### Tor
You can route incoming and outgoing requests through tor by following [docker tor documentation](./docker_tor.md)

View File

@ -1,174 +0,0 @@
#### Overview
This guide will walk you through using 4get in docker with tor running in
another container. This guide covers how to make outgoing and incoming traffic
go through tor.
##### Starting tor
This guide will use `luuul/tor` which is a simple image that installs and starts
tor in an alpine container SocksPort set to 0.0.0.0:9050
For additional configuration you can mount your own `torrc` file to `/etc/tor/torrc`
Remember to set `SocksPort 0.0.0.0:9050` otherwise communication between containers won't work.
You will see this warning `Other people on the Internet might find your computer and use it as an open proxy. Please don't allow this unless you have a good reason.`
This setting is in the torrc of this `luuul/tor` image. If you mount your own torrc then that will be read instead.
If you use `SocksPort 0.0.0.0:9050` anywhere make sure it is inaccessible to outside world.
As long as you don't publish this port (-p or --publish) it shouldn't be accessible to outside world.
Tor always starts a socks5 proxy on port 9050 by default.
##### Route outgoing requests over tor
create a folder named `proxies` and create a file in that folder named `onion.txt`
this folder will be mounted to `/var/www/html/4get/data/proxies/`
directory structure
```
proxies/
onion.txt
```
put the following content into `onion.txt`
More information about this file available in [proxy documentation](./configure.md#Proxies).
```
# proxies/onion.txt
# Note: "tor" is the service name of luuul/tor in docker-compose.yaml
socks5:tor:9050::
```
create a file named `docker-compose.yaml` with the following content
This docker compose file will run `luuul/tor` and `luuul/4get` and configure 4get to load `proxies/onion.txt` for outgoing requests.
If you mount your own torrc make sure you include `SocksPort 0.0.0.0:9050`
Read the warning in [starting tor](./docker_tor.md#Starting-tor)!
```
# docker-compose.yaml
version: "3.7"
services:
tor:
image: luuul/tor:latest
restart: unless-stopped
# Warning: Do not publish port 9050
fourget:
image: luuul/4get:latest
restart: unless-stopped
environment:
- FOURGET_PROTO=http
- FOURGET_SERVER_NAME=4get.ca
# loads proxies/onion.txt
- FOURGET_PROXY_DDG="onion"
- FOURGET_PROXY_BRAVE="onion"
- FOURGET_PROXY_FB="onion"
- FOURGET_PROXY_GOOGLE="onion"
- FOURGET_PROXY_QWANT="onion"
- FOURGET_PROXY_MARGINALIA="onion"
- FOURGET_PROXY_MOJEEK="onion"
- FOURGET_PROXY_SC="onion"
- FOURGET_PROXY_SPOTIFY="onion"
- FOURGET_PROXY_WIBY="onion"
- FOURGET_PROXY_CURLIE="onion"
- FOURGET_PROXY_YT="onion"
- FOURGET_PROXY_YEP="onion"
- FOURGET_PROXY_PINTEREST="onion"
- FOURGET_PROXY_SEZNAM="onion"
- FOURGET_PROXY_NAVER="onion"
- FOURGET_PROXY_GREPPR="onion"
- FOURGET_PROXY_CROWDVIEW="onion"
- FOURGET_PROXY_MWMBL="onion"
- FOURGET_PROXY_FTM="onion"
- FOURGET_PROXY_IMGUR="onion"
- FOURGET_PROXY_YANDEX_W="onion"
- FOURGET_PROXY_YANDEX_I="onion"
- FOURGET_PROXY_YANDEX_V="onion"
ports:
- "80:80"
depends_on:
- tor
volumes:
- ./proxies/:/var/www/html/4get/data/proxies/
```
You can now start both containers with `docker compose up -d`
#### Route incoming requests over tor
This will create a hidden service that will be accessible via an onion link.
1. create a file named `torrc` with the following content
```
# torrc
User root
HiddenServiceDir /var/lib/tor/4get/
HiddenServicePort 80 fourget:80
```
2. create a folder named "4get" which will contain your hidden service keys.
Make sure it has permission `600` otherwise you will get an error
> Permissions on directory /var/lib/tor/4get/ are too permissive.
you can change permissions with
```
chmod 600 4get
```
3. Create a folder named "data" that will contain your DataDirectory
4. create a `docker-compose.yaml` with the following content
```
# docker-compose.yaml
version: "3.7"
services:
fourget:
image: luuul/4get:latest
restart: unless-stopped
environment:
- FOURGET_PROTO=http
- FOURGET_SERVER_NAME=4get.ca
depends_on:
- tor
tor:
image: luuul/tor:latest
restart: unless-stopped
volumes:
- ./torrc:/etc/tor/torrc
- ./4get:/var/lib/tor/4get
- ./data:/root/.tor
```
5. You can now start both with `docker compose up -d`
6. print onion hostname with
```
docker exec `docker ps -qf ancestor=luuul/tor:latest` sh -c "cat /var/lib/tor/4get/hostname"
```
or `cat ./4get/hostname`

View File

@ -1,194 +0,0 @@
<h1 align=center>Installation of 4get in NGINX</h1>
<div align=right>
> NOTE: As the previous version stated, it is better to follow the <a href="https://git.lolcat.ca/lolcat/4get/src/branch/master/docs/apache2.md">Apache2 guide</a> instead of the Nginx one.
> NOTE: This is going to guess that you're using either a <abbr title="(Arch Linux, Artix Linux, Endeavouros, etc...) ">Arch-based system</abbr> or a <abbr title="(Debian, Ubuntu, Devuan, etc...)">Debian-based system</abbr>, although you can still follow it with minor issues.
</div>
1. Login as root.
2. Upgrade your system:
* On Arch-based, run `pacman -Syu`.
* On Debian-based, run `apt update`, then `apt upgrade`.
3. Install the following dependencies:
* `git`: So you can clone <a href="https://git.lolcat.ca/lolcat/4get">this</a> repository.
* `nginx`: So you can run Nginx.
* `php-fpm`: This is what allows Nginx to run *(and show)* PHP files.
* `php-imagick`, `imagemagick`: Image manipulation.
* `php-apcu`: Caching module.
* `php-curl`, `curl`: Transferring data with URLs.
* `php-mbstring`: String utils.
* `certbot`, `certbot-nginx`: ACME client. Used to create SSL certificates.
* In Arch-based distributions:
* `pacman -S nginx certbot php-imagick certbot-nginx imagemagick curl php-apcu git`
* In Debian-based distributions:
* `apt install php-mbstring nginx certbot-nginx certbot php-imagick imagemagick php-curl curl php-apcu git`
<div align=right>
> IMPORTANT: `php-curl`, `php-mbstring` might be a Debian-only package, but this needs further fact checking.
> IMPORTANT: If having issues with `php-apcu` or `libsodium`, go to [^1].
</div>
4. `cd` to `/etc/nginx` and make the `conf.d/` directory if it doesn't exist:
* Again, this guesses you're logged in as root.
```sh
cd /etc/nginx
ls -l conf.d/ # If ls shows conf.d, then it means it exists.
# If it does not, run:
mkdir conf.d
```
5. Make a file inside `conf.d/` called `4get.conf` and place the following content:
* First run `touch conf.d/4get.conf` then `nano conf.d/4get.conf` to open the nano editor: *(Install it if it is not, or use another editor.)*
```sh
server {
access_log /dev/null; # Search log file. Do you really need to?
error_log /dev/null; # Error log file.
# Change this if you have 4get in another folder.
root /var/www/4get;
# Change 'yourdomain' to your domain.
server_name www.yourdomain.com yourdomain.com;
# Port to listen to.
listen 80;
location @php {
try_files $uri.php $uri/index.php =404;
# Change the unix socket address if it's different for you.
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
fastcgi_index index.php;
# Change this to `fastcgi_params` if you use a debian based distribution.
include fastcgi.conf;
fastcgi_intercept_errors on;
}
location / {
try_files $uri @php;
}
location ~* ^(.*)\.php$ {
return 301 $1;
}
}
```
* The above is a very basic configuration and thus will need tweaking to your personal needs. It should still work as-is, though. A 'real world' example is present in [^2].
* After saving the file, check that the `nginx.conf` file inside the main directory includes files inside `conf.d/`:
* It should be inside the the http block: *(The following is an example! Don't just Copy and Paste it!)*
```sh
http {
include mime.types;
include conf.d/*.conf;
types_hash_max_size 4096;
# ...
}
```
* Now, test your configuration with `nginx -t`, if it says that everything is good, restart *(or start)* the Nginx daemon:
* This depends on the init manager, most distributions use `systemd`, but it's better practice to include most.
```sh
# systemd
systemctl stop nginx
systemctl start nginxt
# or
systemctl restart nginx
# openrc
rc-service nginx stop
rc-service nginx start
# or
rc-service nginx restart
# runit
sv down nginx
sv up nginx
# or
sv restart nginx
# s6
s6-rc -d change nginx
s6-rc -u change nginx
# or
s6-svc -r /run/service/nginx
# dinit
dinitctl stop nginx
dinitctl start nginx
# or
dinitctl restart nginx
```
6. Clone the repository to `/var/www`:
* `git clone --depth 1 https://git.lolcat.ca/lolcat/4get 4get` - It clones the repository with the depth of one commit *(so it takes less time to download)* and saves the cloned repository as '4get'.
7. That should be it! There are some extra steps you can take, but it really just depends on you.
<h2 align=center>Encryption setup</h2>
1. Generate a certificate for the domain you're using with:
* Note that `certbot-nginx` is needed.
```sh
certbot --nginx --key-type ecdsa -d www.yourdomain.com -d yourdomain.com
```
2. After that, certbot will deploy the certificate automatically to your 4get conf file; It should be ready to use from there.
<h2 align=center>Tor Setup</h2>
<div align=right>
> IMPORTANT: Tor onion addresses are very long compared to traditional domains, so, Before doing anything, edit `nginx.conf` and increase <abbr title="This setting in your Nginx configuration controls the internal data structure used to manage multiple server names (hostnames) associated with your web server. Each hostname requires a certain amount of memory within this structure. If the size is insufficient, Nginx will encounter errors."><code>server_names_hash_bucket_size</code></abbr> to your needs.
</div>
1. `cd` to `/etc/nginx` *(if you haven't)* and open your `nginx.conf` file.
2. Find the line containing `# server_names_hash_bucket_size 64;` inside said file.
3. Uncomment the line and adjust the value; start with 64, but if you encounter issues, incrementally increase it *(e.g., 128, 256)* until it accommodates your configuration.
4. Open *(or duplicate the configuration)* and edit it:
* Example configuration, again:
```sh
server {
access_log /dev/null; # Search log file. Do you really need to?
error_log /dev/null; # Error log file.
# Change this if you have 4get in another folder.
root /var/www/4get;
# Change 'onionadress.onion' to your onion link.
server_name onionadress.onion;
# Port to listen to.
listen 80;
location @php {
try_files $uri.php $uri/index.php =404;
# Change the unix socket address if it's different for you.
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
fastcgi_index index.php;
# Change this to `fastcgi_params` if you use a debian based distribution.
include fastcgi.conf;
fastcgi_intercept_errors on;
}
location / {
try_files $uri @php;
}
location ~* ^(.*)\.php$ {
return 301 $1;
}
}
```
A real world example is present in [^2].
5. Once done, check the configuration with `nginx -t`. If everything's fine and dandy, refer to <a href="https://git.lolcat.ca/lolcat/4get/src/branch/master/docs/tor.md">the Tor guide</a> to setup your onion site.
<h2 align=center>Other important things</h2>
1. <a href="https://git.lolcat.ca/lolcat/4get/src/branch/master/docs/configure.md">Configuration guide</a>: Things to do after setup.
2. <a href="https://git.lolcat.ca/lolcat/4get/src/branch/master/docs/apache2.md">Apache2 guide</a>: Fallback to this if you couldn't get something to work, or you don't know something.
<h2 align=center>Known issues</h2>
1. https://git.lolcat.ca/lolcat/4get/issues
[^1]: lolcat/4get#40, If having issues with `libsodium`, or `php-apcu`.
[^2]: <a href="https://git.nadeko.net/Fijxu/etc-configs/src/branch/selfhost/nginx/conf.d/4get.conf">git.nadeko.net</a> nadeko.net's 4get instance configuration.

View File

@ -1,16 +0,0 @@
# Tor setup
This guide assumes that there is already a configured webserver sitting on port 80 waiting for localhost connections. The <a href="https://git.lolcat.ca/lolcat/4get/src/branch/master/docs/apache2.md">apache2 guide</a> guides you through this.
1. Login as `root`.
2. Install `tor`.
3. Edit `/etc/tor/torrc`
4. Go to the line that contains `HiddenServiceDir` and `HiddenServicePort`, uncomment those 2 lines and set them like this:
```
HiddenServiceDir /var/lib/tor/4get
HiddenServicePort 80 127.0.0.1:80
```
5. Restart the tor service using `service tor restart`
6. Wait for a while...
7. Run `cat /var/lib/tor/4get/hostname`. That is your onion address!
# Specify your own tor address

View File

@ -1,55 +0,0 @@
<?php
include "lib/frontend.php";
$frontend = new frontend();
include "data/config.php";
$params = "";
$first = true;
foreach($_GET as $key => $value){
if(
!is_string($value) ||
$key == "target"
){
continue;
}
if($first === true){
$first = false;
$params = "?";
}else{
$params .= "&";
}
$params .= urlencode($key) . "=" . urlencode($value);
}
if(
!isset($_GET["target"]) ||
!is_string($_GET["target"])
){
$target = "";
}else{
$target = "/" . urlencode($_GET["target"]);
}
$instances = "";
foreach(config::INSTANCES as $instance){
$instances .= '<tr><td class="expand"><a href="' . htmlspecialchars($instance) . $target . $params . '" target="_BLANK" rel="noreferer">' . htmlspecialchars($instance) . '</a></td></tr>';
}
echo
$frontend->load(
"instances.html",
[
"instances_html" => $instances
]
);

View File

@ -1,28 +0,0 @@
# When the robots.txt is sus
# ⠀⠀⠀⡯⡯⡾⠝⠘⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢊⠘⡮⣣⠪⠢⡑⡌
# ⠀⠀⠀⠟⠝⠈⠀⠀⠀⠡⠀⠠⢈⠠⢐⢠⢂⢔⣐⢄⡂⢔⠀⡁⢉⠸⢨⢑⠕⡌
# ⠀⠀⡀⠁⠀⠀⠀⡀⢂⠡⠈⡔⣕⢮⣳⢯⣿⣻⣟⣯⣯⢷⣫⣆⡂⠀⠀⢐⠑⡌
# ⢀⠠⠐⠈⠀⢀⢂⠢⡂⠕⡁⣝⢮⣳⢽⡽⣾⣻⣿⣯⡯⣟⣞⢾⢜⢆⠀⡀⠀⠪
# ⣬⠂⠀⠀⢀⢂⢪⠨⢂⠥⣺⡪⣗⢗⣽⢽⡯⣿⣽⣷⢿⡽⡾⡽⣝⢎⠀⠀⠀⢡
# ⣿⠀⠀⠀⢂⠢⢂⢥⢱⡹⣪⢞⡵⣻⡪⡯⡯⣟⡾⣿⣻⡽⣯⡻⣪⠧⠑⠀⠁⢐
# ⣿⠀⠀⠀⠢⢑⠠⠑⠕⡝⡎⡗⡝⡎⣞⢽⡹⣕⢯⢻⠹⡹⢚⠝⡷⡽⡨⠀⠀⢔
# ⣿⡯⠀⢈⠈⢄⠂⠂⠐⠀⠌⠠⢑⠱⡱⡱⡑⢔⠁⠀⡀⠐⠐⠐⡡⡹⣪⠀⠀⢘
# ⣿⣽⠀⡀⡊⠀⠐⠨⠈⡁⠂⢈⠠⡱⡽⣷⡑⠁⠠⠑⠀⢉⢇⣤⢘⣪⢽⠀⢌⢎
# ⣿⢾⠀⢌⠌⠀⡁⠢⠂⠐⡀⠀⢀⢳⢽⣽⡺⣨⢄⣑⢉⢃⢭⡲⣕⡭⣹⠠⢐⢗
# ⣿⡗⠀⠢⠡⡱⡸⣔⢵⢱⢸⠈⠀⡪⣳⣳⢹⢜⡵⣱⢱⡱⣳⡹⣵⣻⢔⢅⢬⡷
# ⣷⡇⡂⠡⡑⢕⢕⠕⡑⠡⢂⢊⢐⢕⡝⡮⡧⡳⣝⢴⡐⣁⠃⡫⡒⣕⢏⡮⣷⡟
# ⣷⣻⣅⠑⢌⠢⠁⢐⠠⠑⡐⠐⠌⡪⠮⡫⠪⡪⡪⣺⢸⠰⠡⠠⠐⢱⠨⡪⡪⡰
# ⣯⢷⣟⣇⡂⡂⡌⡀⠀⠁⡂⠅⠂⠀⡑⡄⢇⠇⢝⡨⡠⡁⢐⠠⢀⢪⡐⡜⡪⡊
# ⣿⢽⡾⢹⡄⠕⡅⢇⠂⠑⣴⡬⣬⣬⣆⢮⣦⣷⣵⣷⡗⢃⢮⠱⡸⢰⢱⢸⢨⢌
# ⣯⢯⣟⠸⣳⡅⠜⠔⡌⡐⠈⠻⠟⣿⢿⣿⣿⠿⡻⣃⠢⣱⡳⡱⡩⢢⠣⡃⠢⠁
# ⡯⣟⣞⡇⡿⣽⡪⡘⡰⠨⢐⢀⠢⢢⢄⢤⣰⠼⡾⢕⢕⡵⣝⠎⢌⢪⠪⡘⡌⠀
# ⡯⣳⠯⠚⢊⠡⡂⢂⠨⠊⠔⡑⠬⡸⣘⢬⢪⣪⡺⡼⣕⢯⢞⢕⢝⠎⢻⢼⣀⠀
# ⠁⡂⠔⡁⡢⠣⢀⠢⠀⠅⠱⡐⡱⡘⡔⡕⡕⣲⡹⣎⡮⡏⡑⢜⢼⡱⢩⣗⣯⣟
# ⢀⢂⢑⠀⡂⡃⠅⠊⢄⢑⠠⠑⢕⢕⢝⢮⢺⢕⢟⢮⢊⢢⢱⢄⠃⣇⣞⢞⣞⢾
# ⢀⠢⡑⡀⢂⢊⠠⠁⡂⡐⠀⠅⡈⠪⠪⠪⠣⠫⠑⡁⢔⠕⣜⣜⢦⡰⡎⡯⡾⡽
User-agent: *
Disallow:
Host: 4get.ca
Sitemap: https://4get.ca/sitemap

View File

@ -1,35 +0,0 @@
<?php
header("Content-Type: application/xml");
include "data/config.php";
$domain =
htmlspecialchars(
(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on" ? "https" : "http") .
'://' . $_SERVER["HTTP_HOST"]
);
echo
'<?xml version="1.0" encoding="UTF-8"?>' .
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' .
'<url>' .
'<loc>' . $domain . '/</loc>' .
'<lastmod>2023-07-31T07:56:12+03:00</lastmod>' .
'</url>' .
'<url>' .
'<loc>' . $domain . '/about</loc>' .
'<lastmod>2023-07-31T07:56:12+03:00</lastmod>' .
'</url>' .
'<url>' .
'<loc>' . $domain . '/instances</loc>' .
'<lastmod>2023-07-31T07:56:12+03:00</lastmod>' .
'</url>' .
'<url>' .
'<loc>' . $domain . '/settings</loc>' .
'<lastmod>2023-07-31T07:56:12+03:00</lastmod>' .
'</url>' .
'<url>' .
'<loc>' . $domain . '/api.txt</loc>' .
'<lastmod>2023-07-31T07:56:12+03:00</lastmod>' .
'</url>' .
'</urlset>';

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 193 B

After

Width:  |  Height:  |  Size: 193 B

View File

Before

Width:  |  Height:  |  Size: 753 B

After

Width:  |  Height:  |  Size: 753 B

View File

Before

Width:  |  Height:  |  Size: 744 B

After

Width:  |  Height:  |  Size: 744 B

View File

Before

Width:  |  Height:  |  Size: 216 B

After

Width:  |  Height:  |  Size: 216 B

View File

Before

Width:  |  Height:  |  Size: 216 B

After

Width:  |  Height:  |  Size: 216 B

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Some files were not shown because too many files have changed in this diff Show More