Merge pull request #41 from dablenparty/main

Use production-ready Gunicorn WSGI server instead of Flask dev server
This commit is contained in:
Wasi Master
2026-05-11 23:34:46 +06:00
committed by GitHub
8 changed files with 75 additions and 38 deletions
+14 -3
View File
@@ -1,5 +1,12 @@
# syntax=docker/dockerfile:1
FROM python:3.13.7-alpine FROM python:3.13.7-alpine
ARG UID=1000
ARG GID=1000
RUN addgroup -g $GID thirteener
RUN adduser -D -g '' -u $UID -G thirteener thirteener
# Generic labels # Generic labels
LABEL maintainer="Arian Mollik Wasi <arianmollik323@gmail.com>" LABEL maintainer="Arian Mollik Wasi <arianmollik323@gmail.com>"
LABEL version="0.3.4" LABEL version="0.3.4"
@@ -18,9 +25,13 @@ LABEL org.opencontainers.image.description="My own custom 12ft.io replacement"
LABEL org.opencontainers.image.documentation="https://github.com/wasi-master/13ft/blob/main/README.md" LABEL org.opencontainers.image.documentation="https://github.com/wasi-master/13ft/blob/main/README.md"
LABEL org.opencontainers.image.licenses=MIT LABEL org.opencontainers.image.licenses=MIT
RUN python -m pip install --upgrade pip
USER thirteener
COPY . . COPY . .
RUN pip install -r requirements.txt RUN --mount=type=cache,mode=0755,target=/home/thirteener/.cache/pip \
pip install -U -r requirements.txt
WORKDIR /app WORKDIR /app
EXPOSE 5000 EXPOSE 5000
ENTRYPOINT [ "python" ] CMD [ "python", "-m", "gunicorn", "portable:app" ]
CMD [ "portable.py" ]
+12 -8
View File
@@ -15,6 +15,7 @@ It pretends to be GoogleBot (Google's web crawler) and gets the same content tha
### Using Docker ### Using Docker
Requirements: Requirements:
- docker - docker
- Docker Compose (available as `docker compose`) - Docker Compose (available as `docker compose`)
@@ -44,7 +45,7 @@ If that doesn't work retry but replace `python` with `py`, then try `python3`, t
Then run `portable.py`, click [this link](https://realpython.com/run-python-scripts/) for a tutorial on how to run python scripts. Then run `portable.py`, click [this link](https://realpython.com/run-python-scripts/) for a tutorial on how to run python scripts.
```sh ```sh
python portable.py python -m gunicorn 'portable:app'
``` ```
Then open the link shown in the terminal in the browser and you'll be able to use this Then open the link shown in the terminal in the browser and you'll be able to use this
@@ -58,26 +59,29 @@ python -m pip install -r requirements.txt
FLASK_APP=app/portable.py flask run --host=127.0.0.1 --port=9982 FLASK_APP=app/portable.py flask run --host=127.0.0.1 --port=9982
``` ```
## Using as a Bookmarklet in Chrome: ## Using as a Bookmarklet in Chrome:
You can create a bookmarklet that performs the URL transformation by writing a small JavaScript snippet. Below is the JavaScript code for your bookmarklet: You can create a bookmarklet that performs the URL transformation by writing a small JavaScript snippet. Below is the JavaScript code for your bookmarklet:
```javascript ```javascript
javascript:(function(){window.location.href='https://13ft.wasimaster.me/'+encodeURIComponent(window.location.href);})(); javascript: (function () {
window.location.href =
"https://13ft.wasimaster.me/" + encodeURIComponent(window.location.href);
})();
``` ```
You can replace https://13ft.wasimaster.me with your own 13ft instance if desired. You can replace https://13ft.wasimaster.me with your own 13ft instance if desired.
Steps: Steps:
1. Open Bookmarks Manager: 1. Open Bookmarks Manager:
2. Click on the three dots (menu) in the top-right corner of Chrome. 2. Click on the three dots (menu) in the top-right corner of Chrome.
Go to Bookmarks > Bookmark manager, or simply press Ctrl+Shift+O on Windows/Linux or Cmd+Option+B on Mac. Go to Bookmarks > Bookmark manager, or simply press Ctrl+Shift+O on Windows/Linux or Cmd+Option+B on Mac.
Create a New Bookmark: Create a New Bookmark:
3. In the Bookmark Manager, click the three-dot menu in the top-right corner of the window and select Add new bookmark. 3. In the Bookmark Manager, click the three-dot menu in the top-right corner of the window and select Add new bookmark.
Enter Bookmark Details: Enter Bookmark Details: - Name: Enter a name for your bookmarklet, such as "13ft-ize". This name will show as a bookmark title in the bookmarks bar - URL: Paste the JavaScript code provided above into the URL field.
- Name: Enter a name for your bookmarklet, such as "13ft-ize". This name will show as a bookmark title in the bookmarks bar
- URL: Paste the JavaScript code provided above into the URL field.
4. Click Save. 4. Click Save.
Using the Bookmarklet: Using the Bookmarklet:
+4
View File
@@ -0,0 +1,4 @@
from os import environ
# https://docs.gunicorn.org/en/stable/settings.html#settings
bind = f"0.0.0.0:{environ.get('PORT', 5000)}"
+14 -11
View File
@@ -1,36 +1,39 @@
from urllib.parse import urljoin, urlparse
import flask import flask
import requests import requests
from flask import request
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from urllib.parse import urlparse, urljoin from flask import request
app = flask.Flask(__name__) app = flask.Flask(__name__)
googlebot_headers = { googlebot_headers = {
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.6533.119 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" "User-Agent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.6533.119 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
} }
def add_base_tag(html_content, original_url): def add_base_tag(html_content, original_url):
soup = BeautifulSoup(html_content, 'html.parser') soup = BeautifulSoup(html_content, "html.parser")
parsed_url = urlparse(original_url) parsed_url = urlparse(original_url)
base_url = f"{parsed_url.scheme}://{parsed_url.netloc}/" base_url = f"{parsed_url.scheme}://{parsed_url.netloc}/"
# Handle paths that are not root, e.g., "https://x.com/some/path/w.html" # Handle paths that are not root, e.g., "https://x.com/some/path/w.html"
if parsed_url.path and not parsed_url.path.endswith('/'): if parsed_url.path and not parsed_url.path.endswith("/"):
base_url = urljoin(base_url, parsed_url.path.rsplit('/', 1)[0] + '/') base_url = urljoin(base_url, parsed_url.path.rsplit("/", 1)[0] + "/")
base_tag = soup.find('base') base_tag = soup.find("base")
print(base_url) print(base_url)
if not base_tag: if not base_tag:
new_base_tag = soup.new_tag('base', href=base_url) new_base_tag = soup.new_tag("base", href=base_url)
if soup.head: if soup.head:
soup.head.insert(0, new_base_tag) soup.head.insert(0, new_base_tag)
else: else:
head_tag = soup.new_tag('head') head_tag = soup.new_tag("head")
head_tag.insert(0, new_base_tag) head_tag.insert(0, new_base_tag)
soup.insert(0, head_tag) soup.insert(0, head_tag)
return str(soup) return str(soup)
def bypass_paywall(url): def bypass_paywall(url):
""" """
Bypass paywall for a given url Bypass paywall for a given url
+16 -12
View File
@@ -1,8 +1,9 @@
from urllib.parse import urljoin, urlparse
import flask import flask
import requests import requests
from flask import request
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from urllib.parse import urlparse, urljoin from flask import request
app = flask.Flask(__name__) app = flask.Flask(__name__)
googlebot_headers = { googlebot_headers = {
@@ -187,28 +188,30 @@ html = """
</html> </html>
""" """
def add_base_tag(html_content, original_url): def add_base_tag(html_content, original_url):
soup = BeautifulSoup(html_content, 'html.parser') soup = BeautifulSoup(html_content, "html.parser")
parsed_url = urlparse(original_url) parsed_url = urlparse(original_url)
base_url = f"{parsed_url.scheme}://{parsed_url.netloc}/" base_url = f"{parsed_url.scheme}://{parsed_url.netloc}/"
# Handle paths that are not root, e.g., "https://x.com/some/path/w.html" # Handle paths that are not root, e.g., "https://x.com/some/path/w.html"
if parsed_url.path and not parsed_url.path.endswith('/'): if parsed_url.path and not parsed_url.path.endswith("/"):
base_url = urljoin(base_url, parsed_url.path.rsplit('/', 1)[0] + '/') base_url = urljoin(base_url, parsed_url.path.rsplit("/", 1)[0] + "/")
base_tag = soup.find('base') base_tag = soup.find("base")
print(base_url) print(base_url)
if not base_tag: if not base_tag:
new_base_tag = soup.new_tag('base', href=base_url) new_base_tag = soup.new_tag("base", href=base_url)
if soup.head: if soup.head:
soup.head.insert(0, new_base_tag) soup.head.insert(0, new_base_tag)
else: else:
head_tag = soup.new_tag('head') head_tag = soup.new_tag("head")
head_tag.insert(0, new_base_tag) head_tag.insert(0, new_base_tag)
soup.insert(0, head_tag) soup.insert(0, head_tag)
return str(soup) return str(soup)
def bypass_paywall(url): def bypass_paywall(url):
""" """
Bypass paywall for a given url Bypass paywall for a given url
@@ -257,4 +260,5 @@ def get_article(path):
return "Invalid URL", 400 return "Invalid URL", 400
app.run(host="0.0.0.0", port=5000, debug=False) if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, debug=False)
+3 -2
View File
@@ -1,3 +1,4 @@
flask
requests
bs4 bs4
flask
gunicorn
requests
+9
View File
@@ -0,0 +1,9 @@
services:
13ft:
container_name: 13ft
hostname: 13ft
build:
context: .
restart: unless-stopped
ports:
- "5000:5000"
+3 -2
View File
@@ -1,3 +1,4 @@
flask
requests
bs4 bs4
flask
gunicorn
requests