structure: refactor codebase
3
.gitmodules
vendored
|
|
@ -1,3 +0,0 @@
|
|||
[submodule "server/toolkits/core"]
|
||||
path = server/toolkits/core
|
||||
url = https://github.com/Freedium-cfd/core
|
||||
16
Dockerfile
|
|
@ -1,7 +1,9 @@
|
|||
FROM python:3.12.3-slim
|
||||
FROM python:3.12.3
|
||||
# -slim
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt update && apt upgrade -y && apt install gcc -y && apt clean # && rm /var/lib/apt/lists/*
|
||||
|
||||
RUN pip install poetry && poetry config virtualenvs.create false
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
|
@ -16,14 +18,12 @@ RUN pip3 install --no-cache-dir ./database-lib
|
|||
COPY ./core ./core
|
||||
RUN pip3 install --no-cache-dir ./core
|
||||
|
||||
# COPY ./server ./server
|
||||
COPY ./web ./web
|
||||
|
||||
COPY ./requirements.txt ./
|
||||
RUN pip3 install --no-cache-dir -r requirements.txt
|
||||
WORKDIR /app/web
|
||||
|
||||
COPY ./requirements-fast.txt ./
|
||||
RUN pip3 install --no-cache-dir -r requirements-fast.txt
|
||||
RUN poetry install
|
||||
|
||||
EXPOSE 7080
|
||||
# EXPOSE 7080
|
||||
|
||||
CMD ["python3", "-m", "server", "server"]
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
FROM shturman/dante:1.4.2
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt update && apt upgrade -y && apt install -y curl && apt clean && rm -rf /var/lib/apt/lists/*
|
||||
59
README.md
|
|
@ -8,10 +8,6 @@
|
|||
|
||||
## FAQ
|
||||
|
||||
### What is happened to GitHub organization?
|
||||
|
||||
Our whole Github organization is not public for now. Reddit community, that was beginning all of that unfourtunately also gone. So we have moved to Codeberg
|
||||
|
||||
### Why did we create Freedium?
|
||||
|
||||
In mid-June to mid-July 2023, Medium changed their paywall method, and all old paywall bypass methods we had stopped working. So I became obsessed with the idea of creating a service to bypass Medium's paywalled posts. Honestly I am not a big fan of Medium, but I sometimes read articles to improve my knowledge.
|
||||
|
|
@ -20,27 +16,20 @@ In mid-June to mid-July 2023, Medium changed their paywall method, and all old p
|
|||
|
||||
In the first version of Freedium, we reverse-engineered Medium.com's GraphQL endpoints and built our own parser and toolkits to show you unpaywalled Medium posts. Unfortunately, Medium closed this loophole and nowadays we just pay subscriptions and share access through Freedium. Sometimes we got a bugs because of the self-written parser, but we are working to make Freedium bug-free.
|
||||
|
||||
### What language are being used?
|
||||
|
||||
We use Python, with Jinja template builder, and some JS magic in Frontend :)
|
||||
|
||||
### Wow! I would like to contribute to Freedium. How can I do that?
|
||||
|
||||
We need volunteers who have Medium subscriptions because we might get banned by Medium. And if you developer you can start from the this (https://codeberg.org/Freedium-cfd/web) repository.
|
||||
|
||||
### Plans, future?
|
||||
|
||||
Speed up Freedium, and probably create open source Medium frontend in next life
|
||||
Speed up Freedium, add support for more services than just Medium and (probably) create open source Medium frontend (in next life)
|
||||
|
||||
## Tech stack:
|
||||
## Technologies:
|
||||
|
||||
- FastAPI, Gunicorn, Unicorn as worker,
|
||||
- Tailwinds CSS v3
|
||||
- Dragonfly (Redis like key-value database)
|
||||
- Jinja2
|
||||
- Python 3.9+
|
||||
- Caddy
|
||||
- Sentry
|
||||
- Backend: Python 3.9+, Unicorn, FastAPI, Jinja2, Sentry
|
||||
- Frontend: Tailwinds CSS v3
|
||||
- Database: PostgreSQL, Dragonfly (Redis and Memcached compatible key-value database)
|
||||
- Utils: Caddy, Docker, Docker Compose, Cloudflare WARP proxy (wgcf)
|
||||
|
||||
## Local run:
|
||||
|
||||
|
|
@ -50,18 +39,38 @@ Requirements:
|
|||
- git
|
||||
- Linux. Officially, we can't guarantee that Freedium will work on other OS.
|
||||
|
||||
We need configure our Freedium instance. Copy `.env_template` to `.env` configuration file and set values, required for you.
|
||||
To configure your Freedium instance, follow these steps:
|
||||
|
||||
```
|
||||
git clone https://codeberg.org/Freedium-cfd/web/ ./web --depth 1
|
||||
cd ./web
|
||||
cp .env_template .env
|
||||
# do some changes in .env, if you want
|
||||
sudo docker-compose -f docker-compose-dev.yml up
|
||||
```
|
||||
1. Clone the repository:
|
||||
|
||||
```
|
||||
git clone https://codeberg.org/Freedium-cfd/web/ ./web --depth 1
|
||||
cd ./web
|
||||
```
|
||||
|
||||
2. Create and configure the environment file:
|
||||
|
||||
```
|
||||
cp .env_template .env
|
||||
```
|
||||
|
||||
Open the `.env` file and adjust the values as needed for your setup.
|
||||
|
||||
3. Set up the Docker network:
|
||||
|
||||
```
|
||||
sudo docker network create caddy_net
|
||||
```
|
||||
|
||||
4. Start the Freedium services:
|
||||
```
|
||||
sudo docker compose -f ./docker-compose/docker-compose.yml up
|
||||
```
|
||||
|
||||
And now you can access local instance of Freedium by opening browser and type `http://localhost:6752`.
|
||||
|
||||
These steps will set up and run your local Freedium instance.
|
||||
|
||||
## TODO:
|
||||
|
||||
- Integrate library notifiers - https://github.com/liiight/notifiers
|
||||
|
|
|
|||
|
|
@ -106,12 +106,6 @@
|
|||
}
|
||||
|
||||
|
||||
handle_path /sitemap.xml {
|
||||
root * /static/sitemap.xml
|
||||
file_server
|
||||
}
|
||||
|
||||
|
||||
handle_path /websocket {
|
||||
respond "Access denied" 403
|
||||
}
|
||||
|
|
@ -208,6 +202,6 @@
|
|||
|
||||
|
||||
route /* {
|
||||
reverse_proxy web:7080
|
||||
reverse_proxy freedium_web:7080
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,6 @@
|
|||
{{ template }}
|
||||
|
||||
route /* {
|
||||
reverse_proxy web:7080
|
||||
reverse_proxy freedium_web:7080
|
||||
}
|
||||
}
|
||||
|
|
@ -107,12 +107,6 @@ freedium.cfd {
|
|||
}
|
||||
|
||||
|
||||
handle_path /sitemap.xml {
|
||||
root * /static/sitemap.xml
|
||||
file_server
|
||||
}
|
||||
|
||||
|
||||
handle_path /websocket {
|
||||
respond "Access denied" 403
|
||||
}
|
||||
|
|
@ -209,6 +203,6 @@ freedium.cfd {
|
|||
|
||||
|
||||
route /* {
|
||||
reverse_proxy web:7080
|
||||
reverse_proxy freedium_web:7080
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,6 @@ freedium.cfd {
|
|||
{{ template }}
|
||||
|
||||
route /* {
|
||||
reverse_proxy web:7080
|
||||
reverse_proxy freedium_web:7080
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 7 KiB After Width: | Height: | Size: 7 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
|
@ -1,3 +0,0 @@
|
|||
djlint==1.32.1
|
||||
ruff==0.0.261
|
||||
black==23.7.0
|
||||
|
|
@ -9,4 +9,5 @@ bs4==0.0.1
|
|||
Jinja2==3.1.2
|
||||
beautifulsoup4==4.12.2
|
||||
async-lru==2.0.4
|
||||
orjson==3.10.6
|
||||
asyncer
|
||||
|
|
@ -1,282 +0,0 @@
|
|||
# https://blog.thelazyfox.xyz/how-to-create-healthchecks-for-docker/
|
||||
|
||||
version: '3.7'
|
||||
|
||||
services:
|
||||
caddy_freedium:
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: ./DockerfileCaddy
|
||||
# container_name: caddy_freedium
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
ports:
|
||||
- "6752:6752"
|
||||
volumes:
|
||||
- ./CaddyfileDev:/etc/caddy/Caddyfile # - ./CaddyfileMaintance:/etc/caddy/Caddyfile
|
||||
- caddy_data:/data
|
||||
- caddy_config:/config
|
||||
- ./static:/static
|
||||
networks:
|
||||
- caddy_net
|
||||
healthcheck:
|
||||
test: [ "CMD-SHELL", "curl -f http://localhost:6752/ --max-time 80 --header 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.3 Safari/605.1.15'" ]
|
||||
interval: 30s
|
||||
start_period: 20s
|
||||
timeout: 80s
|
||||
retries: 3
|
||||
# restart: always
|
||||
stop_grace_period: 2m
|
||||
|
||||
web:
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: ./Dockerfile
|
||||
command: python3 -m server server
|
||||
environment:
|
||||
- "PROXY_LIST=socks5://wgcf1:1080,socks5://wgcf2:1080"
|
||||
volumes:
|
||||
# - ./.env:/app/.env
|
||||
# - ./server/user_data/logs:/app/server/user_data/logs
|
||||
- .:/app
|
||||
- ./core/medium_parser/:/app/medium_parser
|
||||
- ./core/rl_string_helper/:/app/rl_string_helper
|
||||
# develop:
|
||||
# watch:
|
||||
# - action: rebuild
|
||||
# path: ./server
|
||||
# target: /app/server
|
||||
# - action: rebuild
|
||||
# path: ./core
|
||||
# target: /app/core
|
||||
# - action: rebuild
|
||||
# path: ./rl_string_helper
|
||||
# target: /app/rl_string_helper
|
||||
# - action: rebuild
|
||||
# path: ./database-lib
|
||||
# target: /app/database-lib
|
||||
# - action: rebuild
|
||||
# path: "**/requirements.txt"
|
||||
# - action: rebuild
|
||||
# path: "**/requirements-fast.txt"
|
||||
ports:
|
||||
- "7080:7080"
|
||||
networks:
|
||||
- caddy_net
|
||||
# healthcheck:
|
||||
# test: ["CMD-SHELL", "curl -f http://web:7080/ --max-time 80 --header 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.3 Safari/605.1.15'"]
|
||||
# interval: 30s
|
||||
# start_period: 20s
|
||||
# timeout: 80s
|
||||
# retries: 3
|
||||
# restart: always
|
||||
mem_limit: 4g
|
||||
stop_grace_period: 2m
|
||||
|
||||
########## WGCF PROXY ----1---- ##########
|
||||
|
||||
wgcf1:
|
||||
image: neilpang/wgcf-docker:latest
|
||||
container_name: wgcf1
|
||||
volumes:
|
||||
# - ./wgcf:/wgcf
|
||||
- /lib/modules:/lib/modules
|
||||
- ./scripts/entryWGCF.sh:/entry.sh
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
privileged: true
|
||||
sysctls:
|
||||
net.ipv6.conf.all.disable_ipv6: 0
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
# ports:
|
||||
# - "9681:1080"
|
||||
restart: always
|
||||
networks:
|
||||
- caddy_net
|
||||
healthcheck:
|
||||
test: curl -fs https://www.cloudflare.com/cdn-cgi/trace | grep -q -E 'warp=(on|plus)' && exit 0 || exit 1
|
||||
interval: 5s
|
||||
timeout: 2s
|
||||
retries: 10000
|
||||
|
||||
dante_1:
|
||||
image: shturman/dante:1.4.2
|
||||
container_name: dante_1
|
||||
# build:
|
||||
# context: ./
|
||||
# dockerfile: ./DockerfileDante
|
||||
volumes:
|
||||
- ./scripts/dante.config:/etc/sockd.conf
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
restart: always
|
||||
environment:
|
||||
- CFGFILE=/etc/sockd.conf
|
||||
network_mode: "service:wgcf1"
|
||||
depends_on:
|
||||
wgcf1:
|
||||
condition: service_healthy
|
||||
# healthcheck:
|
||||
# test: curl --proxy socks5://localhost:1080 https://google.com
|
||||
# interval: 5s
|
||||
# timeout: 2s
|
||||
# retries: 5
|
||||
|
||||
wgcf1_healthcare_service:
|
||||
build:
|
||||
context: ./scripts/wgcf-healthcare
|
||||
dockerfile: Dockerfile
|
||||
environment:
|
||||
- TG_TOKEN=${TELEGRAM_BOT_TOKEN}
|
||||
- TG_CHAT_ID=${TELEGRAM_ADMIN_ID}
|
||||
- CONTAINERS_TO_RESTART=wgcf1,dante_1
|
||||
- PROXY_HOST=wgcf1
|
||||
- PROXY_PORT=1080
|
||||
- SLEEP_DURATION=30
|
||||
# depends_on:
|
||||
# - dante_1
|
||||
restart: always
|
||||
networks:
|
||||
- caddy_net
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
# network_mode: "host"
|
||||
|
||||
########## WGCF PROXY ----2---- ##########
|
||||
|
||||
wgcf2:
|
||||
image: neilpang/wgcf-docker:latest
|
||||
container_name: wgcf2
|
||||
volumes:
|
||||
# - ./wgcf:/wgcf
|
||||
- /lib/modules:/lib/modules
|
||||
- ./scripts/entryWGCF.sh:/entry.sh
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
privileged: true
|
||||
sysctls:
|
||||
net.ipv6.conf.all.disable_ipv6: 0
|
||||
depends_on:
|
||||
wgcf1:
|
||||
condition: service_healthy
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
# ports:
|
||||
# - "9681:1080"
|
||||
restart: always
|
||||
networks:
|
||||
- caddy_net
|
||||
healthcheck:
|
||||
test: curl -fs https://www.cloudflare.com/cdn-cgi/trace | grep -q -E 'warp=(on|plus)' && exit 0 || exit 1
|
||||
interval: 5s
|
||||
timeout: 2s
|
||||
retries: 10000
|
||||
|
||||
dante_2:
|
||||
image: shturman/dante:1.4.2
|
||||
container_name: dante_2
|
||||
# build:
|
||||
# context: ./
|
||||
# dockerfile: ./DockerfileDante
|
||||
volumes:
|
||||
- ./scripts/dante.config:/etc/sockd.conf
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
restart: always
|
||||
environment:
|
||||
- CFGFILE=/etc/sockd.conf
|
||||
network_mode: "service:wgcf2"
|
||||
depends_on:
|
||||
wgcf2:
|
||||
condition: service_healthy
|
||||
# healthcheck:
|
||||
# test: curl --proxy socks5://localhost:1080 https://google.com
|
||||
# interval: 5s
|
||||
# timeout: 2s
|
||||
# retries: 5
|
||||
|
||||
wgcf2_healthcare_service:
|
||||
build:
|
||||
context: ./scripts/wgcf-healthcare
|
||||
dockerfile: Dockerfile
|
||||
environment:
|
||||
- TG_TOKEN=${TELEGRAM_BOT_TOKEN}
|
||||
- TG_CHAT_ID=${TELEGRAM_ADMIN_ID}
|
||||
- CONTAINERS_TO_RESTART=wgcf2,dante_2
|
||||
- PROXY_HOST=wgcf2
|
||||
- PROXY_PORT=1080
|
||||
- SLEEP_DURATION=40
|
||||
# depends_on:
|
||||
# - dante_2
|
||||
restart: always
|
||||
networks:
|
||||
- caddy_net
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
# network_mode: "host"
|
||||
|
||||
# redis_service:
|
||||
# image: redis:latest
|
||||
# networks:
|
||||
# - caddy_net
|
||||
# healthcheck:
|
||||
# test: ["CMD", "redis-cli", "ping"]
|
||||
# interval: 30s
|
||||
# start_period: 20s
|
||||
# timeout: 10s
|
||||
# retries: 3
|
||||
# restart: always
|
||||
# stop_grace_period: 2m
|
||||
|
||||
redis_service:
|
||||
image: 'docker.dragonflydb.io/dragonflydb/dragonfly'
|
||||
ulimits:
|
||||
memlock: -1
|
||||
# expose:
|
||||
# - 6379
|
||||
networks:
|
||||
- caddy_net
|
||||
volumes:
|
||||
- dragonflydata:/data
|
||||
healthcheck:
|
||||
test: [ "CMD", "redis-cli", "ping" ]
|
||||
interval: 30s
|
||||
start_period: 20s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
restart: always
|
||||
stop_grace_period: 2m
|
||||
mem_limit: 2g
|
||||
|
||||
postgres:
|
||||
image: postgres:16.3-alpine3.20
|
||||
networks:
|
||||
- caddy_net
|
||||
# ports:
|
||||
# - 5432:5432
|
||||
volumes:
|
||||
# - ~/apps/postgres:/var/lib/postgresql/data
|
||||
- ./postgres:/var/lib/postgresql/data
|
||||
environment:
|
||||
- POSTGRES_PASSWORD=postgres
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_DB=postgres
|
||||
stop_grace_period: 2m
|
||||
|
||||
autoheal:
|
||||
restart: always
|
||||
image: willfarrell/autoheal
|
||||
environment:
|
||||
- AUTOHEAL_CONTAINER_LABEL=all
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
stop_grace_period: 2m
|
||||
|
||||
volumes:
|
||||
caddy_data:
|
||||
caddy_config:
|
||||
dragonflydata:
|
||||
|
||||
|
||||
networks:
|
||||
caddy_net:
|
||||
external: true
|
||||
37
docker-compose/docker-compose.db.yml
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
version: '3.7'
|
||||
|
||||
services:
|
||||
redis_service:
|
||||
container_name: redis_service
|
||||
image: 'docker.dragonflydb.io/dragonflydb/dragonfly'
|
||||
ulimits:
|
||||
memlock: -1
|
||||
networks:
|
||||
- freedium_net
|
||||
volumes:
|
||||
- freedium_dragonflydata:/data
|
||||
healthcheck:
|
||||
test: [ "CMD", "redis-cli", "ping" ]
|
||||
interval: 30s
|
||||
start_period: 20s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
restart: always
|
||||
stop_grace_period: 2m
|
||||
mem_limit: 2g
|
||||
|
||||
postgres:
|
||||
image: postgres:16.3-alpine3.20
|
||||
container_name: freedium_postgres
|
||||
networks:
|
||||
- freedium_net
|
||||
volumes:
|
||||
- ./postgres:/var/lib/postgresql/data
|
||||
environment:
|
||||
- POSTGRES_PASSWORD=postgres
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_DB=postgres
|
||||
stop_grace_period: 2m
|
||||
|
||||
volumes:
|
||||
freedium_dragonflydata:
|
||||
55
docker-compose/docker-compose.main.yml
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
version: '3.7'
|
||||
|
||||
services:
|
||||
caddy_freedium:
|
||||
container_name: caddy_freedium
|
||||
build:
|
||||
context: ../caddy
|
||||
dockerfile: Dockerfile
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
ports:
|
||||
- "6752:6752"
|
||||
volumes:
|
||||
- ../caddy/CaddyfileDev:/etc/caddy/Caddyfile
|
||||
- freedium_caddy_data:/data
|
||||
- freedium_caddy_config:/config
|
||||
- ../caddy/static:/static
|
||||
networks:
|
||||
- freedium_net
|
||||
- caddy_net
|
||||
healthcheck:
|
||||
test: [ "CMD-SHELL", "curl -f http://localhost:6752/ --max-time 80 --header 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.3 Safari/605.1.15'" ]
|
||||
interval: 30s
|
||||
start_period: 20s
|
||||
timeout: 80s
|
||||
retries: 3
|
||||
stop_grace_period: 2m
|
||||
|
||||
freedium_web:
|
||||
container_name: freedium_web
|
||||
build:
|
||||
context: ../
|
||||
dockerfile: Dockerfile
|
||||
environment:
|
||||
- "PROXY_LIST=socks5://wgcf1:1080,socks5://wgcf2:1080"
|
||||
# volumes:
|
||||
# - ./web:/app/web
|
||||
# - ./core/medium_parser/:/app/medium_parser
|
||||
# - ./core/rl_string_helper/:/app/rl_string_helper
|
||||
ports:
|
||||
- "7080:7080"
|
||||
networks:
|
||||
- freedium_net
|
||||
mem_limit: 4g
|
||||
stop_grace_period: 2m
|
||||
|
||||
volumes:
|
||||
freedium_caddy_data:
|
||||
freedium_caddy_config:
|
||||
|
||||
|
||||
networks:
|
||||
caddy_net:
|
||||
external: true
|
||||
freedium_net:
|
||||
12
docker-compose/docker-compose.utility.yml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
version: '3.7'
|
||||
|
||||
services:
|
||||
autoheal:
|
||||
container_name: freedium_autoheal
|
||||
restart: always
|
||||
image: willfarrell/autoheal
|
||||
environment:
|
||||
- AUTOHEAL_CONTAINER_LABEL=all
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
stop_grace_period: 2m
|
||||
111
docker-compose/docker-compose.wgcf.yml
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
version: '3.7'
|
||||
|
||||
services:
|
||||
wgcf1:
|
||||
image: neilpang/wgcf-docker:latest
|
||||
container_name: wgcf1
|
||||
volumes:
|
||||
- /lib/modules:/lib/modules
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ../wgcf/entry.sh:/entry.sh
|
||||
privileged: true
|
||||
sysctls:
|
||||
net.ipv6.conf.all.disable_ipv6: 0
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
restart: always
|
||||
networks:
|
||||
- freedium_net
|
||||
healthcheck:
|
||||
test: curl -fs https://www.cloudflare.com/cdn-cgi/trace | grep -q -E 'warp=(on|plus)' && exit 0 || exit 1
|
||||
interval: 5s
|
||||
timeout: 2s
|
||||
retries: 10000
|
||||
|
||||
dante_1:
|
||||
image: shturman/dante:1.4.2
|
||||
container_name: dante_1
|
||||
volumes:
|
||||
- ../wgcf/dante/sockd.conf:/etc/sockd.conf
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
restart: always
|
||||
environment:
|
||||
- CFGFILE=/etc/sockd.conf
|
||||
network_mode: "service:wgcf1"
|
||||
depends_on:
|
||||
wgcf1:
|
||||
condition: service_healthy
|
||||
|
||||
wgcf1_healthcare_service:
|
||||
build:
|
||||
context: ../wgcf/wgcf-healthcare
|
||||
dockerfile: Dockerfile
|
||||
environment:
|
||||
- TG_TOKEN=${TELEGRAM_BOT_TOKEN}
|
||||
- TG_CHAT_ID=${TELEGRAM_ADMIN_ID}
|
||||
- CONTAINERS_TO_RESTART=wgcf1,dante_1
|
||||
- PROXY_HOST=wgcf1
|
||||
- PROXY_PORT=1080
|
||||
- SLEEP_DURATION=30
|
||||
restart: always
|
||||
networks:
|
||||
- freedium_net
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
|
||||
wgcf2:
|
||||
image: neilpang/wgcf-docker:latest
|
||||
container_name: wgcf2
|
||||
volumes:
|
||||
- /lib/modules:/lib/modules
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ../wgcf/entry.sh:/entry.sh
|
||||
privileged: true
|
||||
sysctls:
|
||||
net.ipv6.conf.all.disable_ipv6: 0
|
||||
depends_on:
|
||||
wgcf1:
|
||||
condition: service_healthy
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
restart: always
|
||||
networks:
|
||||
- freedium_net
|
||||
healthcheck:
|
||||
test: curl -fs https://www.cloudflare.com/cdn-cgi/trace | grep -q -E 'warp=(on|plus)' && exit 0 || exit 1
|
||||
interval: 5s
|
||||
timeout: 2s
|
||||
retries: 10000
|
||||
|
||||
dante_2:
|
||||
image: shturman/dante:1.4.2
|
||||
container_name: dante_2
|
||||
volumes:
|
||||
- ../wgcf/dante/sockd.conf:/etc/sockd.conf
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
restart: always
|
||||
environment:
|
||||
- CFGFILE=/etc/sockd.conf
|
||||
network_mode: "service:wgcf2"
|
||||
depends_on:
|
||||
wgcf2:
|
||||
condition: service_healthy
|
||||
|
||||
wgcf2_healthcare_service:
|
||||
build:
|
||||
context: ../wgcf/wgcf-healthcare
|
||||
dockerfile: Dockerfile
|
||||
environment:
|
||||
- TG_TOKEN=${TELEGRAM_BOT_TOKEN}
|
||||
- TG_CHAT_ID=${TELEGRAM_ADMIN_ID}
|
||||
- CONTAINERS_TO_RESTART=wgcf2,dante_2
|
||||
- PROXY_HOST=wgcf2
|
||||
- PROXY_PORT=1080
|
||||
- SLEEP_DURATION=40
|
||||
restart: always
|
||||
networks:
|
||||
- freedium_net
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
7
docker-compose/docker-compose.yml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
version: '3.7'
|
||||
|
||||
include:
|
||||
- docker-compose.main.yml
|
||||
- docker-compose.wgcf.yml
|
||||
- docker-compose.db.yml
|
||||
- docker-compose.utility.yml
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
# https://blog.thelazyfox.xyz/how-to-create-healthchecks-for-docker/
|
||||
|
||||
version: '3.7'
|
||||
|
||||
services:
|
||||
caddy:
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: ./DockerfileCaddy
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./CaddyfileProd:/etc/caddy/Caddyfile
|
||||
- caddy_data:/data
|
||||
- caddy_config:/config
|
||||
networks:
|
||||
- web_network
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl http://caddy:80/ --max-time 80"]
|
||||
interval: 30s
|
||||
start_period: 20s
|
||||
timeout: 80s
|
||||
retries: 3
|
||||
restart: always
|
||||
|
||||
web:
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: ./Dockerfile
|
||||
command: python3 -m server server
|
||||
volumes:
|
||||
- .:/app
|
||||
expose:
|
||||
- 7080
|
||||
networks:
|
||||
- web_network
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl http://localhost:7080/ --max-time 60"]
|
||||
interval: 30s
|
||||
start_period: 20s
|
||||
timeout: 80s
|
||||
retries: 3
|
||||
restart: always
|
||||
mem_limit: 2g
|
||||
|
||||
dragonfly:
|
||||
image: 'docker.dragonflydb.io/dragonflydb/dragonfly'
|
||||
ulimits:
|
||||
memlock: -1
|
||||
expose:
|
||||
- 6379
|
||||
networks:
|
||||
- web_network
|
||||
volumes:
|
||||
- dragonflydata:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 30s
|
||||
start_period: 20s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
restart: always
|
||||
|
||||
autoheal:
|
||||
restart: always
|
||||
image: willfarrell/autoheal
|
||||
environment:
|
||||
- AUTOHEAL_CONTAINER_LABEL=all
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
|
||||
volumes:
|
||||
caddy_data:
|
||||
caddy_config:
|
||||
dragonflydata:
|
||||
|
||||
networks:
|
||||
web_network:
|
||||
driver: bridge
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
djlint==1.32.1
|
||||
ruff==0.0.261
|
||||
black==23.7.0
|
||||
|
|
@ -1 +0,0 @@
|
|||
orjson==3.10.6
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
pickledb==0.9.2
|
||||
html5lib==1.1
|
||||
sentry-sdk[fastapi]==1.29.2
|
||||
loguru==0.6.0 # due to: https://github.com/Delgan/loguru/issues/916
|
||||
uvicorn[standard]==0.30.3
|
||||
anyio<=4.0.0 # Workaround to: https://github.getafreenode.com/tiangolo/fastapi/discussions/11652
|
||||
Jinja2==3.1.2
|
||||
fastapi==0.108.0
|
||||
gunicorn==21.2.0
|
||||
redis[hiredis]==4.6.0
|
||||
xkcdpass==1.19.3
|
||||
apscheduler==3.10.4
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
from loguru import logger
|
||||
from .logger_trace import trace
|
||||
from .utils import quote_html, quote_symbol
|
||||
from jinja2 import Environment, DebugUndefined, Template
|
||||
|
||||
from rl_string_helper.mixins.string_assignment import StringAssignmentMixin_py as StringAssignmentMixin
|
||||
from rl_string_helper.mixins.string_assignment import (
|
||||
StringAssignmentMixin_py as StringAssignmentMixin,
|
||||
)
|
||||
|
||||
jinja_env = Environment(undefined=DebugUndefined)
|
||||
|
||||
|
|
@ -21,9 +22,21 @@ Python uses UTF-8 encoding, which each character is encoded as one byte. So here
|
|||
# TODO: doc! Who will read this noodles lol?
|
||||
# TODO: check cases when UTF-16 character can be more that 2 bytes
|
||||
class RLStringHelper:
|
||||
__slots__ = ("string", "templates", "replaces", "quote_html_type", "quote_replaces", "_default_bang_char")
|
||||
__slots__ = (
|
||||
"string",
|
||||
"templates",
|
||||
"replaces",
|
||||
"quote_html_type",
|
||||
"quote_replaces",
|
||||
"_default_bang_char",
|
||||
)
|
||||
|
||||
def __init__(self, string: str, quote_html_type: list[str] = ["full"], _default_bang_char: str = "R"):
|
||||
def __init__(
|
||||
self,
|
||||
string: str,
|
||||
quote_html_type: list[str] = ["full"],
|
||||
_default_bang_char: str = "R",
|
||||
):
|
||||
self.string: str = quote_symbol(string)
|
||||
self.templates = []
|
||||
self.quote_replaces = []
|
||||
|
|
@ -31,7 +44,6 @@ class RLStringHelper:
|
|||
self.quote_html_type = quote_html_type
|
||||
self._default_bang_char = _default_bang_char
|
||||
|
||||
@trace
|
||||
def pre_utf_16_bang(self, string: str, string_pos_matrix: list):
|
||||
utf_16_bang_list = []
|
||||
string_len_utf_16 = len(string.encode("utf-16-le")) // 2
|
||||
|
|
@ -47,7 +59,9 @@ class RLStringHelper:
|
|||
if char_len == 2:
|
||||
char_len_dif = char_len - 1
|
||||
char_present = self._default_bang_char * char_len_dif
|
||||
string, string_pos_matrix = self._paste_char(string, string_pos_matrix, new_i + 1, char_present)
|
||||
string, string_pos_matrix = self._paste_char(
|
||||
string, string_pos_matrix, new_i + 1, char_present
|
||||
)
|
||||
i += 1
|
||||
utf_16_bang_list.append((i, char_len_dif, i))
|
||||
i += 1
|
||||
|
|
@ -62,54 +76,70 @@ class RLStringHelper:
|
|||
string.insert(pos, char)
|
||||
return string, string_pos_matrix
|
||||
|
||||
def _delete_char(self, string: str, string_pos_matrix: list, pos: int, char_len: int, old_pos: int):
|
||||
def _delete_char(
|
||||
self,
|
||||
string: str,
|
||||
string_pos_matrix: list,
|
||||
pos: int,
|
||||
char_len: int,
|
||||
old_pos: int,
|
||||
):
|
||||
string.pop(pos)
|
||||
string_pos_matrix.pop(old_pos)
|
||||
for matrix_i in range(pos, len(string_pos_matrix)):
|
||||
if isinstance(string_pos_matrix[matrix_i], int):
|
||||
string_pos_matrix[matrix_i] -= char_len
|
||||
elif isinstance(string_pos_matrix[matrix_i], tuple):
|
||||
string_pos_matrix[matrix_i] = (string_pos_matrix[matrix_i][0] - char_len, string_pos_matrix[matrix_i][1] - char_len)
|
||||
string_pos_matrix[matrix_i] = (
|
||||
string_pos_matrix[matrix_i][0] - char_len,
|
||||
string_pos_matrix[matrix_i][1] - char_len,
|
||||
)
|
||||
return string, string_pos_matrix
|
||||
|
||||
@trace
|
||||
def post_utf_16_bang(self, string: StringAssignmentMixin, string_pos_matrix: list, utf_16_bang_list: list):
|
||||
def post_utf_16_bang(
|
||||
self,
|
||||
string: StringAssignmentMixin,
|
||||
string_pos_matrix: list,
|
||||
utf_16_bang_list: list,
|
||||
):
|
||||
string = StringAssignmentMixin(str(string))
|
||||
post_transbang = 0
|
||||
for bang_pos, char_len, old_pos in utf_16_bang_list:
|
||||
string, string_pos_matrix = self._delete_char(string, string_pos_matrix, bang_pos - post_transbang, char_len, old_pos - post_transbang)
|
||||
string, string_pos_matrix = self._delete_char(
|
||||
string,
|
||||
string_pos_matrix,
|
||||
bang_pos - post_transbang,
|
||||
char_len,
|
||||
old_pos - post_transbang,
|
||||
)
|
||||
post_transbang += char_len
|
||||
return string, string_pos_matrix
|
||||
|
||||
@trace
|
||||
def set_template(self, start: int, end: int, template: str):
|
||||
if not isinstance(template, Template):
|
||||
template = jinja_env.from_string(template)
|
||||
self.templates.append(((start, end), template))
|
||||
|
||||
@trace
|
||||
def set_replace(self, start: int, end: int, replace_with: str):
|
||||
self.replaces.append(((start, end), replace_with))
|
||||
|
||||
@trace
|
||||
def _render_templates(self, string: str, string_pos_matrix: list, utf_16_bang_list: list):
|
||||
def _render_templates(
|
||||
self, string: str, string_pos_matrix: list, utf_16_bang_list: list
|
||||
):
|
||||
if not self.templates:
|
||||
return string, string_pos_matrix, utf_16_bang_list
|
||||
|
||||
templates = reversed(self.templates)
|
||||
updated_text = string
|
||||
|
||||
@trace
|
||||
def _get_prefix_len(template_raw: Template, inner_char: str = "{"):
|
||||
template = template_raw.render()
|
||||
return template.find(inner_char)
|
||||
|
||||
@trace
|
||||
def _get_suffix_len(template_raw: Template, outer_char: str = "}"):
|
||||
template = template_raw.render()
|
||||
return len(template) - template.rfind(outer_char) - 1
|
||||
|
||||
@trace
|
||||
def update_nested_positions(start, end, prefix_len, suffix_len):
|
||||
for i in range(end, len(string_pos_matrix)):
|
||||
string_pos_matrix[i] += suffix_len + prefix_len
|
||||
|
|
@ -118,9 +148,17 @@ class RLStringHelper:
|
|||
for n in range(len(utf_16_bang_list)):
|
||||
utf_16_bang = utf_16_bang_list[n]
|
||||
if utf_16_bang[2] > end:
|
||||
utf_16_bang_list[n] = (utf_16_bang[0] + prefix_len + suffix_len, utf_16_bang[1], utf_16_bang[2])
|
||||
utf_16_bang_list[n] = (
|
||||
utf_16_bang[0] + prefix_len + suffix_len,
|
||||
utf_16_bang[1],
|
||||
utf_16_bang[2],
|
||||
)
|
||||
elif utf_16_bang[2] > start:
|
||||
utf_16_bang_list[n] = (utf_16_bang[0] + prefix_len, utf_16_bang[1], utf_16_bang[2])
|
||||
utf_16_bang_list[n] = (
|
||||
utf_16_bang[0] + prefix_len,
|
||||
utf_16_bang[1],
|
||||
utf_16_bang[2],
|
||||
)
|
||||
|
||||
for (start, end), template in templates:
|
||||
if start >= len(string_pos_matrix) or end - 1 >= len(string_pos_matrix):
|
||||
|
|
@ -128,13 +166,23 @@ class RLStringHelper:
|
|||
if start == end:
|
||||
continue
|
||||
|
||||
new_start, new_end = string_pos_matrix[start], string_pos_matrix[end - 1] + 1
|
||||
new_start, new_end = (
|
||||
string_pos_matrix[start],
|
||||
string_pos_matrix[end - 1] + 1,
|
||||
)
|
||||
if new_end < new_start:
|
||||
continue
|
||||
|
||||
context_text = template.render(text=updated_text[new_start:new_end])
|
||||
updated_text_template = jinja_env.from_string("{{ updated_text[:new_start] }}{{ context_text }}{{updated_text[new_end:]}}")
|
||||
updated_text = updated_text_template.render(updated_text=updated_text, context_text=context_text, new_start=new_start, new_end=new_end)
|
||||
updated_text_template = jinja_env.from_string(
|
||||
"{{ updated_text[:new_start] }}{{ context_text }}{{updated_text[new_end:]}}"
|
||||
)
|
||||
updated_text = updated_text_template.render(
|
||||
updated_text=updated_text,
|
||||
context_text=context_text,
|
||||
new_start=new_start,
|
||||
new_end=new_end,
|
||||
)
|
||||
|
||||
prefix_len = _get_prefix_len(template)
|
||||
suffix_len = _get_suffix_len(template)
|
||||
|
|
@ -142,16 +190,21 @@ class RLStringHelper:
|
|||
|
||||
return updated_text, string_pos_matrix, utf_16_bang_list
|
||||
|
||||
@trace
|
||||
def _render_replaces(self, string: StringAssignmentMixin, string_pos_matrix: list, utf_16_bang_list: list):
|
||||
def _render_replaces(
|
||||
self,
|
||||
string: StringAssignmentMixin,
|
||||
string_pos_matrix: list,
|
||||
utf_16_bang_list: list,
|
||||
):
|
||||
if not self.replaces and not self.quote_replaces:
|
||||
return string, string_pos_matrix, utf_16_bang_list
|
||||
|
||||
string = StringAssignmentMixin(str(string))
|
||||
replaces = self.replaces + self.quote_replaces
|
||||
|
||||
@trace
|
||||
def update_positions(start: int, end: int, replace_len: int, new_start: int, new_end: int):
|
||||
def update_positions(
|
||||
start: int, end: int, replace_len: int, new_start: int, new_end: int
|
||||
):
|
||||
pos_len_diff = replace_len - (end - start)
|
||||
for pos_index in range(end, len(string_pos_matrix)):
|
||||
if isinstance(string_pos_matrix[pos_index], int):
|
||||
|
|
@ -176,7 +229,11 @@ class RLStringHelper:
|
|||
for n in range(len(utf_16_bang_list)):
|
||||
utf_16_bang = utf_16_bang_list[n]
|
||||
if utf_16_bang[0] > end:
|
||||
utf_16_bang_list[n] = (utf_16_bang[0] + pos_len_diff, utf_16_bang[1], utf_16_bang[2])
|
||||
utf_16_bang_list[n] = (
|
||||
utf_16_bang[0] + pos_len_diff,
|
||||
utf_16_bang[1],
|
||||
utf_16_bang[2],
|
||||
)
|
||||
|
||||
for (start, end), replace_with in replaces:
|
||||
new_start, new_end = string_pos_matrix[start], string_pos_matrix[end - 1]
|
||||
|
|
@ -184,7 +241,9 @@ class RLStringHelper:
|
|||
new_end += 1
|
||||
|
||||
if isinstance(new_start, tuple) or isinstance(new_end, tuple):
|
||||
new_start = min(new_start) if isinstance(new_start, tuple) else new_start
|
||||
new_start = (
|
||||
min(new_start) if isinstance(new_start, tuple) else new_start
|
||||
)
|
||||
new_end = max(new_end) if isinstance(new_end, tuple) else new_end
|
||||
|
||||
string[new_start:new_end] = replace_with
|
||||
|
|
@ -192,22 +251,31 @@ class RLStringHelper:
|
|||
|
||||
return string, string_pos_matrix, utf_16_bang_list
|
||||
|
||||
@trace
|
||||
def __str__(self):
|
||||
string = StringAssignmentMixin(self.string)
|
||||
|
||||
string_pos_matrix = list(range(len(string)))
|
||||
updated_text, string_pos_matrix, utf_16_bang_list = self.pre_utf_16_bang(string, string_pos_matrix)
|
||||
updated_text, string_pos_matrix, utf_16_bang_list = self.pre_utf_16_bang(
|
||||
string, string_pos_matrix
|
||||
)
|
||||
|
||||
if self.quote_html_type:
|
||||
self.quote_replaces = list(quote_html(str(updated_text), self.quote_html_type))
|
||||
self.quote_replaces = list(
|
||||
quote_html(str(updated_text), self.quote_html_type)
|
||||
)
|
||||
|
||||
if not self.templates and not self.replaces and not self.quote_replaces:
|
||||
return self.string
|
||||
|
||||
updated_text, string_pos_matrix, utf_16_bang_list = self._render_templates(updated_text, string_pos_matrix, utf_16_bang_list)
|
||||
updated_text, string_pos_matrix, utf_16_bang_list = self._render_replaces(updated_text, string_pos_matrix, utf_16_bang_list)
|
||||
updated_text, string_pos_matrix = self.post_utf_16_bang(updated_text, string_pos_matrix, utf_16_bang_list)
|
||||
updated_text, string_pos_matrix, utf_16_bang_list = self._render_templates(
|
||||
updated_text, string_pos_matrix, utf_16_bang_list
|
||||
)
|
||||
updated_text, string_pos_matrix, utf_16_bang_list = self._render_replaces(
|
||||
updated_text, string_pos_matrix, utf_16_bang_list
|
||||
)
|
||||
updated_text, string_pos_matrix = self.post_utf_16_bang(
|
||||
updated_text, string_pos_matrix, utf_16_bang_list
|
||||
)
|
||||
return str(updated_text)
|
||||
|
||||
def get_text(self):
|
||||
|
|
@ -281,10 +349,20 @@ def parse_markups(markups: list[str]):
|
|||
for markup in markups:
|
||||
if markup["type"] == "A":
|
||||
if markup["anchorType"] == "LINK":
|
||||
template = jinja_env.from_string('<a style="text-decoration: underline;" rel="{{rel}}" title="{{title}}" href="{{href}}" target="_blank">{{text}}</a>')
|
||||
template = template.render(raw_render(rel=markup.get("rel", ""), title=markup.get("title", ""), href=markup["href"]))
|
||||
template = jinja_env.from_string(
|
||||
'<a style="text-decoration: underline;" rel="{{rel}}" title="{{title}}" href="{{href}}" target="_blank">{{text}}</a>'
|
||||
)
|
||||
template = template.render(
|
||||
raw_render(
|
||||
rel=markup.get("rel", ""),
|
||||
title=markup.get("title", ""),
|
||||
href=markup["href"],
|
||||
)
|
||||
)
|
||||
elif markup["anchorType"] == "USER":
|
||||
template = jinja_env.from_string('<a style="text-decoration: underline;" href="https://medium.com/u/{{userId}}">{{text}}</a>')
|
||||
template = jinja_env.from_string(
|
||||
'<a style="text-decoration: underline;" href="https://medium.com/u/{{userId}}">{{text}}</a>'
|
||||
)
|
||||
template = template.render(userId=markup["userId"])
|
||||
else:
|
||||
continue
|
||||
|
|
@ -293,7 +371,9 @@ def parse_markups(markups: list[str]):
|
|||
elif markup["type"] == "EM":
|
||||
template = "<em>{{text}}</em>"
|
||||
elif markup["type"] == "CODE":
|
||||
template = "<code class='p-1.5 bg-gray-300 dark:bg-gray-600'>{{text}}</code>"
|
||||
template = (
|
||||
"<code class='p-1.5 bg-gray-300 dark:bg-gray-600'>{{text}}</code>"
|
||||
)
|
||||
else:
|
||||
continue
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# pip install nuitka==2.0.1
|
||||
# sudo apt install patchelf ccache -y
|
||||
# sudo /usr/sbin/update-ccache-symlinks
|
||||
# export PATH="/usr/lib/ccache:$PATH"
|
||||
|
||||
python3 -m nuitka --standalone --nofollow-import-to=pytest --python-flag=nosite,-O,isolated --plugin-enable=anti-bloat,implicit-imports,data-files,pylint-warnings --warn-implicit-exceptions --warn-unusual-code --prefer-source-code --include-package=uvicorn.workers --include-package=sentry_sdk.integrations server
|
||||
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
import asyncio
|
||||
import aiohttp
|
||||
import os
|
||||
|
||||
from aiogram import Bot
|
||||
from loguru import logger
|
||||
|
||||
BOT_TOKEN = os.getenv("BOT_TOKEN")
|
||||
|
||||
if not BOT_TOKEN:
|
||||
raise ValueError("No bot token!")
|
||||
|
||||
bot = Bot(BOT_TOKEN)
|
||||
|
||||
ADMIN_CHAT_ID = "1621425349"
|
||||
SLEEP_TIME = 15 * 60
|
||||
|
||||
|
||||
async def main():
|
||||
while True:
|
||||
logger.debug("Checking health of freedium.cfd")
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get("https://freedium.cfd", timeout=3) as response:
|
||||
response_status = response.status
|
||||
except Exception as ex:
|
||||
logger.exception(ex)
|
||||
response_status = "ERROR"
|
||||
finally:
|
||||
if response_status != 200:
|
||||
await bot.send_message(ADMIN_CHAT_ID, "EMERGENCY! SITE IS DOWN!!!")
|
||||
|
||||
logger.debug("Sleeping ...")
|
||||
await asyncio.sleep(SLEEP_TIME)
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
ruff check ./ --fix
|
||||
black .
|
||||
|
|
@ -1,148 +0,0 @@
|
|||
|
||||
handle_path /site.webmanifest {
|
||||
root * ./static/site.webmanifest
|
||||
file_server
|
||||
}
|
||||
|
||||
handle_path /favicon-32x32.png {
|
||||
root * ./static/favicon-32x32.png
|
||||
file_server
|
||||
}
|
||||
|
||||
handle_path /robots.txt {
|
||||
root * ./static/robots.txt
|
||||
file_server
|
||||
}
|
||||
|
||||
handle_path /ads.txt {
|
||||
root * ./static/ads.txt
|
||||
file_server
|
||||
}
|
||||
|
||||
handle_path /humans.txt {
|
||||
root * ./static/humans.txt
|
||||
file_server
|
||||
}
|
||||
|
||||
handle_path /mstile-150x150.png {
|
||||
root * ./static/mstile-150x150.png
|
||||
file_server
|
||||
}
|
||||
|
||||
handle_path /mstile-310x310.png {
|
||||
root * ./static/mstile-310x310.png
|
||||
file_server
|
||||
}
|
||||
|
||||
handle_path /sitemap.xml {
|
||||
root * ./static/sitemap.xml
|
||||
file_server
|
||||
}
|
||||
|
||||
handle_path /99860281ef1143d5a5558ad9a21a470d.txt {
|
||||
root * ./static/99860281ef1143d5a5558ad9a21a470d.txt
|
||||
file_server
|
||||
}
|
||||
|
||||
handle_path /mstile-70x70.png {
|
||||
root * ./static/mstile-70x70.png
|
||||
file_server
|
||||
}
|
||||
|
||||
handle_path /android-chrome-192x192.png {
|
||||
root * ./static/android-chrome-192x192.png
|
||||
file_server
|
||||
}
|
||||
|
||||
handle_path /mstile-310x150.png {
|
||||
root * ./static/mstile-310x150.png
|
||||
file_server
|
||||
}
|
||||
|
||||
handle_path /safari-pinned-tab.svg {
|
||||
root * ./static/safari-pinned-tab.svg
|
||||
file_server
|
||||
}
|
||||
|
||||
handle_path /android-chrome-512x512.png {
|
||||
root * ./static/android-chrome-512x512.png
|
||||
file_server
|
||||
}
|
||||
|
||||
handle_path /favicon-16x16.png {
|
||||
root * ./static/favicon-16x16.png
|
||||
file_server
|
||||
}
|
||||
|
||||
handle_path /favicon.ico {
|
||||
root * ./static/favicon.ico
|
||||
file_server
|
||||
}
|
||||
|
||||
handle_path /browserconfig.xml {
|
||||
root * ./static/browserconfig.xml
|
||||
file_server
|
||||
}
|
||||
|
||||
handle_path /mstile-144x144.png {
|
||||
root * ./static/mstile-144x144.png
|
||||
file_server
|
||||
}
|
||||
|
||||
handle_path /security.txt {
|
||||
root * ./static/security.txt
|
||||
file_server
|
||||
}
|
||||
|
||||
handle_path /apple-touch-icon.png {
|
||||
root * ./static/apple-touch-icon.png
|
||||
file_server
|
||||
}
|
||||
|
||||
handle_path /onboarding/* {
|
||||
respond "Access denied" 403
|
||||
}
|
||||
|
||||
handle_path /wp-* {
|
||||
respond "Access denied" 403
|
||||
}
|
||||
|
||||
handle_path /.env {
|
||||
respond "Access denied" 403
|
||||
}
|
||||
|
||||
handle_path /api* {
|
||||
respond "Access denied" 403
|
||||
}
|
||||
|
||||
handle_path /apple-touch-icon-precomposed.png {
|
||||
respond "Access denied" 403
|
||||
}
|
||||
|
||||
handle_path /rss.xml {
|
||||
respond "Access denied" 403
|
||||
}
|
||||
|
||||
handle_path /.git/* {
|
||||
respond "Access denied" 403
|
||||
}
|
||||
|
||||
handle_path /apple-touch-icon-120x120.png {
|
||||
respond "Access denied" 403
|
||||
}
|
||||
|
||||
handle_path /apple-touch-icon-120x120-precomposed.png {
|
||||
respond "Access denied" 403
|
||||
}
|
||||
|
||||
handle_path /apple-touch-icon-152x152.png {
|
||||
respond "Access denied" 403
|
||||
}
|
||||
|
||||
handle_path /apple-touch-icon-152x152-precomposed.png {
|
||||
respond "Access denied" 403
|
||||
}
|
||||
|
||||
handle_path /.well-known/* {
|
||||
respond "Access denied" 403
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
import http.server
|
||||
import socketserver
|
||||
|
||||
class RedirectHandler(http.server.SimpleHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
self.send_response(301) # 301 indicates a permanent redirect
|
||||
self.send_header('Location', 'https://freedium.cfd')
|
||||
self.end_headers()
|
||||
|
||||
port = 80
|
||||
server = socketserver.TCPServer(('', port), RedirectHandler)
|
||||
print(f"Redirect server running at http://localhost:{port}")
|
||||
server.serve_forever()
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
#!/bin/bash
|
||||
arch=$(lscpu | grep Architecture | awk {'print $2'})
|
||||
|
||||
sudo setcap cap_net_bind_service=+ep $(pwd)/bin/${arch}/caddy
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
import os
|
||||
import sys
|
||||
import time
|
||||
from loguru import logger
|
||||
import docker
|
||||
import requests
|
||||
|
||||
logger.remove()
|
||||
logger.add(sys.stdout, level="DEBUG")
|
||||
|
||||
error_msg = "[Cloudflare proxy] Freedium proxy is not working, restarting containers..."
|
||||
|
||||
USER_NAME = os.getenv("USER_NAME")
|
||||
USER_PASSWORD = os.getenv("USER_PASSWORD")
|
||||
PROXY_HOST = os.getenv("PROXY_HOST", "localhost")
|
||||
PROXY_PORT = os.getenv("PROXY_PORT", "9681") # 1080
|
||||
|
||||
_AUTH_DATA = [USER_NAME, USER_PASSWORD]
|
||||
AUTH_FORMATED = ":".join(filter(bool, _AUTH_DATA))
|
||||
|
||||
if not PROXY_HOST or not PROXY_PORT:
|
||||
raise ValueError("PROXY_HOST and PROXY POER must be set")
|
||||
|
||||
containers_to_restart = ["dante_1", "wgcf1"]
|
||||
|
||||
|
||||
def restart_containers():
|
||||
# client = docker.from_env()
|
||||
client = docker.DockerClient(base_url="unix://var/run/docker.sock")
|
||||
|
||||
for container_name in containers_to_restart:
|
||||
try:
|
||||
container = client.containers.get(container_name)
|
||||
logger.info(f"Restarting {container_name}...")
|
||||
container.restart()
|
||||
logger.info(f"{container_name} restarted successfully.")
|
||||
except docker.errors.NotFound:
|
||||
logger.error(f"Container {container_name} not found.")
|
||||
except Exception as e:
|
||||
logger.error(f"Error restarting {container_name}: {str(e)}")
|
||||
|
||||
|
||||
def send_msg(text: str, max_retries: int = 3) -> bool:
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
tg_token = os.getenv("TG_TOKEN")
|
||||
chat_id = os.getenv("TG_CHAT_ID")
|
||||
url_req = "https://api.telegram.org/bot" + tg_token + "/sendMessage" + "?chat_id=" + chat_id + "&text=" + text
|
||||
results = requests.get(url_req)
|
||||
logger.debug(results.json())
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
logger.error(f"Error sending message: {str(e)}")
|
||||
if attempt < max_retries - 1:
|
||||
logger.info(f"Retrying in 5 seconds...")
|
||||
time.sleep(5)
|
||||
else:
|
||||
logger.info("Message sent successfully.")
|
||||
return True
|
||||
|
||||
logger.error("All retry attempts failed. Message not sent.")
|
||||
return False
|
||||
|
||||
|
||||
def check_proxy(max_retries=3, retry_delay=5) -> bool:
|
||||
auth_str = f"{AUTH_FORMATED}@" if AUTH_FORMATED else ""
|
||||
|
||||
proxy_url = f"socks5://{auth_str}{PROXY_HOST}:{PROXY_PORT}"
|
||||
proxy_test_url = "http://ipinfo.io"
|
||||
logger.info(f"Checking proxy: {proxy_url}")
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
resp = requests.get(
|
||||
proxy_test_url,
|
||||
proxies=dict(
|
||||
http=proxy_url,
|
||||
https=proxy_url,
|
||||
),
|
||||
timeout=5,
|
||||
)
|
||||
logger.info(f"Proxy is working: {resp.status_code}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
logger.error(f"Error checking proxy (attempt {attempt + 1}/{max_retries}): {str(e)}")
|
||||
if attempt < max_retries - 1:
|
||||
logger.info(f"Retrying in {retry_delay} seconds...")
|
||||
time.sleep(retry_delay)
|
||||
|
||||
logger.error("All retry attempts failed. Proxy is not working.")
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
while True:
|
||||
time.sleep(75)
|
||||
# time.sleep(10)
|
||||
try:
|
||||
logger.info("Checking proxy...")
|
||||
if not check_proxy():
|
||||
logger.info(error_msg)
|
||||
send_msg(error_msg)
|
||||
restart_containers()
|
||||
except Exception as e:
|
||||
logger.error(f"Error checking proxy: {str(e)}")
|
||||
|
|
@ -1 +0,0 @@
|
|||
from .scheduler import scheduler
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||
|
||||
scheduler = AsyncIOScheduler()
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
<div class="md:max-w-6xl bg-white dark:bg-gray-600 w-full shadow-md rounded-md p-8 mt-8">
|
||||
<form id="medium-link-form" class="flex items-center border rounded-md border-gray-300 px-4 py-2">
|
||||
<svg class="h-5 w-5 text-gray-500 dark:text-gray-100 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
|
||||
</svg>
|
||||
<input id="medium-link-input"
|
||||
type="text"
|
||||
placeholder="Enter Medium post link"
|
||||
class="w-full focus:outline-none text-green-500 dark:bg-gray-600 border-gray-300">
|
||||
<button type="submit" class="ml-2 bg-green-500 text-white px-4 py-2 rounded-md hover:bg-green-600 focus:outline-none">Go</button>
|
||||
</form>
|
||||
</div>
|
||||
<script>
|
||||
document.getElementById('medium-link-form').addEventListener('submit', function (event) {
|
||||
event.preventDefault();
|
||||
const mediumLinkInput = document.getElementById('medium-link-input');
|
||||
window.location.href = `${window.location.origin}/${mediumLinkInput.value}`;
|
||||
});
|
||||
</script>
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
import asyncio
|
||||
import time
|
||||
from functools import wraps
|
||||
|
||||
from loguru import logger
|
||||
|
||||
|
||||
def trace(func):
|
||||
if asyncio.iscoroutinefunction(func):
|
||||
logger.trace(f"{func.__name__!r} function is a coroutine")
|
||||
|
||||
@wraps(func)
|
||||
async def wrapper(*args, **kwargs):
|
||||
start_ts = time.time()
|
||||
logger.trace(f"Calling {func.__name__}() with {args}, {kwargs}")
|
||||
original_result = await func(*args, **kwargs)
|
||||
logger.trace(f"Result: {original_result}")
|
||||
logger.trace(f"Result type: {type(original_result)}")
|
||||
duration_ts = time.time() - start_ts
|
||||
result = f"{original_result[:42]}..." if type(original_result).__name__ in ["str", "bytes"] else original_result
|
||||
logger.trace(f"{func.__name__!r}() returned {result!r} in {duration_ts:.2} seconds")
|
||||
return original_result
|
||||
|
||||
else:
|
||||
logger.trace(f"{func.__name__!r} is not a coroutine")
|
||||
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
start_ts = time.time()
|
||||
logger.trace(f"Calling {func.__name__}() with {args}, {kwargs}")
|
||||
original_result = func(*args, **kwargs)
|
||||
logger.trace(f"Result: {original_result}")
|
||||
logger.trace(f"Result type: {type(original_result)}")
|
||||
duration_ts = time.time() - start_ts
|
||||
result = f"{original_result[:42]}..." if type(original_result).__name__ in ["str", "bytes"] else original_result
|
||||
logger.trace(f"{func.__name__!r}() returned {result!r} in {duration_ts:.2} seconds")
|
||||
return original_result
|
||||
|
||||
return wrapper
|
||||
48794
static/sitemap.xml
433
poetry.lock → web/poetry.lock
generated
|
|
@ -1,4 +1,4 @@
|
|||
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "annotated-types"
|
||||
|
|
@ -32,32 +32,48 @@ test = ["anyio[trio]", "coverage[toml] (>=7)", "hypothesis (>=4.0)", "psutil (>=
|
|||
trio = ["trio (>=0.22)"]
|
||||
|
||||
[[package]]
|
||||
name = "apscheduler"
|
||||
version = "3.10.4"
|
||||
description = "In-process task scheduler with Cron-like capabilities"
|
||||
name = "black"
|
||||
version = "24.8.0"
|
||||
description = "The uncompromising code formatter."
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "APScheduler-3.10.4-py3-none-any.whl", hash = "sha256:fb91e8a768632a4756a585f79ec834e0e27aad5860bac7eaa523d9ccefd87661"},
|
||||
{file = "APScheduler-3.10.4.tar.gz", hash = "sha256:e6df071b27d9be898e486bc7940a7be50b4af2e9da7c08f0744a96d4bd4cef4a"},
|
||||
{file = "black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6"},
|
||||
{file = "black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb"},
|
||||
{file = "black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42"},
|
||||
{file = "black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a"},
|
||||
{file = "black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1"},
|
||||
{file = "black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af"},
|
||||
{file = "black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4"},
|
||||
{file = "black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af"},
|
||||
{file = "black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368"},
|
||||
{file = "black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed"},
|
||||
{file = "black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018"},
|
||||
{file = "black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2"},
|
||||
{file = "black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd"},
|
||||
{file = "black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2"},
|
||||
{file = "black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e"},
|
||||
{file = "black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920"},
|
||||
{file = "black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c"},
|
||||
{file = "black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e"},
|
||||
{file = "black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47"},
|
||||
{file = "black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb"},
|
||||
{file = "black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed"},
|
||||
{file = "black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pytz = "*"
|
||||
six = ">=1.4.0"
|
||||
tzlocal = ">=2.0,<3.dev0 || >=4.dev0"
|
||||
click = ">=8.0.0"
|
||||
mypy-extensions = ">=0.4.3"
|
||||
packaging = ">=22.0"
|
||||
pathspec = ">=0.9.0"
|
||||
platformdirs = ">=2"
|
||||
|
||||
[package.extras]
|
||||
doc = ["sphinx", "sphinx-rtd-theme"]
|
||||
gevent = ["gevent"]
|
||||
mongodb = ["pymongo (>=3.0)"]
|
||||
redis = ["redis (>=3.0)"]
|
||||
rethinkdb = ["rethinkdb (>=2.4.0)"]
|
||||
sqlalchemy = ["sqlalchemy (>=1.4)"]
|
||||
testing = ["pytest", "pytest-asyncio", "pytest-cov", "pytest-tornado5"]
|
||||
tornado = ["tornado (>=4.3)"]
|
||||
twisted = ["twisted"]
|
||||
zookeeper = ["kazoo"]
|
||||
colorama = ["colorama (>=0.4.3)"]
|
||||
d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"]
|
||||
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
|
||||
uvloop = ["uvloop (>=0.15.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
|
|
@ -95,6 +111,55 @@ files = [
|
|||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cssbeautifier"
|
||||
version = "1.15.1"
|
||||
description = "CSS unobfuscator and beautifier."
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "cssbeautifier-1.15.1.tar.gz", hash = "sha256:9f7064362aedd559c55eeecf6b6bed65e05f33488dcbe39044f0403c26e1c006"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
editorconfig = ">=0.12.2"
|
||||
jsbeautifier = "*"
|
||||
six = ">=1.13.0"
|
||||
|
||||
[[package]]
|
||||
name = "djlint"
|
||||
version = "1.35.2"
|
||||
description = "HTML Template Linter and Formatter"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8"
|
||||
files = [
|
||||
{file = "djlint-1.35.2-py3-none-any.whl", hash = "sha256:4ba995bad378f2afa77c8ea56ba1c14429d9ff26a18e8ae23bc71eedb9152243"},
|
||||
{file = "djlint-1.35.2.tar.gz", hash = "sha256:318de9d4b9b0061a111f8f5164ecbacd8215f449dd4bd5a76d2a691c815ee103"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
click = ">=8.0.1"
|
||||
colorama = ">=0.4.4"
|
||||
cssbeautifier = ">=1.14.4"
|
||||
html-tag-names = ">=0.1.2"
|
||||
html-void-elements = ">=0.1.0"
|
||||
jsbeautifier = ">=1.14.4"
|
||||
json5 = ">=0.9.11"
|
||||
pathspec = ">=0.12.0"
|
||||
PyYAML = ">=6.0"
|
||||
regex = ">=2023"
|
||||
tqdm = ">=4.62.2"
|
||||
|
||||
[[package]]
|
||||
name = "editorconfig"
|
||||
version = "0.12.4"
|
||||
description = "EditorConfig File Locator and Interpreter for Python"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "EditorConfig-0.12.4.tar.gz", hash = "sha256:24857fa1793917dd9ccf0c7810a07e05404ce9b823521c7dce22a4fb5d125f80"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastapi"
|
||||
version = "0.108.0"
|
||||
|
|
@ -248,6 +313,28 @@ files = [
|
|||
{file = "hiredis-3.0.0.tar.gz", hash = "sha256:fed8581ae26345dea1f1e0d1a96e05041a727a45e7d8d459164583e23c6ac441"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "html-tag-names"
|
||||
version = "0.1.2"
|
||||
description = "List of known HTML tag names"
|
||||
optional = false
|
||||
python-versions = ">=3.7,<4.0"
|
||||
files = [
|
||||
{file = "html-tag-names-0.1.2.tar.gz", hash = "sha256:04924aca48770f36b5a41c27e4d917062507be05118acb0ba869c97389084297"},
|
||||
{file = "html_tag_names-0.1.2-py3-none-any.whl", hash = "sha256:eeb69ef21078486b615241f0393a72b41352c5219ee648e7c61f5632d26f0420"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "html-void-elements"
|
||||
version = "0.1.0"
|
||||
description = "List of HTML void tag names."
|
||||
optional = false
|
||||
python-versions = ">=3.7,<4.0"
|
||||
files = [
|
||||
{file = "html-void-elements-0.1.0.tar.gz", hash = "sha256:931b88f84cd606fee0b582c28fcd00e41d7149421fb673e1e1abd2f0c4f231f0"},
|
||||
{file = "html_void_elements-0.1.0-py3-none-any.whl", hash = "sha256:784cf39db03cdeb017320d9301009f8f3480f9d7b254d0974272e80e0cb5e0d2"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "html5lib"
|
||||
version = "1.1"
|
||||
|
|
@ -297,6 +384,31 @@ MarkupSafe = ">=2.0"
|
|||
[package.extras]
|
||||
i18n = ["Babel (>=2.7)"]
|
||||
|
||||
[[package]]
|
||||
name = "jsbeautifier"
|
||||
version = "1.15.1"
|
||||
description = "JavaScript unobfuscator and beautifier."
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "jsbeautifier-1.15.1.tar.gz", hash = "sha256:ebd733b560704c602d744eafc839db60a1ee9326e30a2a80c4adb8718adc1b24"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
editorconfig = ">=0.12.2"
|
||||
six = ">=1.13.0"
|
||||
|
||||
[[package]]
|
||||
name = "json5"
|
||||
version = "0.9.25"
|
||||
description = "A Python implementation of the JSON5 data format."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "json5-0.9.25-py3-none-any.whl", hash = "sha256:34ed7d834b1341a86987ed52f3f76cd8ee184394906b6e22a1e0deb9ab294e8f"},
|
||||
{file = "json5-0.9.25.tar.gz", hash = "sha256:548e41b9be043f9426776f05df8635a00fe06104ea51ed24b67f908856e151ae"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "loguru"
|
||||
version = "0.6.0"
|
||||
|
|
@ -384,6 +496,17 @@ files = [
|
|||
{file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "1.0.0"
|
||||
description = "Type system extensions for programs checked with the mypy type checker."
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
{file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
|
||||
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "24.1"
|
||||
|
|
@ -395,6 +518,17 @@ files = [
|
|||
{file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathspec"
|
||||
version = "0.12.1"
|
||||
description = "Utility library for gitignore style pattern matching of file paths."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
|
||||
{file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pickledb"
|
||||
version = "0.9.2"
|
||||
|
|
@ -405,6 +539,22 @@ files = [
|
|||
{file = "pickleDB-0.9.2.tar.gz", hash = "sha256:ec6973e65d7d112849e78ce522840aa908efb2523470bb8ce5c7942310192240"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "4.3.6"
|
||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"},
|
||||
{file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"]
|
||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"]
|
||||
type = ["mypy (>=1.11.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.8.2"
|
||||
|
|
@ -529,14 +679,65 @@ files = [
|
|||
typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
|
||||
|
||||
[[package]]
|
||||
name = "pytz"
|
||||
version = "2024.1"
|
||||
description = "World timezone definitions, modern and historical"
|
||||
name = "pyyaml"
|
||||
version = "6.0.2"
|
||||
description = "YAML parser and emitter for Python"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"},
|
||||
{file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"},
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"},
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"},
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"},
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"},
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"},
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"},
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"},
|
||||
{file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"},
|
||||
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"},
|
||||
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"},
|
||||
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"},
|
||||
{file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"},
|
||||
{file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"},
|
||||
{file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"},
|
||||
{file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -557,6 +758,136 @@ hiredis = {version = ">=1.0.0", optional = true, markers = "extra == \"hiredis\"
|
|||
hiredis = ["hiredis (>=1.0.0)"]
|
||||
ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "2024.9.11"
|
||||
description = "Alternative regular expression module, to replace re."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "regex-2024.9.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1494fa8725c285a81d01dc8c06b55287a1ee5e0e382d8413adc0a9197aac6408"},
|
||||
{file = "regex-2024.9.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0e12c481ad92d129c78f13a2a3662317e46ee7ef96c94fd332e1c29131875b7d"},
|
||||
{file = "regex-2024.9.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:16e13a7929791ac1216afde26f712802e3df7bf0360b32e4914dca3ab8baeea5"},
|
||||
{file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46989629904bad940bbec2106528140a218b4a36bb3042d8406980be1941429c"},
|
||||
{file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a906ed5e47a0ce5f04b2c981af1c9acf9e8696066900bf03b9d7879a6f679fc8"},
|
||||
{file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a091b0550b3b0207784a7d6d0f1a00d1d1c8a11699c1a4d93db3fbefc3ad35"},
|
||||
{file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ddcd9a179c0a6fa8add279a4444015acddcd7f232a49071ae57fa6e278f1f71"},
|
||||
{file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6b41e1adc61fa347662b09398e31ad446afadff932a24807d3ceb955ed865cc8"},
|
||||
{file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ced479f601cd2f8ca1fd7b23925a7e0ad512a56d6e9476f79b8f381d9d37090a"},
|
||||
{file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:635a1d96665f84b292e401c3d62775851aedc31d4f8784117b3c68c4fcd4118d"},
|
||||
{file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c0256beda696edcf7d97ef16b2a33a8e5a875affd6fa6567b54f7c577b30a137"},
|
||||
{file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3ce4f1185db3fbde8ed8aa223fc9620f276c58de8b0d4f8cc86fd1360829edb6"},
|
||||
{file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:09d77559e80dcc9d24570da3745ab859a9cf91953062e4ab126ba9d5993688ca"},
|
||||
{file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a22ccefd4db3f12b526eccb129390942fe874a3a9fdbdd24cf55773a1faab1a"},
|
||||
{file = "regex-2024.9.11-cp310-cp310-win32.whl", hash = "sha256:f745ec09bc1b0bd15cfc73df6fa4f726dcc26bb16c23a03f9e3367d357eeedd0"},
|
||||
{file = "regex-2024.9.11-cp310-cp310-win_amd64.whl", hash = "sha256:01c2acb51f8a7d6494c8c5eafe3d8e06d76563d8a8a4643b37e9b2dd8a2ff623"},
|
||||
{file = "regex-2024.9.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2cce2449e5927a0bf084d346da6cd5eb016b2beca10d0013ab50e3c226ffc0df"},
|
||||
{file = "regex-2024.9.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b37fa423beefa44919e009745ccbf353d8c981516e807995b2bd11c2c77d268"},
|
||||
{file = "regex-2024.9.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64ce2799bd75039b480cc0360907c4fb2f50022f030bf9e7a8705b636e408fad"},
|
||||
{file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4cc92bb6db56ab0c1cbd17294e14f5e9224f0cc6521167ef388332604e92679"},
|
||||
{file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d05ac6fa06959c4172eccd99a222e1fbf17b5670c4d596cb1e5cde99600674c4"},
|
||||
{file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:040562757795eeea356394a7fb13076ad4f99d3c62ab0f8bdfb21f99a1f85664"},
|
||||
{file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6113c008a7780792efc80f9dfe10ba0cd043cbf8dc9a76ef757850f51b4edc50"},
|
||||
{file = "regex-2024.9.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e5fb5f77c8745a60105403a774fe2c1759b71d3e7b4ca237a5e67ad066c7199"},
|
||||
{file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:54d9ff35d4515debf14bc27f1e3b38bfc453eff3220f5bce159642fa762fe5d4"},
|
||||
{file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df5cbb1fbc74a8305b6065d4ade43b993be03dbe0f8b30032cced0d7740994bd"},
|
||||
{file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7fb89ee5d106e4a7a51bce305ac4efb981536301895f7bdcf93ec92ae0d91c7f"},
|
||||
{file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a738b937d512b30bf75995c0159c0ddf9eec0775c9d72ac0202076c72f24aa96"},
|
||||
{file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e28f9faeb14b6f23ac55bfbbfd3643f5c7c18ede093977f1df249f73fd22c7b1"},
|
||||
{file = "regex-2024.9.11-cp311-cp311-win32.whl", hash = "sha256:18e707ce6c92d7282dfce370cd205098384b8ee21544e7cb29b8aab955b66fa9"},
|
||||
{file = "regex-2024.9.11-cp311-cp311-win_amd64.whl", hash = "sha256:313ea15e5ff2a8cbbad96ccef6be638393041b0a7863183c2d31e0c6116688cf"},
|
||||
{file = "regex-2024.9.11-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b0d0a6c64fcc4ef9c69bd5b3b3626cc3776520a1637d8abaa62b9edc147a58f7"},
|
||||
{file = "regex-2024.9.11-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:49b0e06786ea663f933f3710a51e9385ce0cba0ea56b67107fd841a55d56a231"},
|
||||
{file = "regex-2024.9.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5b513b6997a0b2f10e4fd3a1313568e373926e8c252bd76c960f96fd039cd28d"},
|
||||
{file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee439691d8c23e76f9802c42a95cfeebf9d47cf4ffd06f18489122dbb0a7ad64"},
|
||||
{file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8f877c89719d759e52783f7fe6e1c67121076b87b40542966c02de5503ace42"},
|
||||
{file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23b30c62d0f16827f2ae9f2bb87619bc4fba2044911e2e6c2eb1af0161cdb766"},
|
||||
{file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ab7824093d8f10d44330fe1e6493f756f252d145323dd17ab6b48733ff6c0a"},
|
||||
{file = "regex-2024.9.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8dee5b4810a89447151999428fe096977346cf2f29f4d5e29609d2e19e0199c9"},
|
||||
{file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98eeee2f2e63edae2181c886d7911ce502e1292794f4c5ee71e60e23e8d26b5d"},
|
||||
{file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:57fdd2e0b2694ce6fc2e5ccf189789c3e2962916fb38779d3e3521ff8fe7a822"},
|
||||
{file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d552c78411f60b1fdaafd117a1fca2f02e562e309223b9d44b7de8be451ec5e0"},
|
||||
{file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a0b2b80321c2ed3fcf0385ec9e51a12253c50f146fddb2abbb10f033fe3d049a"},
|
||||
{file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:18406efb2f5a0e57e3a5881cd9354c1512d3bb4f5c45d96d110a66114d84d23a"},
|
||||
{file = "regex-2024.9.11-cp312-cp312-win32.whl", hash = "sha256:e464b467f1588e2c42d26814231edecbcfe77f5ac414d92cbf4e7b55b2c2a776"},
|
||||
{file = "regex-2024.9.11-cp312-cp312-win_amd64.whl", hash = "sha256:9e8719792ca63c6b8340380352c24dcb8cd7ec49dae36e963742a275dfae6009"},
|
||||
{file = "regex-2024.9.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c157bb447303070f256e084668b702073db99bbb61d44f85d811025fcf38f784"},
|
||||
{file = "regex-2024.9.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4db21ece84dfeefc5d8a3863f101995de646c6cb0536952c321a2650aa202c36"},
|
||||
{file = "regex-2024.9.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:220e92a30b426daf23bb67a7962900ed4613589bab80382be09b48896d211e92"},
|
||||
{file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1ae19e64c14c7ec1995f40bd932448713d3c73509e82d8cd7744dc00e29e86"},
|
||||
{file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f47cd43a5bfa48f86925fe26fbdd0a488ff15b62468abb5d2a1e092a4fb10e85"},
|
||||
{file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9d4a76b96f398697fe01117093613166e6aa8195d63f1b4ec3f21ab637632963"},
|
||||
{file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ea51dcc0835eea2ea31d66456210a4e01a076d820e9039b04ae8d17ac11dee6"},
|
||||
{file = "regex-2024.9.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7aaa315101c6567a9a45d2839322c51c8d6e81f67683d529512f5bcfb99c802"},
|
||||
{file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c57d08ad67aba97af57a7263c2d9006d5c404d721c5f7542f077f109ec2a4a29"},
|
||||
{file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8404bf61298bb6f8224bb9176c1424548ee1181130818fcd2cbffddc768bed8"},
|
||||
{file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dd4490a33eb909ef5078ab20f5f000087afa2a4daa27b4c072ccb3cb3050ad84"},
|
||||
{file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:eee9130eaad130649fd73e5cd92f60e55708952260ede70da64de420cdcad554"},
|
||||
{file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a2644a93da36c784e546de579ec1806bfd2763ef47babc1b03d765fe560c9f8"},
|
||||
{file = "regex-2024.9.11-cp313-cp313-win32.whl", hash = "sha256:e997fd30430c57138adc06bba4c7c2968fb13d101e57dd5bb9355bf8ce3fa7e8"},
|
||||
{file = "regex-2024.9.11-cp313-cp313-win_amd64.whl", hash = "sha256:042c55879cfeb21a8adacc84ea347721d3d83a159da6acdf1116859e2427c43f"},
|
||||
{file = "regex-2024.9.11-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:35f4a6f96aa6cb3f2f7247027b07b15a374f0d5b912c0001418d1d55024d5cb4"},
|
||||
{file = "regex-2024.9.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:55b96e7ce3a69a8449a66984c268062fbaa0d8ae437b285428e12797baefce7e"},
|
||||
{file = "regex-2024.9.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cb130fccd1a37ed894824b8c046321540263013da72745d755f2d35114b81a60"},
|
||||
{file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:323c1f04be6b2968944d730e5c2091c8c89767903ecaa135203eec4565ed2b2b"},
|
||||
{file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be1c8ed48c4c4065ecb19d882a0ce1afe0745dfad8ce48c49586b90a55f02366"},
|
||||
{file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5b029322e6e7b94fff16cd120ab35a253236a5f99a79fb04fda7ae71ca20ae8"},
|
||||
{file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6fff13ef6b5f29221d6904aa816c34701462956aa72a77f1f151a8ec4f56aeb"},
|
||||
{file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:587d4af3979376652010e400accc30404e6c16b7df574048ab1f581af82065e4"},
|
||||
{file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:079400a8269544b955ffa9e31f186f01d96829110a3bf79dc338e9910f794fca"},
|
||||
{file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f9268774428ec173654985ce55fc6caf4c6d11ade0f6f914d48ef4719eb05ebb"},
|
||||
{file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:23f9985c8784e544d53fc2930fc1ac1a7319f5d5332d228437acc9f418f2f168"},
|
||||
{file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:ae2941333154baff9838e88aa71c1d84f4438189ecc6021a12c7573728b5838e"},
|
||||
{file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e93f1c331ca8e86fe877a48ad64e77882c0c4da0097f2212873a69bbfea95d0c"},
|
||||
{file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:846bc79ee753acf93aef4184c040d709940c9d001029ceb7b7a52747b80ed2dd"},
|
||||
{file = "regex-2024.9.11-cp38-cp38-win32.whl", hash = "sha256:c94bb0a9f1db10a1d16c00880bdebd5f9faf267273b8f5bd1878126e0fbde771"},
|
||||
{file = "regex-2024.9.11-cp38-cp38-win_amd64.whl", hash = "sha256:2b08fce89fbd45664d3df6ad93e554b6c16933ffa9d55cb7e01182baaf971508"},
|
||||
{file = "regex-2024.9.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:07f45f287469039ffc2c53caf6803cd506eb5f5f637f1d4acb37a738f71dd066"},
|
||||
{file = "regex-2024.9.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4838e24ee015101d9f901988001038f7f0d90dc0c3b115541a1365fb439add62"},
|
||||
{file = "regex-2024.9.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6edd623bae6a737f10ce853ea076f56f507fd7726bee96a41ee3d68d347e4d16"},
|
||||
{file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c69ada171c2d0e97a4b5aa78fbb835e0ffbb6b13fc5da968c09811346564f0d3"},
|
||||
{file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02087ea0a03b4af1ed6ebab2c54d7118127fee8d71b26398e8e4b05b78963199"},
|
||||
{file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69dee6a020693d12a3cf892aba4808fe168d2a4cef368eb9bf74f5398bfd4ee8"},
|
||||
{file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:297f54910247508e6e5cae669f2bc308985c60540a4edd1c77203ef19bfa63ca"},
|
||||
{file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecea58b43a67b1b79805f1a0255730edaf5191ecef84dbc4cc85eb30bc8b63b9"},
|
||||
{file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eab4bb380f15e189d1313195b062a6aa908f5bd687a0ceccd47c8211e9cf0d4a"},
|
||||
{file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0cbff728659ce4bbf4c30b2a1be040faafaa9eca6ecde40aaff86f7889f4ab39"},
|
||||
{file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:54c4a097b8bc5bb0dfc83ae498061d53ad7b5762e00f4adaa23bee22b012e6ba"},
|
||||
{file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:73d6d2f64f4d894c96626a75578b0bf7d9e56dcda8c3d037a2118fdfe9b1c664"},
|
||||
{file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:e53b5fbab5d675aec9f0c501274c467c0f9a5d23696cfc94247e1fb56501ed89"},
|
||||
{file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ffbcf9221e04502fc35e54d1ce9567541979c3fdfb93d2c554f0ca583a19b35"},
|
||||
{file = "regex-2024.9.11-cp39-cp39-win32.whl", hash = "sha256:e4c22e1ac1f1ec1e09f72e6c44d8f2244173db7eb9629cc3a346a8d7ccc31142"},
|
||||
{file = "regex-2024.9.11-cp39-cp39-win_amd64.whl", hash = "sha256:faa3c142464efec496967359ca99696c896c591c56c53506bac1ad465f66e919"},
|
||||
{file = "regex-2024.9.11.tar.gz", hash = "sha256:6c188c307e8433bcb63dc1915022deb553b4203a70722fc542c363bf120a01fd"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.6.6"
|
||||
description = "An extremely fast Python linter and code formatter, written in Rust."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "ruff-0.6.6-py3-none-linux_armv6l.whl", hash = "sha256:f5bc5398457484fc0374425b43b030e4668ed4d2da8ee7fdda0e926c9f11ccfb"},
|
||||
{file = "ruff-0.6.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:515a698254c9c47bb84335281a170213b3ee5eb47feebe903e1be10087a167ce"},
|
||||
{file = "ruff-0.6.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6bb1b4995775f1837ab70f26698dd73852bbb82e8f70b175d2713c0354fe9182"},
|
||||
{file = "ruff-0.6.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69c546f412dfae8bb9cc4f27f0e45cdd554e42fecbb34f03312b93368e1cd0a6"},
|
||||
{file = "ruff-0.6.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:59627e97364329e4eae7d86fa7980c10e2b129e2293d25c478ebcb861b3e3fd6"},
|
||||
{file = "ruff-0.6.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94c3f78c3d32190aafbb6bc5410c96cfed0a88aadb49c3f852bbc2aa9783a7d8"},
|
||||
{file = "ruff-0.6.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:704da526c1e137f38c8a067a4a975fe6834b9f8ba7dbc5fd7503d58148851b8f"},
|
||||
{file = "ruff-0.6.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:efeede5815a24104579a0f6320660536c5ffc1c91ae94f8c65659af915fb9de9"},
|
||||
{file = "ruff-0.6.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e368aef0cc02ca3593eae2fb8186b81c9c2b3f39acaaa1108eb6b4d04617e61f"},
|
||||
{file = "ruff-0.6.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2653fc3b2a9315bd809725c88dd2446550099728d077a04191febb5ea79a4f79"},
|
||||
{file = "ruff-0.6.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:bb858cd9ce2d062503337c5b9784d7b583bcf9d1a43c4df6ccb5eab774fbafcb"},
|
||||
{file = "ruff-0.6.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:488f8e15c01ea9afb8c0ba35d55bd951f484d0c1b7c5fd746ce3c47ccdedce68"},
|
||||
{file = "ruff-0.6.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:aefb0bd15f1cfa4c9c227b6120573bb3d6c4ee3b29fb54a5ad58f03859bc43c6"},
|
||||
{file = "ruff-0.6.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a4c0698cc780bcb2c61496cbd56b6a3ac0ad858c966652f7dbf4ceb029252fbe"},
|
||||
{file = "ruff-0.6.6-py3-none-win32.whl", hash = "sha256:aadf81ddc8ab5b62da7aae78a91ec933cbae9f8f1663ec0325dae2c364e4ad84"},
|
||||
{file = "ruff-0.6.6-py3-none-win_amd64.whl", hash = "sha256:0adb801771bc1f1b8cf4e0a6fdc30776e7c1894810ff3b344e50da82ef50eeb1"},
|
||||
{file = "ruff-0.6.6-py3-none-win_arm64.whl", hash = "sha256:4b4d32c137bc781c298964dd4e52f07d6f7d57c03eae97a72d97856844aa510a"},
|
||||
{file = "ruff-0.6.6.tar.gz", hash = "sha256:0fc030b6fd14814d69ac0196396f6761921bd20831725c7361e1b8100b818034"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sentry-sdk"
|
||||
version = "1.29.2"
|
||||
|
|
@ -639,6 +970,26 @@ anyio = ">=3.4.0,<5"
|
|||
[package.extras]
|
||||
full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyaml"]
|
||||
|
||||
[[package]]
|
||||
name = "tqdm"
|
||||
version = "4.66.5"
|
||||
description = "Fast, Extensible Progress Meter"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd"},
|
||||
{file = "tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
|
||||
[package.extras]
|
||||
dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"]
|
||||
notebook = ["ipywidgets (>=6)"]
|
||||
slack = ["slack-sdk"]
|
||||
telegram = ["requests"]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.12.2"
|
||||
|
|
@ -650,34 +1001,6 @@ files = [
|
|||
{file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tzdata"
|
||||
version = "2024.1"
|
||||
description = "Provider of IANA time zone data"
|
||||
optional = false
|
||||
python-versions = ">=2"
|
||||
files = [
|
||||
{file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"},
|
||||
{file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tzlocal"
|
||||
version = "5.2"
|
||||
description = "tzinfo object for the local timezone"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "tzlocal-5.2-py3-none-any.whl", hash = "sha256:49816ef2fe65ea8ac19d19aa7a1ae0551c834303d5014c6d5a62e4cbda8047b8"},
|
||||
{file = "tzlocal-5.2.tar.gz", hash = "sha256:8d399205578f1a9342816409cc1e46a93ebd5755e39ea2d85334bea911bf0e6e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
tzdata = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
|
||||
[package.extras]
|
||||
devenv = ["check-manifest", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3)", "zest.releaser"]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.2.2"
|
||||
|
|
@ -751,4 +1074,4 @@ files = [
|
|||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.12"
|
||||
content-hash = "c04733f92b42c7d2b4b8b89e8bb4510c7de5048a678b7ccc58969323a072a10f"
|
||||
content-hash = "4015e3dbdfe37ed67baac4c18550b38bd93664462c449a8a0164134eb5497229"
|
||||
|
|
@ -19,11 +19,15 @@ fastapi = "0.108.0"
|
|||
gunicorn = "21.2.0"
|
||||
redis = {version = "4.6.0", extras = ["hiredis"]}
|
||||
xkcdpass = "1.19.3"
|
||||
apscheduler = "3.10.4"
|
||||
loguru = "0.6.0"
|
||||
anyio = "<=4.0.0"
|
||||
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
djlint = "^1.35.2"
|
||||
ruff = "^0.6.6"
|
||||
black = "^24.8.0"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
|
@ -43,9 +43,9 @@ wait_for_postgres()
|
|||
# logging.basicConfig(handlers=[InterceptHandler()], level=0, force=True)
|
||||
configure_logger()
|
||||
|
||||
medium_cache = PostgreSQLCacheBackend("postgresql://postgres:postgres@postgres:5432/postgres")
|
||||
medium_cache = PostgreSQLCacheBackend(config.DATABASE_URL)
|
||||
medium_cache.init_db()
|
||||
# migrate_to_postgres_thread = execute_migrate_to_postgres_in_thread("medium_db_cache.sqlite", "postgresql://postgres:postgres@postgres:5432/postgres")
|
||||
|
||||
logger.debug(f"Database length: {medium_cache.all_length()}")
|
||||
|
||||
medium_api = MediumApi(auth_cookies=config.MEDIUM_AUTH_COOKIES, timeout=3, proxy_list=config.PROXY_LIST)
|
||||
|
|
@ -30,9 +30,11 @@ REDIS_HOST = config("REDIS_HOST", default="redis_service")
|
|||
REDIS_PORT = config("REDIS_PORT", cast=int, default=6379)
|
||||
REDIS_TIMEOUT = config("REDIS_TIMEOUT", cast=int, default=1.75)
|
||||
|
||||
DATABASE_URL = config("DATABASE_URL", default="postgresql://postgres:postgres@postgres:5432/postgres")
|
||||
|
||||
SENTRY_SDK_DSN = config("SENTRY_SDK_DSN", default=None)
|
||||
SENTRY_TRACES_SAMPLE_RATE = config("SENTRY_TRACES_SAMPLE_RATE", cast=float, default=0.2)
|
||||
SENTRY_PROFILES_SAMPLE_RATE = config("SENTRY_PROFILES_SAMPLE_RATE", cast=float, default=0.2)
|
||||
|
||||
_PROXY_LIST = config("PROXY_LIST", cast=str, default="")
|
||||
PROXY_LIST = _PROXY_LIST.split(",") if _PROXY_LIST else []
|
||||
PROXY_LIST_RAW = config("PROXY_LIST", cast=str, default="")
|
||||
PROXY_LIST = PROXY_LIST_RAW.split(",") if PROXY_LIST_RAW else []
|
||||
44
web/server/templates/url_box.html
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<div
|
||||
class="md:max-w-6xl bg-white dark:bg-gray-600 w-full shadow-md rounded-md p-8 mt-8"
|
||||
>
|
||||
<form
|
||||
id="medium-link-form"
|
||||
class="flex items-center border rounded-md border-gray-300 px-4 py-2"
|
||||
>
|
||||
<svg
|
||||
class="h-5 w-5 text-gray-500 dark:text-gray-100 mr-2"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
|
||||
></path>
|
||||
</svg>
|
||||
<input
|
||||
id="medium-link-input"
|
||||
type="text"
|
||||
placeholder="Enter Medium post link"
|
||||
class="w-full focus:outline-none text-green-500 dark:bg-gray-600 border-gray-300"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
class="ml-2 bg-green-500 text-white px-4 py-2 rounded-md hover:bg-green-600 focus:outline-none"
|
||||
>
|
||||
Go
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<script>
|
||||
document
|
||||
.getElementById("medium-link-form")
|
||||
.addEventListener("submit", function (event) {
|
||||
event.preventDefault();
|
||||
const mediumLinkInput = document.getElementById("medium-link-input");
|
||||
window.location.href = `${window.location.origin}/${mediumLinkInput.value}`;
|
||||
});
|
||||
</script>
|
||||
0
web/server/utils/__init__.py
Normal file
|
|
@ -12,7 +12,6 @@ RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /main ./main.go
|
|||
FROM alpine:3.20
|
||||
WORKDIR /app
|
||||
|
||||
# COPY .env .
|
||||
COPY --from=builder /main .
|
||||
|
||||
ENTRYPOINT ["/app/main"]
|
||||