Compare commits
58 commits
Author | SHA1 | Date | |
---|---|---|---|
|
ef2827a6d2 | ||
|
734acedecb | ||
|
7c01bce35f | ||
|
bbcbdff8bc | ||
|
33b437d8fe | ||
|
2071d5c2bc | ||
|
0a57629cff | ||
|
4d458fb75f | ||
|
deea4d6a2a | ||
|
22e836b707 | ||
|
01237362c5 | ||
|
08ba5eb1a6 | ||
|
80a0bec993 | ||
|
86be4a69a5 | ||
|
33a7569d17 | ||
|
3e99e34fa7 | ||
|
b283965299 | ||
|
86e6c9b838 | ||
|
99258b0376 | ||
|
ee6f57aa43 | ||
|
5409407833 | ||
|
f09a861cd1 | ||
|
7d449b5229 | ||
|
5ca8403afc | ||
|
91176050c0 | ||
|
abb1966b33 | ||
|
badd23ba7c | ||
|
602fc277dd | ||
|
8f277a648e | ||
|
4079f568ba | ||
|
30ad1d9113 | ||
|
72a5c6781f | ||
|
d921dcd115 | ||
|
9a257efd46 | ||
|
7df2056c1d | ||
|
884641e58a | ||
|
b7921a2a97 | ||
|
8b3aae2985 | ||
|
6d08a3e017 | ||
|
cfc13da108 | ||
|
c54b89eb15 | ||
|
545e8a7980 | ||
|
dc297cbff8 | ||
|
ff1e1bcc86 | ||
|
c945798d09 | ||
|
db4e512e63 | ||
|
91f58e61c7 | ||
|
9ffd8260fd | ||
|
d8568c06e9 | ||
|
3875b19817 | ||
|
c41ca87e4e | ||
|
1f5fb428a3 | ||
|
d9ef063243 | ||
|
84291c7739 | ||
|
90bcbd97fd | ||
|
1e205b6b2e | ||
|
88b37a5325 | ||
|
99ca0f420f |
37
.github/workflows/release.yml
vendored
|
@ -1,5 +1,5 @@
|
||||||
name: 🛠️ Main
|
name: 🛠️ Main
|
||||||
run-name: 🚀 Deploy de versão
|
run-name: 🚀 Version Deployment
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
@ -9,49 +9,58 @@ on:
|
||||||
env:
|
env:
|
||||||
DOCKER_REGISTRY: ghcr.io
|
DOCKER_REGISTRY: ghcr.io
|
||||||
DOCKER_IMAGE_NAME: ${{ github.repository }}
|
DOCKER_IMAGE_NAME: ${{ github.repository }}
|
||||||
|
DOCKERHUB_REPOSITORY: ${{ secrets.DOCKERHUB_USERNAME }}/marreta
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
docker-build:
|
docker-build:
|
||||||
name: 🐳 Build e Push
|
name: 🐳 Build and Push
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 📥 Checkout código
|
- name: 📥 Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: 🏷️ Extrair versão da tag
|
- name: 🏷️ Extract version from tag
|
||||||
id: get_version
|
id: get_version
|
||||||
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: 🔧 Configurar QEMU
|
- name: 🔧 Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
- name: 🛠️ Configurar Docker Buildx
|
- name: 🛠️ Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
with:
|
with:
|
||||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||||
|
|
||||||
- name: 📋 Extrair metadata Docker
|
- name: 📋 Extract Docker metadata
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE_NAME }}
|
images: |
|
||||||
|
${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE_NAME }}
|
||||||
|
${{ env.DOCKERHUB_REPOSITORY }}
|
||||||
tags: |
|
tags: |
|
||||||
type=semver,pattern={{version}}
|
type=semver,pattern={{version}}
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
type=sha
|
type=sha
|
||||||
|
|
||||||
- name: 🔐 Login no Registry
|
- name: 🔐 Log in to GitHub Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.DOCKER_REGISTRY }}
|
registry: ${{ env.DOCKER_REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: 🏗️ Build e Push
|
- name: 🔐 Log in to Docker Hub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: 🏗️ Build and Push
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
|
@ -63,21 +72,21 @@ jobs:
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
publish-release:
|
publish-release:
|
||||||
name: 📦 Publicar Release
|
name: 📦 Publish Release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: docker-build
|
needs: docker-build
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 📥 Checkout código
|
- name: 📥 Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: 🏷️ Extrair versão da tag
|
- name: 🏷️ Extract version from tag
|
||||||
id: get_version
|
id: get_version
|
||||||
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: 📝 Criar Release
|
- name: 📝 Create Release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
with:
|
||||||
name: "🎉 Release v${{ steps.get_version.outputs.VERSION }}"
|
name: "🎉 Release v${{ steps.get_version.outputs.VERSION }}"
|
||||||
|
|
51
.gitignore
vendored
|
@ -3,48 +3,23 @@ composer.lock
|
||||||
.env
|
.env
|
||||||
app/logs/*.log
|
app/logs/*.log
|
||||||
app/cache/*.gz
|
app/cache/*.gz
|
||||||
|
app/cache/database/.sqlite
|
||||||
|
app/cache/*.json
|
||||||
TODO.md
|
TODO.md
|
||||||
|
node_modules
|
||||||
|
|
||||||
# Created by https://www.toptal.com/developers/gitignore/api/composer,windows,macos,linux
|
|
||||||
# Edit at https://www.toptal.com/developers/gitignore?templates=composer,windows,macos,linux
|
|
||||||
|
|
||||||
### Composer ###
|
|
||||||
composer.phar
|
composer.phar
|
||||||
/vendor/
|
/vendor/
|
||||||
|
|
||||||
# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control
|
|
||||||
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
|
|
||||||
# composer.lock
|
|
||||||
|
|
||||||
### Linux ###
|
|
||||||
*~
|
*~
|
||||||
|
|
||||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
|
||||||
.fuse_hidden*
|
.fuse_hidden*
|
||||||
|
|
||||||
# KDE directory preferences
|
|
||||||
.directory
|
.directory
|
||||||
|
|
||||||
# Linux trash folder which might appear on any partition or disk
|
|
||||||
.Trash-*
|
.Trash-*
|
||||||
|
|
||||||
# .nfs files are created when an open file is removed but is still being accessed
|
|
||||||
.nfs*
|
.nfs*
|
||||||
|
|
||||||
### macOS ###
|
|
||||||
# General
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.AppleDouble
|
.AppleDouble
|
||||||
.LSOverride
|
.LSOverride
|
||||||
|
|
||||||
# Icon must end with two \r
|
|
||||||
Icon
|
Icon
|
||||||
|
|
||||||
|
|
||||||
# Thumbnails
|
|
||||||
._*
|
._*
|
||||||
|
|
||||||
# Files that might appear in the root of a volume
|
|
||||||
.DocumentRevisions-V100
|
.DocumentRevisions-V100
|
||||||
.fseventsd
|
.fseventsd
|
||||||
.Spotlight-V100
|
.Spotlight-V100
|
||||||
|
@ -52,42 +27,22 @@ Icon
|
||||||
.Trashes
|
.Trashes
|
||||||
.VolumeIcon.icns
|
.VolumeIcon.icns
|
||||||
.com.apple.timemachine.donotpresent
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
# Directories potentially created on remote AFP share
|
|
||||||
.AppleDB
|
.AppleDB
|
||||||
.AppleDesktop
|
.AppleDesktop
|
||||||
Network Trash Folder
|
Network Trash Folder
|
||||||
Temporary Items
|
Temporary Items
|
||||||
.apdisk
|
.apdisk
|
||||||
|
|
||||||
### macOS Patch ###
|
|
||||||
# iCloud generated files
|
|
||||||
*.icloud
|
*.icloud
|
||||||
|
|
||||||
### Windows ###
|
|
||||||
# Windows thumbnail cache files
|
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
Thumbs.db:encryptable
|
Thumbs.db:encryptable
|
||||||
ehthumbs.db
|
ehthumbs.db
|
||||||
ehthumbs_vista.db
|
ehthumbs_vista.db
|
||||||
|
|
||||||
# Dump file
|
|
||||||
*.stackdump
|
*.stackdump
|
||||||
|
|
||||||
# Folder config file
|
|
||||||
[Dd]esktop.ini
|
[Dd]esktop.ini
|
||||||
|
|
||||||
# Recycle Bin used on file shares
|
|
||||||
$RECYCLE.BIN/
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
# Windows Installer files
|
|
||||||
*.cab
|
*.cab
|
||||||
*.msi
|
*.msi
|
||||||
*.msix
|
*.msix
|
||||||
*.msm
|
*.msm
|
||||||
*.msp
|
*.msp
|
||||||
|
|
||||||
# Windows shortcuts
|
|
||||||
*.lnk
|
*.lnk
|
||||||
|
|
||||||
# End of https://www.toptal.com/developers/gitignore/api/composer,windows,macos,linux
|
|
51
Dockerfile
|
@ -1,50 +1,67 @@
|
||||||
FROM php:8.3-fpm
|
# Stage 0: Base
|
||||||
|
FROM php:8.3-fpm AS base
|
||||||
|
|
||||||
# Install PHP dependencies and extensions
|
# Install dependencies and extensions
|
||||||
# Instala dependências e extensões do PHP
|
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
nginx \
|
nginx \
|
||||||
nano \
|
nano \
|
||||||
procps \
|
procps \
|
||||||
|
psmisc \
|
||||||
zip \
|
zip \
|
||||||
git \
|
git \
|
||||||
htop \
|
htop \
|
||||||
|
cron \
|
||||||
libzip-dev \
|
libzip-dev \
|
||||||
libhiredis-dev \
|
libsqlite3-dev \
|
||||||
&& docker-php-ext-install zip opcache \
|
&& docker-php-ext-install zip opcache pdo_sqlite \
|
||||||
&& pecl install redis \
|
&& docker-php-ext-enable opcache \
|
||||||
&& docker-php-ext-enable redis opcache
|
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Stage 1: Build stage
|
||||||
|
FROM base AS builder
|
||||||
|
|
||||||
# Copy OPCache configuration
|
# Copy OPCache configuration
|
||||||
# Copia a configuração do OPCache
|
|
||||||
COPY opcache.ini /usr/local/etc/php/conf.d/opcache.ini
|
COPY opcache.ini /usr/local/etc/php/conf.d/opcache.ini
|
||||||
|
|
||||||
# Install Composer
|
# Install Composer
|
||||||
# Instala o Composer
|
|
||||||
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
|
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
|
||||||
|
|
||||||
# Copy webservice configuration
|
# Copy app folder
|
||||||
# Copia a configuração do webservice
|
|
||||||
COPY default.conf /etc/nginx/sites-available/default
|
|
||||||
|
|
||||||
RUN mkdir -p /app
|
|
||||||
COPY app/ /app/
|
COPY app/ /app/
|
||||||
|
|
||||||
|
# Install composer packages
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
RUN composer install --no-interaction --optimize-autoloader
|
RUN composer install --no-interaction --optimize-autoloader
|
||||||
|
|
||||||
|
# Stage 2: Final
|
||||||
|
FROM base
|
||||||
|
|
||||||
|
# Copy necessary files from the builder stage
|
||||||
|
COPY --from=builder /usr/local/etc/php/conf.d/opcache.ini /usr/local/etc/php/conf.d/opcache.ini
|
||||||
|
COPY --from=builder /usr/local/bin/composer /usr/local/bin/composer
|
||||||
|
COPY --from=builder /app /app
|
||||||
|
|
||||||
|
# Copy webservice configuration
|
||||||
|
COPY default.conf /etc/nginx/sites-available/default
|
||||||
|
|
||||||
# Copy and configure initialization script permissions
|
# Copy and configure initialization script permissions
|
||||||
# Copia e configura permissões do script de inicialização
|
|
||||||
COPY docker-entrypoint.sh /usr/local/bin/
|
COPY docker-entrypoint.sh /usr/local/bin/
|
||||||
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||||
|
RUN chmod +x /app/bin/cleanup
|
||||||
|
RUN chmod +x /app/bin/proxy
|
||||||
|
|
||||||
RUN mkdir -p /app/cache /app/logs
|
# Create cache, database, and logs folders
|
||||||
|
RUN mkdir -p /app/cache /app/cache/database /app/logs
|
||||||
|
|
||||||
# Configure base permissions for /app directory
|
# Configure base permissions for /app directory
|
||||||
# Configura permissões base para o diretório /app
|
|
||||||
RUN chown -R www-data:www-data /app \
|
RUN chown -R www-data:www-data /app \
|
||||||
&& chmod -R 755 /app
|
&& chmod -R 755 /app
|
||||||
|
|
||||||
|
# Configure Cron
|
||||||
|
RUN touch /app/logs/cron.log
|
||||||
|
RUN echo '0 * * * * root php "/app/bin/cleanup" >> /app/logs/cleanup.log 2>&1' >> /etc/crontab
|
||||||
|
RUN echo '0 * * * * root php "/app/bin/proxy" >> /app/logs/proxy.log 2>&1' >> /etc/crontab
|
||||||
|
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
|
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
|
226
README.en.md
|
@ -1,49 +1,41 @@
|
||||||
# 🛠️ Marreta
|
# 🛠️ Marreta
|
||||||
|
|
||||||
[](https://github.com/manualdousuario/marreta/blob/master/README.en.md)
|
|
||||||
[](https://github.com/manualdousuario/marreta/blob/master/README.md)
|
[](https://github.com/manualdousuario/marreta/blob/master/README.md)
|
||||||
|
[](https://github.com/manualdousuario/marreta/blob/master/README.en.md)
|
||||||
|
|
||||||
[](https://github.com/manualdousuario/marreta/network/members)
|
[](https://github.com/manualdousuario/marreta/network/members)
|
||||||
[](https://github.com/manualdousuario/marreta/stargazers)
|
[](https://github.com/manualdousuario/marreta/stargazers)
|
||||||
[](https://github.com/manualdousuario/marreta/issues)
|
[](https://github.com/manualdousuario/marreta/issues)
|
||||||
|
|
||||||
Marreta is a tool that removes access barriers and visual distractions!
|
Marreta is a tool that breaks access barriers and elements that hinder reading!
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Public instance at [marreta.pcdomanual.com](https://marreta.pcdomanual.com)!
|
Public instance at [marreta.pcdomanual.com](https://marreta.pcdomanual.com)!
|
||||||
|
|
||||||
## ✨ Features
|
## ✨ What's Cool?
|
||||||
|
|
||||||
- Automatically cleans and fixes URLs
|
- Automatically cleans and corrects URLs
|
||||||
- Removes annoying tracking parameters
|
- Removes annoying tracking parameters
|
||||||
- Forces HTTPS to keep everything secure
|
- Forces HTTPS to keep everything secure
|
||||||
- Changes user agent to avoid blocks
|
- Changes user agent to avoid blocking
|
||||||
- Smart DNS
|
- Leaves HTML clean and optimized
|
||||||
- Keeps HTML clean and optimized
|
- Fixes relative URLs on its own
|
||||||
- Fixes relative URLs automatically
|
- Allows you to add your own styles and scripts
|
||||||
- Allows custom styles
|
|
||||||
- Removes unwanted elements
|
- Removes unwanted elements
|
||||||
- Cache, cache!
|
- Caching, caching!
|
||||||
- Blocks domains you don't want
|
- Blocks domains you don't want
|
||||||
- Allows custom headers and cookies configuration
|
- Allows configuring headers and cookies your way
|
||||||
- Everything with SSL/TLS
|
- PHP-FPM and OPcache
|
||||||
- PHP-FPM
|
- Proxy Support
|
||||||
- OPcache enabled
|
|
||||||
- Direct sharing via PWA on Chrome on Android
|
|
||||||
|
|
||||||
## 🐳 Docker
|
## 🐳 Installing with Docker
|
||||||
|
|
||||||
### Prerequisites
|
Install [Docker and Docker Compose](https://docs.docker.com/engine/install/)
|
||||||
|
|
||||||
You only need:
|
|
||||||
- Docker and docker compose
|
|
||||||
|
|
||||||
### Production
|
|
||||||
|
|
||||||
`curl -o ./docker-compose.yml https://raw.githubusercontent.com/manualdousuario/marreta/main/docker-compose.yml`
|
`curl -o ./docker-compose.yml https://raw.githubusercontent.com/manualdousuario/marreta/main/docker-compose.yml`
|
||||||
|
|
||||||
If needed
|
Now modify with your preferences:
|
||||||
|
|
||||||
`nano docker-compose.yml`
|
`nano docker-compose.yml`
|
||||||
|
|
||||||
|
@ -58,187 +50,49 @@ services:
|
||||||
- SITE_NAME=
|
- SITE_NAME=
|
||||||
- SITE_DESCRIPTION=
|
- SITE_DESCRIPTION=
|
||||||
- SITE_URL=
|
- SITE_URL=
|
||||||
|
- LANGUAGE=
|
||||||
```
|
```
|
||||||
|
|
||||||
- `SITE_NAME`: Your Marreta's name
|
- `SITE_NAME`: Name of your Marreta
|
||||||
- `SITE_DESCRIPTION`: Tell what it's for
|
- `SITE_DESCRIPTION`: Explain what it's for
|
||||||
- `SITE_URL`: Where it will run, full address with `https://`. If you change the port in docker-compose (e.g., 8080:80), you must also include the port in SITE_URL (e.g., https://yoursite:8080)
|
- `SITE_URL`: Where it will run, full address with `https://`. If you change the port in docker-compose (e.g., 8080:80), you must also include the port in SITE_URL (e.g., https://yoursite:8080)
|
||||||
- `DNS_SERVERS`: Which DNS servers to use `1.1.1.1, 8.8.8.8`
|
- `SELENIUM_HOST`: Server:PORT of Selenium host (e.g., selenium-hub:4444)
|
||||||
- `SELENIUM_HOST`: Selenium host server:PORT (e.g., selenium-hub:4444)
|
- `LANGUAGE`: pt-br (Brazilian Portuguese), en (English), es (Spanish), de-de (German), ru-ru (Russian)
|
||||||
|
|
||||||
Now you can run `docker compose up -d`
|
Now just run `docker compose up -d`
|
||||||
|
|
||||||
#### Development
|
### More configurations:
|
||||||
|
- Selenium: https://github.com/manualdousuario/marreta/wiki/%F0%9F%92%BB-Selenium-Hub-(Chrome-and-Firefox)
|
||||||
|
- S3 Cache: https://github.com/manualdousuario/marreta/wiki/%F0%9F%97%83%EF%B8%8F-Cache-S3
|
||||||
|
- Maintenance: https://github.com/manualdousuario/marreta/wiki/%F0%9F%9B%A0%EF%B8%8F-Maintenance
|
||||||
|
|
||||||
1. First, clone the project:
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/manualdousuario/marreta/
|
|
||||||
cd marreta
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Create the configuration file:
|
### 🛡️ DMCA
|
||||||
```bash
|
|
||||||
cp app/.env.sample app/.env
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Configure it your way in `app/.env`:
|
To block domains from DMCA requests, create the file `app/cache/dmca_domains.json`:
|
||||||
```env
|
|
||||||
SITE_NAME="Marreta"
|
|
||||||
SITE_DESCRIPTION="Paywall hammer!"
|
|
||||||
SITE_URL=http://localhost
|
|
||||||
DNS_SERVERS=1.1.1.1,8.8.8.8
|
|
||||||
LOG_LEVEL=WARNING
|
|
||||||
SELENIUM_HOST=selenium-hub:4444
|
|
||||||
LANGUAGE=pt-br
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Run everything:
|
```json
|
||||||
```bash
|
[
|
||||||
docker-compose up -d
|
{
|
||||||
```
|
"host": "exemplo.com.br",
|
||||||
|
"message": "This content has been blocked on request"
|
||||||
Done! It will be running at `http://localhost` 🎉
|
}
|
||||||
|
]
|
||||||
## ⚙️ Customization
|
|
||||||
|
|
||||||
The configurations are organized in `data/`:
|
|
||||||
|
|
||||||
- `domain_rules.php`: Site-specific rules
|
|
||||||
- `global_rules.php`: Rules that apply to all sites
|
|
||||||
- `blocked_domains.php`: List of blocked sites
|
|
||||||
|
|
||||||
### Translations
|
|
||||||
|
|
||||||
- `/languages/`: Each language is in its ISO id (`pt-br, en, es or de-de`) and can be defined in the `LANGUAGE` environment
|
|
||||||
|
|
||||||
### S3 Cache
|
|
||||||
|
|
||||||
Cache storage support in S3. Configure the following variables in your `.env`:
|
|
||||||
|
|
||||||
```env
|
|
||||||
S3_CACHE_ENABLED=true
|
|
||||||
|
|
||||||
S3_ACCESS_KEY=access_key
|
|
||||||
S3_SECRET_KEY=secret_key
|
|
||||||
S3_BUCKET=bucket_name
|
|
||||||
S3_REGION=us-east-1
|
|
||||||
S3_FOLDER_=cache/
|
|
||||||
S3_ACL=private
|
|
||||||
S3_ENDPOINT=
|
|
||||||
```
|
|
||||||
|
|
||||||
Possible configurations:
|
|
||||||
|
|
||||||
```
|
|
||||||
## R2
|
|
||||||
S3_ACCESS_KEY=access_key
|
|
||||||
S3_SECRET_KEY=secret_key
|
|
||||||
S3_BUCKET=bucket_name
|
|
||||||
S3_ENDPOINT=https://{TOKEN}.r2.cloudflarestorage.com
|
|
||||||
S3_REGION=auto
|
|
||||||
S3_FOLDER_=cache/
|
|
||||||
S3_ACL=private
|
|
||||||
|
|
||||||
## DigitalOcean
|
|
||||||
S3_ACCESS_KEY=access_key
|
|
||||||
S3_SECRET_KEY=secret_key
|
|
||||||
S3_BUCKET=bucket_name
|
|
||||||
S3_ENDPOINT=https://{REGION}.digitaloceanspaces.com
|
|
||||||
S3_REGION=auto
|
|
||||||
S3_FOLDER_=cache/
|
|
||||||
S3_ACL=private
|
|
||||||
```
|
|
||||||
|
|
||||||
### Selenium Integration
|
|
||||||
|
|
||||||
Selenium integration for processing websites that require javascript or have more advanced protection barriers. To use this functionality, you need to set up a Selenium environment with Firefox. Add the following configuration to your `docker-compose.yml`:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
services:
|
|
||||||
selenium-firefox:
|
|
||||||
container_name: selenium-firefox
|
|
||||||
image: selenium/node-firefox:4.27.0-20241204
|
|
||||||
shm_size: 2gb
|
|
||||||
environment:
|
|
||||||
- SE_EVENT_BUS_HOST=selenium-hub
|
|
||||||
- SE_EVENT_BUS_PUBLISH_PORT=4442
|
|
||||||
- SE_EVENT_BUS_SUBSCRIBE_PORT=4443
|
|
||||||
- SE_ENABLE_TRACING=false
|
|
||||||
- SE_NODE_MAX_SESSIONS=10
|
|
||||||
- SE_NODE_OVERRIDE_MAX_SESSIONS=true
|
|
||||||
entrypoint: bash -c 'SE_OPTS="--host $$HOSTNAME" /opt/bin/entry_point.sh'
|
|
||||||
depends_on:
|
|
||||||
- selenium-hub
|
|
||||||
|
|
||||||
selenium-hub:
|
|
||||||
image: selenium/hub:4.27.0-20241204
|
|
||||||
container_name: selenium-hub
|
|
||||||
environment:
|
|
||||||
- SE_ENABLE_TRACING=false
|
|
||||||
- GRID_MAX_SESSION=10
|
|
||||||
- GRID_BROWSER_TIMEOUT=10
|
|
||||||
- GRID_TIMEOUT=10
|
|
||||||
ports:
|
|
||||||
- 4442:4442
|
|
||||||
- 4443:4443
|
|
||||||
- 4444:4444
|
|
||||||
```
|
|
||||||
|
|
||||||
Important settings:
|
|
||||||
- `shm_size`: Sets the shared memory size for Firefox (2GB recommended)
|
|
||||||
- `SE_NODE_MAX_SESSIONS`: Maximum number of concurrent sessions per node
|
|
||||||
- `GRID_MAX_SESSION`: Maximum number of concurrent sessions in the hub
|
|
||||||
- `GRID_BROWSER_TIMEOUT` and `GRID_TIMEOUT`: Timeouts in seconds
|
|
||||||
|
|
||||||
After setting up Selenium, make sure to set the `SELENIUM_HOST` variable in your environment to point to the Selenium hub (typically `selenium-hub:4444`).
|
|
||||||
|
|
||||||
### Logging System
|
|
||||||
|
|
||||||
Logs are stored in `app/logs/app.log` with automatic 7-day rotation.
|
|
||||||
|
|
||||||
Log settings available in `.env` or docker:
|
|
||||||
|
|
||||||
```env
|
|
||||||
LOG_LEVEL=WARNING
|
|
||||||
```
|
|
||||||
|
|
||||||
Available log levels:
|
|
||||||
- DEBUG: Detailed information for debugging
|
|
||||||
- INFO: General operational information
|
|
||||||
- WARNING: Warnings that deserve attention (default)
|
|
||||||
- ERROR: Errors that don't stop operation
|
|
||||||
- CRITICAL: Critical errors that need immediate attention
|
|
||||||
|
|
||||||
## 🛠️ Maintenance
|
|
||||||
|
|
||||||
### Logs
|
|
||||||
|
|
||||||
View application logs:
|
|
||||||
```bash
|
|
||||||
docker-compose logs app
|
|
||||||
# or directly from the log file
|
|
||||||
cat app/logs/app.log
|
|
||||||
```
|
|
||||||
|
|
||||||
### Clearing the cache
|
|
||||||
|
|
||||||
When you need to clear:
|
|
||||||
```bash
|
|
||||||
docker-compose exec app rm -rf /app/cache/*
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🚀 Integrations
|
## 🚀 Integrations
|
||||||
|
|
||||||
- 🤖 **Telegram**: [Official Bot](https://t.me/leissoai_bot)
|
- 🤖 **Telegram**: [Official Bot](https://t.me/leissoai_bot)
|
||||||
- 🦊 **Firefox**: Extension by [Clarissa Mendes](https://claromes.com/pages/whoami) - [Download](https://addons.mozilla.org/pt-BR/firefox/addon/marreta/) | [Source Code](https://github.com/manualdousuario/marreta-extensao)
|
- 🦊 **Firefox**: Extension by [Clarissa Mendes](https://claromes.com/pages/whoami) - [Download](https://addons.mozilla.org/en-US/firefox/addon/marreta/) | [Source Code](https://github.com/manualdousuario/marreta-extensao)
|
||||||
|
- 🌀 **Chrome**: Extension by [Clarissa Mendes](https://claromes.com/pages/whoami) - [Download](https://chromewebstore.google.com/detail/marreta/ipelapagohjgjcgpncpbmaaacemafppe) | [Source Code](https://github.com/manualdousuario/marreta-extensao)
|
||||||
- 🦋 **Bluesky**: Bot by [Joselito](https://bsky.app/profile/joseli.to) - [Profile](https://bsky.app/profile/marreta.pcdomanual.com) | [Source Code](https://github.com/manualdousuario/marreta-bot)
|
- 🦋 **Bluesky**: Bot by [Joselito](https://bsky.app/profile/joseli.to) - [Profile](https://bsky.app/profile/marreta.pcdomanual.com) | [Source Code](https://github.com/manualdousuario/marreta-bot)
|
||||||
- 🍎 **Apple**: [Shortcuts](https://www.icloud.com/shortcuts/3594074b69ee4707af52ed78922d624f) integration
|
- 🍎 **Apple**: Integration with [Shortcuts](https://www.icloud.com/shortcuts/3594074b69ee4707af52ed78922d624f)
|
||||||
-
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Made with ❤️! If you have questions or suggestions, open an issue and we'll help! 😉
|
Made with ❤️! If you have questions or suggestions, open an issue and we'll help! 😉
|
||||||
|
|
||||||
Thanks to the project [https://github.com/burlesco/burlesco](Burlesco) and [https://github.com/nang-dev/hover-paywalls-browser-extension/](Hover) which was used as a basis for several rules!
|
Special thanks to the projects [Burlesco](https://github.com/burlesco/burlesco) and [Hover](https://github.com/nang-dev/hover-paywalls-browser-extension/) which served as the basis for many rules!
|
||||||
|
|
||||||
## Star History
|
## Star History
|
||||||
|
|
||||||
|
|
200
README.md
|
@ -1,7 +1,7 @@
|
||||||
# 🛠️ Marreta
|
# 🛠️ Marreta
|
||||||
|
|
||||||
[](https://github.com/manualdousuario/marreta/blob/master/README.md)
|
|
||||||
[](https://github.com/manualdousuario/marreta/blob/master/README.en.md)
|
[](https://github.com/manualdousuario/marreta/blob/master/README.en.md)
|
||||||
|
[](https://github.com/manualdousuario/marreta/blob/master/README.md)
|
||||||
|
|
||||||
[](https://github.com/manualdousuario/marreta/network/members)
|
[](https://github.com/manualdousuario/marreta/network/members)
|
||||||
[](https://github.com/manualdousuario/marreta/stargazers)
|
[](https://github.com/manualdousuario/marreta/stargazers)
|
||||||
|
@ -15,35 +15,28 @@ Instancia publica em [marreta.pcdomanual.com](https://marreta.pcdomanual.com)!
|
||||||
|
|
||||||
## ✨ O que tem de legal?
|
## ✨ O que tem de legal?
|
||||||
|
|
||||||
- Limpa e arruma URLs automaticamente
|
- Limpa e corrige URLs automaticamente
|
||||||
- Remove parâmetros chatos de rastreamento
|
- Remove parâmetros chatos de rastreamento
|
||||||
- Força HTTPS pra manter tudo seguro
|
- Força HTTPS pra manter tudo seguro
|
||||||
- Troca de user agent pra evitar bloqueios
|
- Troca de user agent pra evitar bloqueios
|
||||||
- DNS esperto
|
|
||||||
- Deixa o HTML limpinho e otimizado
|
- Deixa o HTML limpinho e otimizado
|
||||||
- Conserta URLs relativas sozinho
|
- Conserta URLs relativas sozinho
|
||||||
- Permite colocar seus próprios estilos
|
- Permite colocar seus próprios estilos e scripts
|
||||||
- Remove elementos indesejados
|
- Remove elementos indesejados
|
||||||
- Cache, cache!
|
- Cache, cache!
|
||||||
- Bloqueia domínios que você não quer
|
- Bloqueia domínios que você não quer
|
||||||
|
- Proteção DMCA com mensagens personalizadas
|
||||||
- Permite configurar headers e cookies do seu jeito
|
- Permite configurar headers e cookies do seu jeito
|
||||||
- Tudo com SSL/TLS
|
- PHP-FPM e OPcache
|
||||||
- PHP-FPM
|
- Suporte a Proxy
|
||||||
- OPcache ligado
|
|
||||||
- Compartilhamento direto via PWA no Chrome do Android
|
|
||||||
|
|
||||||
## 🐳 Docker
|
## 🐳 Instalando em Docker
|
||||||
|
|
||||||
### Antes de começar
|
Instale [Docker e Docker Compose](https://docs.docker.com/engine/install/)
|
||||||
|
|
||||||
Só precisa ter instalado:
|
|
||||||
- Docker e docker compose
|
|
||||||
|
|
||||||
### Produção
|
|
||||||
|
|
||||||
`curl -o ./docker-compose.yml https://raw.githubusercontent.com/manualdousuario/marreta/main/docker-compose.yml`
|
`curl -o ./docker-compose.yml https://raw.githubusercontent.com/manualdousuario/marreta/main/docker-compose.yml`
|
||||||
|
|
||||||
Se necessario
|
Agora modifique com suas preferencias:
|
||||||
|
|
||||||
`nano docker-compose.yml`
|
`nano docker-compose.yml`
|
||||||
|
|
||||||
|
@ -58,182 +51,43 @@ services:
|
||||||
- SITE_NAME=
|
- SITE_NAME=
|
||||||
- SITE_DESCRIPTION=
|
- SITE_DESCRIPTION=
|
||||||
- SITE_URL=
|
- SITE_URL=
|
||||||
|
- LANGUAGE=
|
||||||
```
|
```
|
||||||
|
|
||||||
- `SITE_NAME`: Nome do seu Marreta
|
- `SITE_NAME`: Nome do seu Marreta
|
||||||
- `SITE_DESCRIPTION`: Conta pra que serve
|
- `SITE_DESCRIPTION`: Conta pra que serve
|
||||||
- `SITE_URL`: Onde vai rodar, endereço completo com `https://`. Se você alterar a porta no docker-compose (ex: 8080:80), você também deve incluir a porta no SITE_URL (ex: https://seusite:8080)
|
- `SITE_URL`: Onde vai rodar, endereço completo com `https://`. Se você alterar a porta no docker-compose (ex: 8080:80), você também deve incluir a porta no SITE_URL (ex: https://seusite:8080)
|
||||||
- `DNS_SERVERS`: Quais servidores DNS usar `1.1.1.1, 8.8.8.8`
|
|
||||||
- `SELENIUM_HOST`: Servidor:PORTA do host do Selenium (ex: selenium-hub:4444)
|
- `SELENIUM_HOST`: Servidor:PORTA do host do Selenium (ex: selenium-hub:4444)
|
||||||
-
|
- `LANGUAGE`: pt-br (Português Brasil), en (Inglês), es (Espanhol) ou de-de (Alemão), ru-ru (Russo)
|
||||||
Agora pode rodar `docker compose up -d`
|
|
||||||
|
|
||||||
#### Desenvolvimento
|
Agora só rodar `docker compose up -d`
|
||||||
|
|
||||||
1. Primeiro, clona o projeto:
|
### Mais configurações:
|
||||||
```bash
|
- Selenium: https://github.com/manualdousuario/marreta/wiki/%F0%9F%92%BB-Selenium-Hub-(Chrome-and-Firefox)
|
||||||
git clone https://github.com/manualdousuario/marreta/
|
- Cache S3: https://github.com/manualdousuario/marreta/wiki/%F0%9F%97%83%EF%B8%8F-Cache-S3
|
||||||
cd marreta
|
- Manutenção: https://github.com/manualdousuario/marreta/wiki/%F0%9F%9B%A0%EF%B8%8F-Maintenance
|
||||||
```
|
|
||||||
|
|
||||||
2. Cria o arquivo de configuração:
|
### 🛡️ DMCA
|
||||||
```bash
|
|
||||||
cp app/.env.sample app/.env
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Configura do seu jeito no `app/.env`:
|
Para bloquear dominios por pedidos de DMCA, crie o arquivo `app/cache/dmca_domains.json`:
|
||||||
```env
|
|
||||||
SITE_NAME="Marreta"
|
|
||||||
SITE_DESCRIPTION="Chapéu de paywall é marreta!"
|
|
||||||
SITE_URL=http://localhost
|
|
||||||
DNS_SERVERS=1.1.1.1,8.8.8.8
|
|
||||||
LOG_LEVEL=WARNING
|
|
||||||
SELENIUM_HOST=selenium-hub:4444
|
|
||||||
LANGUAGE=pt-br
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Roda tudo:
|
```json
|
||||||
```bash
|
[
|
||||||
docker-compose up -d
|
{
|
||||||
```
|
"host": "exemplo.com.br",
|
||||||
|
"message": "Este conteúdo foi bloqueado a pedido"
|
||||||
Pronto! Vai estar rodando em `http://localhost` 🎉
|
}
|
||||||
|
]
|
||||||
## ⚙️ Personalizando
|
|
||||||
|
|
||||||
As configurações estão organizadas em `data/`:
|
|
||||||
|
|
||||||
- `domain_rules.php`: Regras específicas para cada site
|
|
||||||
- `global_rules.php`: Regras que se aplicam a todos os sites
|
|
||||||
- `blocked_domains.php`: Lista de sites bloqueados
|
|
||||||
|
|
||||||
### Traduções
|
|
||||||
|
|
||||||
- `/languages/`: Cada lingua está em seu ISO id (`pt-br, en, es ou de-de`) e pode ser definida no environment `LANGUAGE`
|
|
||||||
|
|
||||||
### Cache S3
|
|
||||||
|
|
||||||
Suporte de armazenamento do cache em S3. Configure as seguintes variáveis no seu `.env`:
|
|
||||||
|
|
||||||
```env
|
|
||||||
S3_CACHE_ENABLED=true
|
|
||||||
|
|
||||||
S3_ACCESS_KEY=access_key
|
|
||||||
S3_SECRET_KEY=secret_key
|
|
||||||
S3_BUCKET=nome_do_bucket
|
|
||||||
S3_REGION=us-east-1
|
|
||||||
S3_FOLDER_=cache/
|
|
||||||
S3_ACL=private
|
|
||||||
S3_ENDPOINT=
|
|
||||||
```
|
|
||||||
|
|
||||||
Configurações possiveis:
|
|
||||||
|
|
||||||
```
|
|
||||||
## R2
|
|
||||||
S3_ACCESS_KEY=access_key
|
|
||||||
S3_SECRET_KEY=secret_key
|
|
||||||
S3_BUCKET=nome_do_bucket
|
|
||||||
S3_ENDPOINT=https://{TOKEN}.r2.cloudflarestorage.com
|
|
||||||
S3_REGION=auto
|
|
||||||
S3_FOLDER_=cache/
|
|
||||||
S3_ACL=private
|
|
||||||
|
|
||||||
## DigitalOcean
|
|
||||||
S3_ACCESS_KEY=access_key
|
|
||||||
S3_SECRET_KEY=secret_key
|
|
||||||
S3_BUCKET=nome_do_bucket
|
|
||||||
S3_ENDPOINT=https://{REGIAO}.digitaloceanspaces.com
|
|
||||||
S3_REGION=auto
|
|
||||||
S3_FOLDER_=cache/
|
|
||||||
S3_ACL=private
|
|
||||||
```
|
|
||||||
|
|
||||||
### Integração com Selenium
|
|
||||||
|
|
||||||
Integração com Selenium para processar sites que requerem javascript ou têm algumas barreiras de proteção mais avançadas. Para usar esta funcionalidade, você precisa configurar um ambiente Selenium com Firefox. Adicione a seguinte configuração ao seu `docker-compose.yml`:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
services:
|
|
||||||
selenium-firefox:
|
|
||||||
container_name: selenium-firefox
|
|
||||||
image: selenium/node-firefox:4.27.0-20241204
|
|
||||||
shm_size: 2gb
|
|
||||||
environment:
|
|
||||||
- SE_EVENT_BUS_HOST=selenium-hub
|
|
||||||
- SE_EVENT_BUS_PUBLISH_PORT=4442
|
|
||||||
- SE_EVENT_BUS_SUBSCRIBE_PORT=4443
|
|
||||||
- SE_ENABLE_TRACING=false
|
|
||||||
- SE_NODE_MAX_SESSIONS=10
|
|
||||||
- SE_NODE_OVERRIDE_MAX_SESSIONS=true
|
|
||||||
entrypoint: bash -c 'SE_OPTS="--host $$HOSTNAME" /opt/bin/entry_point.sh'
|
|
||||||
depends_on:
|
|
||||||
- selenium-hub
|
|
||||||
|
|
||||||
selenium-hub:
|
|
||||||
image: selenium/hub:4.27.0-20241204
|
|
||||||
container_name: selenium-hub
|
|
||||||
environment:
|
|
||||||
- SE_ENABLE_TRACING=false
|
|
||||||
- GRID_MAX_SESSION=10
|
|
||||||
- GRID_BROWSER_TIMEOUT=10
|
|
||||||
- GRID_TIMEOUT=10
|
|
||||||
ports:
|
|
||||||
- 4442:4442
|
|
||||||
- 4443:4443
|
|
||||||
- 4444:4444
|
|
||||||
```
|
|
||||||
|
|
||||||
Configurações importantes:
|
|
||||||
- `shm_size`: Define o tamanho da memória compartilhada para o Firefox (2GB recomendado)
|
|
||||||
- `SE_NODE_MAX_SESSIONS`: Número máximo de sessões simultâneas por nó
|
|
||||||
- `GRID_MAX_SESSION`: Número máximo de sessões simultâneas no hub
|
|
||||||
- `GRID_BROWSER_TIMEOUT` e `GRID_TIMEOUT`: Timeouts em segundos
|
|
||||||
|
|
||||||
Após configurar o Selenium, certifique-se de definir a variável `SELENIUM_HOST` no seu ambiente para apontar para o hub do Selenium (geralmente `selenium-hub:4444`).
|
|
||||||
|
|
||||||
### Sistema de Logs
|
|
||||||
|
|
||||||
Os logs são armazenados em `app/logs/app.log` com rotação automática a cada 7 dias.
|
|
||||||
|
|
||||||
Configurações de log disponíveis no `.env` ou docker:
|
|
||||||
|
|
||||||
```env
|
|
||||||
LOG_LEVEL=WARNING
|
|
||||||
```
|
|
||||||
|
|
||||||
Níveis de log disponíveis:
|
|
||||||
- DEBUG: Informações detalhadas para debug
|
|
||||||
- INFO: Informações gerais sobre operações
|
|
||||||
- WARNING: Avisos que merecem atenção (padrão)
|
|
||||||
- ERROR: Erros que não interrompem a operação
|
|
||||||
- CRITICAL: Erros críticos que precisam de atenção imediata
|
|
||||||
|
|
||||||
## 🛠️ Manutenção
|
|
||||||
|
|
||||||
### Logs
|
|
||||||
|
|
||||||
Ver os logs da aplicação:
|
|
||||||
```bash
|
|
||||||
docker-compose logs app
|
|
||||||
# ou diretamente do arquivo de log
|
|
||||||
cat app/logs/app.log
|
|
||||||
```
|
|
||||||
|
|
||||||
### Limpando o cache
|
|
||||||
|
|
||||||
Quando precisar limpar:
|
|
||||||
```bash
|
|
||||||
docker-compose exec app rm -rf /app/cache/*
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🚀 Integrações
|
## 🚀 Integrações
|
||||||
|
|
||||||
- 🤖 **Telegram**: [Bot oficial](https://t.me/leissoai_bot)
|
- 🤖 **Telegram**: [Bot oficial](https://t.me/leissoai_bot)
|
||||||
- 🦊 **Firefox**: Extensão por [Clarissa Mendes](https://claromes.com/pages/whoami) - [Baixar](https://addons.mozilla.org/pt-BR/firefox/addon/marreta/) | [Código fonte](https://github.com/manualdousuario/marreta-extensao)
|
- 🦊 **Firefox**: Extensão por [Clarissa Mendes](https://claromes.com/pages/whoami) - [Baixar](https://addons.mozilla.org/pt-BR/firefox/addon/marreta/) | [Código fonte](https://github.com/manualdousuario/marreta-extensao)
|
||||||
|
- 🌀 **Chrome**: Extensão por [Clarissa Mendes](https://claromes.com/pages/whoami) - [Baixar](https://chromewebstore.google.com/detail/marreta/ipelapagohjgjcgpncpbmaaacemafppe) | [Código fonte](https://github.com/manualdousuario/marreta-extensao)
|
||||||
- 🦋 **Bluesky**: Bot por [Joselito](https://bsky.app/profile/joseli.to) - [Perfil](https://bsky.app/profile/marreta.pcdomanual.com) | [Código fonte](https://github.com/manualdousuario/marreta-bot)
|
- 🦋 **Bluesky**: Bot por [Joselito](https://bsky.app/profile/joseli.to) - [Perfil](https://bsky.app/profile/marreta.pcdomanual.com) | [Código fonte](https://github.com/manualdousuario/marreta-bot)
|
||||||
- 🍎 **Apple**: Integração ao [Atalhos](https://www.icloud.com/shortcuts/3594074b69ee4707af52ed78922d624f)
|
- 🍎 **Apple**: Integração ao [Atalhos](https://www.icloud.com/shortcuts/3594074b69ee4707af52ed78922d624f)
|
||||||
-
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Feito com ❤️! Se tiver dúvidas ou sugestões, abre uma issue que a gente ajuda! 😉
|
Feito com ❤️! Se tiver dúvidas ou sugestões, abre uma issue que a gente ajuda! 😉
|
||||||
|
|
|
@ -1,73 +0,0 @@
|
||||||
# Testados/Validos:
|
|
||||||
## Brasil
|
|
||||||
https://www1.folha.uol.com.br/poder/2024/11/justica-argentina-emite-mandados-de-prisao-contra-61-foragidos-do-81.shtml
|
|
||||||
https://g1.globo.com/politica/noticia/2024/11/20/pf-devera-concluir-inquerito-contra-atos-do-8-de-janeiro-nesta-semana.ghtml
|
|
||||||
https://www.estadao.com.br/politica/planalto-entregou-para-pf-acesso-a-computadores-de-grupo-que-tentou-matar-lula-alckmin-e-moraes/
|
|
||||||
https://www.correio24horas.com.br/minha-bahia/bahia-registra-primeira-morte-por-coqueluche-em-cinco-anos-1124
|
|
||||||
https://correio.rac.com.br/campinasermc/iphan-analisa-pedido-de-tombamento-emergencial-da-fazenda-santa-elisa-1.1591950
|
|
||||||
https://veja.abril.com.br/politica/pt-pede-arquivamento-de-projeto-de-anistia-apos-pf-revelar-plano-para-matar-lula/
|
|
||||||
https://super.abril.com.br/cultura/consciencia-negra-conheca-os-adinkras-simbolos-africanos-que-estao-no-seu-cotidiano/
|
|
||||||
https://quatrorodas.abril.com.br/noticias/latinncap-c3aircross-corolla-teste-seguranca/#google_vignette
|
|
||||||
https://www.abcmais.com/policia/casa-e-destruida-por-incendio-em-novo-hamburgo-2/
|
|
||||||
https://www.agazeta.com.br/es/cotidiano/negros-sao-a-maioria-entre-pobres-e-analfabetos-no-es-aponta-estudo-1124
|
|
||||||
https://www.gazetadopovo.com.br/republica/moraes-nao-tem-condicoes-de-julgar-suposto-plano-golpista-diz-rogerio-marinho/
|
|
||||||
https://revistagalileu.globo.com/colunistas/the-conversation/noticia/2024/11/por-que-nos-sentimos-melhor-quando-acordamos-sem-despertador-neurologista-explica.ghtml
|
|
||||||
https://oglobo.globo.com/epoca/noticia/2024/09/07/moeda-rara-do-seculo-xviii-encontrada-dentro-de-lata-de-caramelo-sera-leiloada-na-inglaterra-e-pode-valer-ate-r-220-mil.ghtml
|
|
||||||
https://valor.globo.com/financas/esg/noticia/2024/11/18/anbima-propoe-novas-regras-para-ofertas-de-titulos-de-renda-fixa-sustentaveis.ghtml
|
|
||||||
https://www.seudinheiro.com/2024/patrocinado/conteudo-eqi/fii-arrecada-r-137-milhoes-com-venda-de-galpoes-e-e-recomendado-por-analistas-veja/
|
|
||||||
https://www.nsctotal.com.br/noticias/veja-os-bairros-mais-populosos-de-florianopolis-segundo-o-ibge
|
|
||||||
https://exame.com/invest/mercados/nvidia-bate-consenso-mais-uma-vez-e-reporta-lucro-de-us-193-bilhoes-no-terceiro-trimestre/
|
|
||||||
https://www.dgabc.com.br/Noticia/4177263/casa-de-acolhimento-a-mulheres-vitimas-de-violencia-e-invadida-em-sto-andre
|
|
||||||
https://diarinho.net/materia/657650/Gasolina-subiu-mais-de-R--0-50-desde-janeiro--veja-os-precos-da-pesquisa-
|
|
||||||
https://www.em.com.br/gerais/2024/11/6993175-incendio-fecha-shopping-na-regiao-centro-sul-de-bh.html
|
|
||||||
https://mais.opovo.com.br/colunistas/ariadne-araujo/2024/11/20/quem-quer-casar-com-um-heroi-de-guerra-ucraniano.html
|
|
||||||
https://www.folhadelondrina.com.br/folha-2/cena-musical-em-londrina-idosos-no-palco-e-na-plateia-3267044e.html?d=1
|
|
||||||
https://www.uol.com.br/esporte/futebol/ultimas-noticias/2024/11/20/bahia-x-palmeiras-campeonato-brasileiro-2024-rodada-34.htm
|
|
||||||
https://www.opovo.com.br/noticias/politica/2024/11/21/atuacao-de-faccoes-nas-eleicoes-teve-compra-de-votos-e-ate-enviado-do-rj-para-intimidacoes-no-ceara.html
|
|
||||||
https://flatout.com.br/o-impossivel-acidente-de-dan-wheldon-uma-analise-medica/
|
|
||||||
https://tab.uol.com.br/noticias/redacao/2024/11/27/roubos-suicidios-e-assassinatos-casos-extremos-do-vicio-em-apostas.htm
|
|
||||||
https://www.jota.info/coberturas-especiais/g20-brasil/presidente-sul-africano-quer-modelo-de-consenso-amplo-para-g20-na-era-trump
|
|
||||||
https://quatrorodas.abril.com.br/noticias/chinesa-gac-investe-r-120-milhoes-para-produzir-motor-hibrido-flex-no-brasil/
|
|
||||||
https://www.intercept.com.br/2024/12/05/uerj-curso-brasil-paralelo/
|
|
||||||
https://www.agazeta.com.br/concursos-e-empregos/concursos/prefeitura-de-cariacica-vai-abrir-concurso-com-salario-de-ate-r-78-mil-1224
|
|
||||||
https://natelinha.uol.com.br/televisao/2024/12/05/boninho-fecha-com-o-sbt-para-novo-reality-show-219855.php
|
|
||||||
https://gamarevista.uol.com.br/semana/deu-vontade-de-ter-outra-vida/novas-formas-abandonar/
|
|
||||||
https://tecnoblog.net/noticias/cor-do-ano-esta-em-celulares-da-motorola-que-serao-vendidos-no-brasil/
|
|
||||||
https://gauchazh.clicrbs.com.br/pioneiro/policia/noticia/2024/11/caxias-do-sul-podera-fazer-emprestimo-de-ate-us-40-milhoes-para-melhorias-tecnologicas-em-educacao-seguranca-e-servicos-municipais-cm3q9yn870051014fzz77djqz.html
|
|
||||||
https://diplomatique.org.br/saude-unica-em-tempos-de-crise-democratica/
|
|
||||||
https://oantagonista.com.br/brasil/lewandowski-insiste-na-pec-da-seguranca/
|
|
||||||
https://jornaldebrasilia.com.br/noticias/politica-e-poder/lula-aguarda-pt-para-troca-em-pastas-chefiadas-por-petistas-em-reforma-ministerial/
|
|
||||||
https://opopular.com.br/cidades/ex-secretario-de-saude-de-goiania-deixa-hospital-e-volta-para-a-cadeia-1.3207162
|
|
||||||
https://www.cartacapital.com.br/politica/surpresa-natalina/
|
|
||||||
https://seucreditodigital.com.br/123milhas-devera-apresentar-plano-de-recuperacao-ainda-este-mes/
|
|
||||||
https://www.matinaljornalismo.com.br/matinal/reportagem-matinal/vazao-guaiba-porto-alegre/
|
|
||||||
|
|
||||||
## Internacional
|
|
||||||
https://www.nytimes.com/2024/11/20/us/politics/matt-gaetz-venmo-payments-sex.html
|
|
||||||
https://www.ft.com/content/cab2e834-e5fa-4dd0-b757-76c243ac2eeb
|
|
||||||
https://foreignpolicy.com/2024/11/20/trump-energy-policy-electric-vehicles-power-grid-infrastructure-oil-gas-climate/
|
|
||||||
https://www.wired.com/story/waymo-robotaxi-driverless-future/
|
|
||||||
https://www.forbes.com/sites/cyrusfarivar/2024/11/20/under-trump-tariffs-made-in-vietnam-will-be-the-new-made-in-china/
|
|
||||||
https://observador.pt/especiais/mais-formacao-investimento-na-frota-reforco-dos-tecnicos-de-emergencia-e-autonomia-o-que-precisa-o-inem-segundo-varios-especialistas/
|
|
||||||
https://www.businessinsider.com/bluesky-user-growth-social-coo-servers-twitter-elon-musk-x-2024-11
|
|
||||||
https://cooking.nytimes.com/recipes/1016583-perfect-instant-ramen?smid=bsky-nytimes
|
|
||||||
https://www.latercera.com/nacional/noticia/gracias-por-arruinar-mi-vida-maldito-el-contenido-de-los-25-correos-que-boric-entrego-a-fiscal-que-investiga-denuncia/HL44T5EN55H6DHADGMORNCCNBY/
|
|
||||||
https://www.liberation.fr/culture/cinema/les-nouveaux-films-de-monstres-de-plus-en-plus-repugnangnan-20241106_BMQANPKL3FEPNLFIAXP5HIUSIQ/
|
|
||||||
https://www.haaretz.com/israel-news/2024-12-04/ty-article-live/idf-claims-hezbollah-liaison-to-syrian-army-killed-in-damascus-airstrike/00000193-8fa5-d2e5-a9b3-bfffbbc70000
|
|
||||||
https://www.haaretz.co.il/news/politics/2024-11-20/ty-article/.premium/00000193-4b4c-d383-abbb-ebffe90d0000
|
|
||||||
https://www.ajc.com/news/crime/six-more-ysl-defendants-may-slip-charges/CWUGV7CVHRCMZGDFSWEMQLLGWM/
|
|
||||||
https://expresso.pt/arquivo/Expresso/Economia/2024-12-05-tap-esta-num-ano-extremamente-positivo-mas-luis-rodrigues-anteve-desafios-significativos-para-2025-5f0d0733
|
|
||||||
https://www.foreignaffairs.com/india/india-hoping-trump-bump
|
|
||||||
https://www.latercera.com/la-tercera-pm/noticia/duro-reves-al-nuevo-criterio-de-dorothy-perez-en-la-antesala-de-cambios-de-equipos-municipales-y-ceses-de-contrata-la-corte-manda-mensaje-a-contralora/G5ADQKPSSBBANAGDVK6TETP3RU/
|
|
||||||
https://www.wired.com/story/operation-destabilise-money-laundering/
|
|
||||||
https://reuters.com/world/us/internet-sleuths-hunt-clues-murder-unitedhealths-brian-thompson-2024-12-05/
|
|
||||||
https://www.washingtonpost.com/world/2024/12/03/martial-law-south-korea-explained/
|
|
||||||
https://www.npr.org/2024/12/06/nx-s1-5219927/unitedhealthcare-ceo-shooting-new-york
|
|
||||||
https://www.bloomberg.com/news/articles/2024-12-07/south-korea-impeachment-vote-drags-on-after-ruling-party-boycott?srnd=homepage-americas
|
|
||||||
https://www.leparisien.fr/politique/le-gouvernement-barnier-censure-emmanuel-macron-de-nouveau-face-a-une-impasse-05-12-2024-KNZVD5MGOJGIPEONBQEYSACOLI.php
|
|
||||||
https://www.businessinsider.com/google-unveiled-quantum-computer-chip-willow-2024-12
|
|
||||||
https://theverge.com/2024/12/11/24318164/lord-of-the-rings-war-of-the-rohirrim-review
|
|
||||||
https://www.sabado.pt/portugal/detalhe/lider-sindical-da-psp-apanhado-numa-investigacao-da-pj
|
|
||||||
https://www.cmjornal.pt/portugal/detalhe/motorista-da-aeroporto-parque-destroi-carro-de-colecao-de-cliente-e-quer-pagar-tuta-e-meia
|
|
||||||
https://www.nzherald.co.nz/entertainment/nz-theatre-legend-tim-bray-on-cancer-chemo-and-the-man-whos-holding-him-together/ESPBIQ7YEFGEBMGPWPB6XBEY5U/
|
|
||||||
https://www.elcorreo.com/athletic/uriarte-estalla-olmo-alucinante-deco-responde-preocupe-20250108203120-nt.html
|
|
|
@ -1,32 +1,33 @@
|
||||||
# Arquivo de exemplo para configuração de variáveis de ambiente
|
# Sample file for environment variable configuration
|
||||||
# Copie este arquivo para .env e ajuste os valores conforme necessário
|
# Copy this file to .env and adjust the values as needed
|
||||||
|
|
||||||
# Nome do site exibido no cabeçalho e meta tags
|
# Site name displayed in the header and meta tags
|
||||||
SITE_NAME=Marreta
|
SITE_NAME=Marreta
|
||||||
|
|
||||||
# Descrição do site usada em meta tags e SEO
|
# Site description used in meta tags and SEO
|
||||||
SITE_DESCRIPTION="Chapéu de paywall é marreta!"
|
SITE_DESCRIPTION="Chapéu de paywall é marreta!"
|
||||||
|
|
||||||
# Idioma do site (opções disponíveis: pt-br, en, es, de-de)
|
# Site language (available options: pt-br, en, es, de-de, ru-ru)
|
||||||
# pt-br = Português do Brasil
|
# pt-br = Brazilian Portuguese
|
||||||
# en = English
|
# en = English
|
||||||
# es = Español
|
# es = Spanish
|
||||||
# de-de = German
|
# de-de = German
|
||||||
|
# ru-ru = Russian
|
||||||
LANGUAGE=pt-br
|
LANGUAGE=pt-br
|
||||||
|
|
||||||
# URL base do site (sem barra no final)
|
# Base URL of the site (without a trailing slash)
|
||||||
# Use https://localhost para desenvolvimento local
|
# Use https://localhost for local development
|
||||||
SITE_URL=https://localhost
|
SITE_URL=https://localhost
|
||||||
|
|
||||||
# Lista de servidores DNS para resolução de domínios
|
# List of DNS servers for domain resolution
|
||||||
# Recomendado: AdGuard DNS (94.140.14.14, 94.140.15.15)
|
# Recommended: AdGuard DNS (94.140.14.14, 94.140.15.15)
|
||||||
DNS_SERVERS='94.140.14.14,94.140.15.15'
|
DNS_SERVERS='94.140.14.14,94.140.15.15'
|
||||||
|
|
||||||
# Modo sem cache (true/false)
|
# Disable cache mode (true/false)
|
||||||
# Quando ativo, desativa o cache do sistema
|
# When enabled, system caching is turned off
|
||||||
DISABLE_CACHE=false
|
DISABLE_CACHE=false
|
||||||
|
|
||||||
# Configurações de Cache S3
|
# S3 Cache Settings
|
||||||
S3_CACHE_ENABLED=false
|
S3_CACHE_ENABLED=false
|
||||||
S3_ACCESS_KEY=
|
S3_ACCESS_KEY=
|
||||||
S3_SECRET_KEY=
|
S3_SECRET_KEY=
|
||||||
|
@ -36,8 +37,21 @@ S3_FOLDER=cache/
|
||||||
S3_ACL=private
|
S3_ACL=private
|
||||||
S3_ENDPOINT=
|
S3_ENDPOINT=
|
||||||
|
|
||||||
# Configurações do Selenium
|
# Selenium Configuration
|
||||||
SELENIUM_HOST=localhost:4444
|
SELENIUM_HOST=localhost:4444
|
||||||
|
|
||||||
# Configurações de Debug
|
# Debug Settings
|
||||||
DEBUG=true
|
DEBUG=false
|
||||||
|
|
||||||
|
# Cache Cleanup Settings
|
||||||
|
# Number of days to keep cache files (*.gz)
|
||||||
|
# If not set, no files will be cleaned
|
||||||
|
CLEANUP_DAYS=7
|
||||||
|
|
||||||
|
# Proxy List Configuration
|
||||||
|
# URL to download proxy list from (used by bin/proxy script)
|
||||||
|
# The proxy list should contain proxies in one of these formats:
|
||||||
|
# 1. http://USER:PASSWORD@HOST:PORT
|
||||||
|
# 2. IP:PORT:USER:PASSWORD
|
||||||
|
# Example: PROXY_LIST=https://example.com/proxy-list.txt
|
||||||
|
PROXY_LIST=
|
||||||
|
|
114
app/Gulpfile.js
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const gulp = require('gulp');
|
||||||
|
const sass = require('gulp-sass')(require('sass'));
|
||||||
|
const concat = require('gulp-concat');
|
||||||
|
const uglify = require('gulp-uglify');
|
||||||
|
const clean_css = require('gulp-clean-css');
|
||||||
|
const sourcemaps = require('gulp-sourcemaps');
|
||||||
|
const imagemin = require('gulp-imagemin');
|
||||||
|
const webp = require('gulp-webp');
|
||||||
|
const newer = require('gulp-newer');
|
||||||
|
const rename = require('gulp-rename');
|
||||||
|
const ttf2woff = require('gulp-ttf2woff');
|
||||||
|
const ttf2woff2 = require('gulp-ttf2woff2');
|
||||||
|
const ttf2eot = require('gulp-ttf2eot');
|
||||||
|
const svgmin = require('gulp-svgmin');
|
||||||
|
|
||||||
|
const paths = {
|
||||||
|
styles: {
|
||||||
|
src: 'assets/scss/*.scss',
|
||||||
|
dest: 'dist/css'
|
||||||
|
},
|
||||||
|
scripts: {
|
||||||
|
src: 'assets/js/*.js',
|
||||||
|
dest: 'dist/js'
|
||||||
|
},
|
||||||
|
images: {
|
||||||
|
src: 'assets/images/**/*',
|
||||||
|
dest: 'dist/images'
|
||||||
|
},
|
||||||
|
fonts: {
|
||||||
|
src: 'assets/fonts/**/*.ttf',
|
||||||
|
dest: 'dist/fonts'
|
||||||
|
},
|
||||||
|
icons: {
|
||||||
|
src: 'assets/icons/**/*.svg',
|
||||||
|
dest: 'dist/icons'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function styles() {
|
||||||
|
return gulp.src(paths.styles.src)
|
||||||
|
.pipe(sourcemaps.init())
|
||||||
|
.pipe(sass({
|
||||||
|
outputStyle: "expanded",
|
||||||
|
includePaths: ['./node_modules']
|
||||||
|
}))
|
||||||
|
.pipe(concat('style.css'))
|
||||||
|
.pipe(clean_css())
|
||||||
|
.pipe(gulp.dest(paths.styles.dest))
|
||||||
|
.pipe(sourcemaps.write('.'))
|
||||||
|
.pipe(gulp.dest(paths.styles.dest))
|
||||||
|
}
|
||||||
|
|
||||||
|
function scripts() {
|
||||||
|
return gulp.src(paths.scripts.src)
|
||||||
|
.pipe(sourcemaps.init())
|
||||||
|
.pipe(concat('scripts.js'))
|
||||||
|
.pipe(uglify())
|
||||||
|
.pipe(gulp.dest(paths.scripts.dest))
|
||||||
|
.pipe(sourcemaps.write('.'))
|
||||||
|
.pipe(gulp.dest(paths.scripts.dest))
|
||||||
|
}
|
||||||
|
|
||||||
|
function images() {
|
||||||
|
return gulp.src(paths.images.src)
|
||||||
|
.pipe(newer(paths.images.dest))
|
||||||
|
.pipe(imagemin())
|
||||||
|
.pipe(gulp.dest(paths.images.dest))
|
||||||
|
.pipe(webp())
|
||||||
|
.pipe(gulp.dest(paths.images.dest))
|
||||||
|
}
|
||||||
|
|
||||||
|
function icons() {
|
||||||
|
return gulp.src(paths.icons.src)
|
||||||
|
.pipe(newer(paths.icons.dest))
|
||||||
|
.pipe(svgmin())
|
||||||
|
.pipe(gulp.dest(paths.icons.dest))
|
||||||
|
}
|
||||||
|
|
||||||
|
function fonts() {
|
||||||
|
return gulp.src(paths.fonts.src)
|
||||||
|
.pipe(newer(paths.fonts.dest))
|
||||||
|
|
||||||
|
.pipe(ttf2woff())
|
||||||
|
.pipe(rename({ extname: '.woff' }))
|
||||||
|
.pipe(gulp.dest(paths.fonts.dest))
|
||||||
|
|
||||||
|
.pipe(gulp.src(paths.fonts.src))
|
||||||
|
.pipe(ttf2woff2())
|
||||||
|
.pipe(rename({ extname: '.woff2' }))
|
||||||
|
.pipe(gulp.dest(paths.fonts.dest))
|
||||||
|
|
||||||
|
.pipe(gulp.src(paths.fonts.src))
|
||||||
|
.pipe(ttf2eot())
|
||||||
|
.pipe(rename({ extname: '.eot' }))
|
||||||
|
.pipe(gulp.dest(paths.fonts.dest))
|
||||||
|
|
||||||
|
.pipe(gulp.src(paths.fonts.src))
|
||||||
|
.pipe(gulp.dest(paths.fonts.dest));
|
||||||
|
}
|
||||||
|
|
||||||
|
function watch() {
|
||||||
|
gulp.watch(paths.styles.src, styles);
|
||||||
|
gulp.watch(paths.scripts.src, scripts);
|
||||||
|
gulp.watch(paths.images.src, images);
|
||||||
|
gulp.watch(paths.fonts.src, fonts);
|
||||||
|
gulp.watch(paths.icons.src, icons);
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.default = gulp.series(
|
||||||
|
gulp.parallel(styles, scripts, images, fonts, icons),
|
||||||
|
watch
|
||||||
|
);
|
0
app/assets/.dockerignore
Normal file
BIN
app/assets/fonts/inter-500.ttf
Normal file
BIN
app/assets/fonts/inter-600.ttf
Normal file
BIN
app/assets/fonts/unna-400.ttf
Normal file
3
app/assets/icons/android.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-android2" viewBox="0 0 16 16">
|
||||||
|
<path d="m10.213 1.471.691-1.26q.069-.124-.048-.192-.128-.057-.195.058l-.7 1.27A4.8 4.8 0 0 0 8.005.941q-1.032 0-1.956.404l-.7-1.27Q5.281-.037 5.154.02q-.117.069-.049.193l.691 1.259a4.25 4.25 0 0 0-1.673 1.476A3.7 3.7 0 0 0 3.5 5.02h9q0-1.125-.623-2.072a4.27 4.27 0 0 0-1.664-1.476ZM6.22 3.303a.37.37 0 0 1-.267.11.35.35 0 0 1-.263-.11.37.37 0 0 1-.107-.264.37.37 0 0 1 .107-.265.35.35 0 0 1 .263-.11q.155 0 .267.11a.36.36 0 0 1 .112.265.36.36 0 0 1-.112.264m4.101 0a.35.35 0 0 1-.262.11.37.37 0 0 1-.268-.11.36.36 0 0 1-.112-.264q0-.154.112-.265a.37.37 0 0 1 .268-.11q.155 0 .262.11a.37.37 0 0 1 .107.265q0 .153-.107.264M3.5 11.77q0 .441.311.75.311.306.76.307h.758l.01 2.182q0 .414.292.703a.96.96 0 0 0 .7.288.97.97 0 0 0 .71-.288.95.95 0 0 0 .292-.703v-2.182h1.343v2.182q0 .414.292.703a.97.97 0 0 0 .71.288.97.97 0 0 0 .71-.288.95.95 0 0 0 .292-.703v-2.182h.76q.436 0 .749-.308.31-.307.311-.75V5.365h-9zm10.495-6.587a.98.98 0 0 0-.702.278.9.9 0 0 0-.293.685v4.063q0 .406.293.69a.97.97 0 0 0 .702.284q.42 0 .712-.284a.92.92 0 0 0 .293-.69V6.146a.9.9 0 0 0-.293-.685 1 1 0 0 0-.712-.278m-12.702.283a1 1 0 0 1 .712-.283q.41 0 .702.283a.9.9 0 0 1 .293.68v4.063a.93.93 0 0 1-.288.69.97.97 0 0 1-.707.284 1 1 0 0 1-.712-.284.92.92 0 0 1-.293-.69V6.146q0-.396.293-.68"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
4
app/assets/icons/apple.svg
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-apple" viewBox="0 0 16 16">
|
||||||
|
<path d="M11.182.008C11.148-.03 9.923.023 8.857 1.18c-1.066 1.156-.902 2.482-.878 2.516s1.52.087 2.475-1.258.762-2.391.728-2.43m3.314 11.733c-.048-.096-2.325-1.234-2.113-3.422s1.675-2.789 1.698-2.854-.597-.79-1.254-1.157a3.7 3.7 0 0 0-1.563-.434c-.108-.003-.483-.095-1.254.116-.508.139-1.653.589-1.968.607-.316.018-1.256-.522-2.267-.665-.647-.125-1.333.131-1.824.328-.49.196-1.422.754-2.074 2.237-.652 1.482-.311 3.83-.067 4.56s.625 1.924 1.273 2.796c.576.984 1.34 1.667 1.659 1.899s1.219.386 1.843.067c.502-.308 1.408-.485 1.766-.472.357.013 1.061.154 1.782.539.571.197 1.111.115 1.652-.105.541-.221 1.324-1.059 2.238-2.758q.52-1.185.473-1.282"/>
|
||||||
|
<path d="M11.182.008C11.148-.03 9.923.023 8.857 1.18c-1.066 1.156-.902 2.482-.878 2.516s1.52.087 2.475-1.258.762-2.391.728-2.43m3.314 11.733c-.048-.096-2.325-1.234-2.113-3.422s1.675-2.789 1.698-2.854-.597-.79-1.254-1.157a3.7 3.7 0 0 0-1.563-.434c-.108-.003-.483-.095-1.254.116-.508.139-1.653.589-1.968.607-.316.018-1.256-.522-2.267-.665-.647-.125-1.333.131-1.824.328-.49.196-1.422.754-2.074 2.237-.652 1.482-.311 3.83-.067 4.56s.625 1.924 1.273 2.796c.576.984 1.34 1.667 1.659 1.899s1.219.386 1.843.067c.502-.308 1.408-.485 1.766-.472.357.013 1.061.154 1.782.539.571.197 1.111.115 1.652-.105.541-.221 1.324-1.059 2.238-2.758q.52-1.185.473-1.282"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
1
app/assets/icons/bookmark.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path fill="black" d="M0 48C0 21.5 21.5 0 48 0l0 48V441.4l130.1-92.9c8.3-6 19.6-6 27.9 0L336 441.4V48H48V0H336c26.5 0 48 21.5 48 48V488c0 9-5 17.2-13 21.3s-17.6 3.4-24.9-1.8L192 397.5 37.9 507.5c-7.3 5.2-16.9 5.9-24.9 1.8S0 497 0 488V48z"/></svg>
|
After Width: | Height: | Size: 309 B |
1
app/assets/icons/bsky.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 568 501"><path fill="currentColor" d="M123.121 33.664C188.241 82.553 258.281 181.68 284 234.873c25.719-53.192 95.759-152.32 160.879-201.21C491.866-1.611 568-28.906 568 57.947c0 17.346-9.945 145.713-15.778 166.555-20.275 72.453-94.155 90.933-159.875 79.748C507.222 323.8 536.444 388.56 473.333 453.32c-119.86 122.992-172.272-30.859-185.702-70.281-2.462-7.227-3.614-10.608-3.631-7.733-.017-2.875-1.169.506-3.631 7.733-13.43 39.422-65.842 193.273-185.702 70.281-63.111-64.76-33.89-129.52 80.986-149.071-65.72 11.185-139.6-7.295-159.875-79.748C9.945 203.659 0 75.291 0 57.946 0-28.906 76.135-1.612 123.121 33.664Z"></path></svg>
|
After Width: | Height: | Size: 677 B |
3
app/assets/icons/chrome.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-browser-chrome" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M16 8a8 8 0 0 1-7.022 7.94l1.902-7.098a3 3 0 0 0 .05-1.492A3 3 0 0 0 10.237 6h5.511A8 8 0 0 1 16 8M0 8a8 8 0 0 0 7.927 8l1.426-5.321a3 3 0 0 1-.723.255 3 3 0 0 1-1.743-.147 3 3 0 0 1-1.043-.7L.633 4.876A8 8 0 0 0 0 8m5.004-.167L1.108 3.936A8.003 8.003 0 0 1 15.418 5H8.066a3 3 0 0 0-1.252.243 2.99 2.99 0 0 0-1.81 2.59M8 10a2 2 0 1 0 0-4 2 2 0 0 0 0 4"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 525 B |
3
app/assets/icons/close.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-x-lg" viewBox="0 0 16 16">
|
||||||
|
<path d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 308 B |
1
app/assets/icons/error.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="black" d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336h80c13.3 0 24-10.7 24-24s-10.7-24-24-24h-80c-13.3 0-24 10.7-24 24s10.7 24 24 24zm-24-72h128c13.3 0 24-10.7 24-24s-10.7-24-24-24H192c-13.3 0-24 10.7-24 24s10.7 24 24 24z"/></svg>
|
After Width: | Height: | Size: 319 B |
3
app/assets/icons/firefox.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-browser-firefox" viewBox="0 0 16 16">
|
||||||
|
<path d="M13.384 3.408c.535.276 1.22 1.152 1.556 1.963a8 8 0 0 1 .503 3.897l-.009.077-.026.224A7.758 7.758 0 0 1 .006 8.257v-.04q.025-.545.114-1.082c.01-.074.075-.42.09-.489l.01-.051a6.6 6.6 0 0 1 1.041-2.35q.327-.465.725-.87.35-.358.758-.65a1.5 1.5 0 0 1 .26-.137c-.018.268-.04 1.553.268 1.943h.003a5.7 5.7 0 0 1 1.868-1.443 3.6 3.6 0 0 0 .021 1.896q.105.07.2.152c.107.09.226.207.454.433l.068.066.009.009a2 2 0 0 0 .213.18c.383.287.943.563 1.306.741.201.1.342.168.359.193l.004.008c-.012.193-.695.858-.933.858-2.206 0-2.564 1.335-2.564 1.335.087.997.714 1.839 1.517 2.357a4 4 0 0 0 .439.241q.114.05.228.094c.325.115.665.18 1.01.194 3.043.143 4.155-2.804 3.129-4.745v-.001a3 3 0 0 0-.731-.9 3 3 0 0 0-.571-.37l-.003-.002a2.68 2.68 0 0 1 1.87.454 3.92 3.92 0 0 0-3.396-1.983q-.116.001-.23.01l-.042.003V4.31h-.002a4 4 0 0 0-.8.14 7 7 0 0 0-.333-.314 2 2 0 0 0-.2-.152 4 4 0 0 1-.088-.383 5 5 0 0 1 1.352-.289l.05-.003c.052-.004.125-.01.205-.012C7.996 2.212 8.733.843 10.17.002l-.003.005.003-.001.002-.002h.002l.002-.002h.015a.02.02 0 0 1 .012.007 2.4 2.4 0 0 0 .206.48q.09.153.183.297c.49.774 1.023 1.379 1.543 1.968.771.874 1.512 1.715 2.036 3.02l-.001-.013a8 8 0 0 0-.786-2.353"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
3
app/assets/icons/hamburguer.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-list" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5m0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5m0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 341 B |
1
app/assets/icons/link.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="black" d="M579.8 267.7c56.5-56.5 56.5-148 0-204.5c-50-50-128.8-56.5-186.3-15.4l-1.6 1.1c-14.4 10.3-17.7 30.3-7.4 44.6s30.3 17.7 44.6 7.4l1.6-1.1c32.1-22.9 76-19.3 103.8 8.6c31.5 31.5 31.5 82.5 0 114L422.3 334.8c-31.5 31.5-82.5 31.5-114 0c-27.9-27.9-31.5-71.8-8.6-103.8l1.1-1.6c10.3-14.4 6.9-34.4-7.4-44.6s-34.4-6.9-44.6 7.4l-1.1 1.6C206.5 251.2 213 330 263 380c56.5 56.5 148 56.5 204.5 0L579.8 267.7zM60.2 244.3c-56.5 56.5-56.5 148 0 204.5c50 50 128.8 56.5 186.3 15.4l1.6-1.1c14.4-10.3 17.7-30.3 7.4-44.6s-30.3-17.7-44.6-7.4l-1.6 1.1c-32.1 22.9-76 19.3-103.8-8.6C74 372 74 321 105.5 289.5L217.7 177.2c31.5-31.5 82.5-31.5 114 0c27.9 27.9 31.5 71.8 8.6 103.9l-1.1 1.6c-10.3 14.4-6.9 34.4 7.4 44.6s34.4 6.9 44.6-7.4l1.1-1.6C433.5 260.8 427 182 377 132c-56.5-56.5-148-56.5-204.5 0L60.2 244.3z"/></svg>
|
After Width: | Height: | Size: 872 B |
1
app/assets/icons/marreta.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="black" d="M283.9 378.6l18.3-60.1c18-4.1 34.2-16 43.1-33.8l64-128c10.5-21.1 8.4-45.2-3.7-63.6l52.7-76.6c3.7-5.4 10.4-8 16.7-6.5s11.2 6.7 12.2 13.1l16.2 104.1 105.1-7.4c6.5-.5 12.7 3.1 15.5 9s1.8 12.9-2.6 17.8L550.1 224l71.3 77.5c4.4 4.8 5.5 11.9 2.6 17.8s-9 9.5-15.5 9l-105.1-7.4L487.3 425c-1 6.5-5.9 11.7-12.2 13.1s-13-1.1-16.7-6.5l-59.7-86.7-91.4 52.2c-5.7 3.3-12.8 2.7-17.9-1.4s-7.2-10.9-5.3-17.2zm28.3-101.7c-9.3 10.9-25.2 14.4-38.6 7.7l-65.9-32.9s0 0 0 0L122 208.8s0 0 0 0L17.7 156.6C1.9 148.7-4.5 129.5 3.4 113.7l40-80C48.8 22.8 59.9 16 72 16l120 0c5 0 9.9 1.2 14.3 3.4l78.2 39.1 81.8 40.9c15.8 7.9 22.2 27.1 14.3 42.9l-64 128c-1.2 2.4-2.7 4.6-4.4 6.6zM107.6 237.4l85.9 42.9L90.9 485.5c-11.9 23.7-40.7 33.3-64.4 21.5S-6.8 466.2 5.1 442.5L107.6 237.4z"/></svg>
|
After Width: | Height: | Size: 838 B |
3
app/assets/icons/moon.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-moon-fill" viewBox="0 0 16 16">
|
||||||
|
<path d="M6 .278a.77.77 0 0 1 .08.858 7.2 7.2 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277q.792-.001 1.533-.16a.79.79 0 0 1 .81.316.73.73 0 0 1-.031.893A8.35 8.35 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.75.75 0 0 1 6 .278"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 394 B |
1
app/assets/icons/refresh.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="black" d="M105.1 202.6c7.7-21.8 20.2-42.3 37.8-59.8c62.5-62.5 163.8-62.5 226.3 0L386.3 160 352 160c-17.7 0-32 14.3-32 32s14.3 32 32 32l111.5 0c0 0 0 0 0 0l.4 0c17.7 0 32-14.3 32-32l0-112c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 35.2L414.4 97.6c-87.5-87.5-229.3-87.5-316.8 0C73.2 122 55.6 150.7 44.8 181.4c-5.9 16.7 2.9 34.9 19.5 40.8s34.9-2.9 40.8-19.5zM39 289.3c-5 1.5-9.8 4.2-13.7 8.2c-4 4-6.7 8.8-8.1 14c-.3 1.2-.6 2.5-.8 3.8c-.3 1.7-.4 3.4-.4 5.1L16 432c0 17.7 14.3 32 32 32s32-14.3 32-32l0-35.1 17.6 17.5c0 0 0 0 0 0c87.5 87.4 229.3 87.4 316.7 0c24.4-24.4 42.1-53.1 52.9-83.8c5.9-16.7-2.9-34.9-19.5-40.8s-34.9 2.9-40.8 19.5c-7.7 21.8-20.2 42.3-37.8 59.8c-62.5 62.5-163.8 62.5-226.3 0l-.1-.1L125.6 352l34.4 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L48.4 288c-1.6 0-3.2 .1-4.8 .3s-3.1 .5-4.6 1z"/></svg>
|
After Width: | Height: | Size: 873 B |
3
app/assets/icons/sun.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-brightness-high-fill" viewBox="0 0 16 16">
|
||||||
|
<path d="M12 8a4 4 0 1 1-8 0 4 4 0 0 1 8 0M8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0m0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13m8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5M3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8m10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0m-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0m9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707M4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 791 B |
3
app/assets/icons/telegram.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-telegram" viewBox="0 0 16 16">
|
||||||
|
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M8.287 5.906q-1.168.486-4.666 2.01-.567.225-.595.442c-.03.243.275.339.69.47l.175.055c.408.133.958.288 1.243.294q.39.01.868-.32 3.269-2.206 3.374-2.23c.05-.012.12-.026.166.016s.042.12.037.141c-.03.129-1.227 1.241-1.846 1.817-.193.18-.33.307-.358.336a8 8 0 0 1-.188.186c-.38.366-.664.64.015 1.088.327.216.589.393.85.571.284.194.568.387.936.629q.14.092.27.187c.331.236.63.448.997.414.214-.02.435-.22.547-.82.265-1.417.786-4.486.906-5.751a1.4 1.4 0 0 0-.013-.315.34.34 0 0 0-.114-.217.53.53 0 0 0-.31-.093c-.3.005-.763.166-2.984 1.09"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 711 B |
1
app/assets/icons/warning.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="black" d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480H40c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24V296c0 13.3 10.7 24 24 24s24-10.7 24-24V184c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"/></svg>
|
After Width: | Height: | Size: 397 B |
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
BIN
app/assets/images/wall.png
Normal file
After Width: | Height: | Size: 60 KiB |
|
@ -1,130 +1,120 @@
|
||||||
/**
|
|
||||||
* JavaScript functions for form validation and error handling
|
|
||||||
* Funções JavaScript para validação de formulário e manipulação de erros
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates the form before submission
|
|
||||||
*
|
|
||||||
* Checks:
|
|
||||||
* - If URL is not empty
|
|
||||||
* - If URL starts with http:// or https://
|
|
||||||
* - If URL has a valid format
|
|
||||||
*
|
|
||||||
* @returns {boolean} True if URL is valid, False otherwise
|
|
||||||
*
|
|
||||||
* Valida o formulário antes do envio
|
|
||||||
*
|
|
||||||
* Verifica:
|
|
||||||
* - Se a URL não está vazia
|
|
||||||
* - Se a URL começa com http:// ou https://
|
|
||||||
* - Se a URL tem um formato válido
|
|
||||||
*
|
|
||||||
* @returns {boolean} True se a URL for válida, False caso contrário
|
|
||||||
*/
|
|
||||||
function validateForm() {
|
|
||||||
const urlInput = document.getElementById('url');
|
|
||||||
const submitButton = document.querySelector('button[type="submit"]');
|
|
||||||
const url = urlInput.value.trim();
|
|
||||||
|
|
||||||
// Check if URL is not empty
|
|
||||||
// Verifica se a URL não está vazia
|
|
||||||
if (!url) {
|
|
||||||
showError('Por favor, insira uma URL');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if URL starts with http:// or https://
|
|
||||||
// Verifica se a URL começa com http:// ou https://
|
|
||||||
if (!/^https?:\/\//i.test(url)) {
|
|
||||||
showError('A URL deve começar com http:// ou https://');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to create a URL object to validate format
|
|
||||||
// Tenta criar um objeto URL para validar o formato
|
|
||||||
try {
|
|
||||||
new URL(url);
|
|
||||||
} catch (e) {
|
|
||||||
showError('Formato de URL inválido');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disable input and button
|
|
||||||
// Desabilita o input e o botão
|
|
||||||
urlInput.readonly = true;
|
|
||||||
submitButton.disabled = true;
|
|
||||||
|
|
||||||
// Add Tailwind disabled classes
|
|
||||||
// Adiciona classes de disabled do Tailwind
|
|
||||||
submitButton.classList.add('cursor-wait', 'disabled:bg-blue-400');
|
|
||||||
submitButton.classList.remove('hover:bg-blue-700');
|
|
||||||
|
|
||||||
urlInput.classList.add('cursor-wait', 'disabled:bg-gray-50', 'focus:outline-none');
|
|
||||||
|
|
||||||
// Add loading state to button
|
|
||||||
// Adiciona estado de loading ao botão
|
|
||||||
submitButton.innerHTML = `
|
|
||||||
<img src="assets/svg/search.svg" class="w-6 h-6 mr-3" alt="Search">
|
|
||||||
Analisando...
|
|
||||||
`;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays an error message below the form
|
|
||||||
*
|
|
||||||
* Removes any existing error message before displaying the new one.
|
|
||||||
* The message is displayed with an error icon and red formatting.
|
|
||||||
*
|
|
||||||
* @param {string} message Error message to be displayed
|
|
||||||
*
|
|
||||||
* Exibe uma mensagem de erro abaixo do formulário
|
|
||||||
*
|
|
||||||
* Remove qualquer mensagem de erro existente antes de exibir a nova.
|
|
||||||
* A mensagem é exibida com um ícone de erro e formatação em vermelho.
|
|
||||||
*
|
|
||||||
* @param {string} message Mensagem de erro a ser exibida
|
|
||||||
*/
|
|
||||||
function showError(message) {
|
|
||||||
const form = document.getElementById('urlForm');
|
|
||||||
const existingError = form.querySelector('.error-message');
|
|
||||||
|
|
||||||
// Remove previous error message if it exists
|
|
||||||
// Remove mensagem de erro anterior se existir
|
|
||||||
if (existingError) {
|
|
||||||
existingError.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create and add new error message
|
|
||||||
// Cria e adiciona nova mensagem de erro
|
|
||||||
const errorDiv = document.createElement('div');
|
|
||||||
errorDiv.className = 'error-message mt-4 text-base text-red-600';
|
|
||||||
errorDiv.innerHTML = `
|
|
||||||
<img src="assets/svgs/error.svg" class="inline-block w-5 h-5 mr-2" alt="Error icon">
|
|
||||||
${message}`;
|
|
||||||
|
|
||||||
form.appendChild(errorDiv);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service Worker registration for PWA functionality
|
* Service Worker registration for PWA functionality
|
||||||
* Registers a service worker to enable offline capabilities and PWA features
|
* Registers a service worker to enable offline capabilities and PWA features
|
||||||
*
|
|
||||||
* Registro do Service Worker para funcionalidade PWA
|
|
||||||
* Registra um service worker para habilitar recursos offline e funcionalidades PWA
|
|
||||||
*/
|
*/
|
||||||
if ('serviceWorker' in navigator) {
|
if ('serviceWorker' in navigator) {
|
||||||
window.addEventListener('load', () => {
|
window.addEventListener('load', () => {
|
||||||
navigator.serviceWorker.register('/service-worker.js')
|
navigator.serviceWorker.register('/service-worker.js')
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// Service Worker registered successfully
|
// Service Worker registered successfully
|
||||||
// Service Worker registrado com sucesso
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
// Service Worker registration failed
|
// Service Worker registration failed
|
||||||
// Falha no registro do Service Worker
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Header toggle menus
|
||||||
|
*/
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
const integration = document.querySelector('.integration');
|
||||||
|
const integrationToggle = document.querySelector('.integration__toggle');
|
||||||
|
const extension = document.querySelector('.extension');
|
||||||
|
const extensionToggle = document.querySelector('.extension__toggle');
|
||||||
|
|
||||||
|
// Function to close all menus
|
||||||
|
const closeAllMenus = () => {
|
||||||
|
integration.classList.remove('open');
|
||||||
|
extension.classList.remove('open');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to close other menus except the one passed
|
||||||
|
const closeOtherMenus = (exceptMenu) => {
|
||||||
|
if (exceptMenu !== integration) {
|
||||||
|
integration.classList.remove('open');
|
||||||
|
}
|
||||||
|
if (exceptMenu !== extension) {
|
||||||
|
extension.classList.remove('open');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
integrationToggle.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation(); // Prevent click from bubbling to document
|
||||||
|
closeOtherMenus(integration);
|
||||||
|
integration.classList.toggle('open');
|
||||||
|
});
|
||||||
|
|
||||||
|
extensionToggle.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation(); // Prevent click from bubbling to document
|
||||||
|
closeOtherMenus(extension);
|
||||||
|
extension.classList.toggle('open');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Prevent clicks inside menus from closing them
|
||||||
|
integration.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
});
|
||||||
|
|
||||||
|
extension.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close menus when clicking outside
|
||||||
|
document.addEventListener('click', () => {
|
||||||
|
closeAllMenus();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove toasty elements when clicked
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
const toastyElement = e.target.closest('.toasty');
|
||||||
|
if (toastyElement) {
|
||||||
|
toastyElement.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Toggle header open class when open-nav is clicked
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
const openNavElement = e.target.closest('.open-nav');
|
||||||
|
if (openNavElement) {
|
||||||
|
const header = document.querySelector('header');
|
||||||
|
if (header.classList.contains('open')) {
|
||||||
|
header.classList.remove('open');
|
||||||
|
} else {
|
||||||
|
header.classList.add('open');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Paste button functionality
|
||||||
|
const pasteButton = document.getElementById('paste');
|
||||||
|
const urlInput = document.getElementById('url');
|
||||||
|
|
||||||
|
if (pasteButton && urlInput) {
|
||||||
|
pasteButton.addEventListener('click', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
try {
|
||||||
|
const clipboardText = await navigator.clipboard.readText();
|
||||||
|
urlInput.value = clipboardText.trim();
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to read clipboard contents', err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dark mode
|
||||||
|
const themeToggle = document.getElementById('themeToggle');
|
||||||
|
const html = document.documentElement;
|
||||||
|
const savedTheme = localStorage.getItem('theme') || 'light';
|
||||||
|
html.setAttribute('data-theme', savedTheme);
|
||||||
|
|
||||||
|
if (themeToggle) {
|
||||||
|
themeToggle.addEventListener('click', () => {
|
||||||
|
const currentTheme = html.getAttribute('data-theme');
|
||||||
|
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
||||||
|
|
||||||
|
html.setAttribute('data-theme', newTheme);
|
||||||
|
localStorage.setItem('theme', newTheme);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
35
app/assets/scss/_base.scss
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
@use "mixin";
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-family-inter);
|
||||||
|
font-size: var(--font-size);
|
||||||
|
font-weight: var(--font-weight);
|
||||||
|
line-height: var(--line-height);
|
||||||
|
color: var(--text);
|
||||||
|
text-align: left;
|
||||||
|
background-color: var(--background);
|
||||||
|
min-width: 320px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--link);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--link-lighten);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding-right: var(--container_spacing);
|
||||||
|
padding-left: var(--container_spacing);
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
@include mixin.devices(desktop) {
|
||||||
|
max-width: 1248px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
35
app/assets/scss/_fonts.scss
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
@font-face {
|
||||||
|
font-family: 'inter';
|
||||||
|
src: url('/dist/fonts/inter-500.eot');
|
||||||
|
src: local('Inter Medium'), local('Inter-Medium'),
|
||||||
|
url('/dist/fonts/inter-500.woff2') format('woff2'),
|
||||||
|
url('/dist/fonts/inter-500.woff') format('woff'),
|
||||||
|
url('/dist/fonts/inter-500.ttf') format('truetype');
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'inter';
|
||||||
|
src: url('/dist/fonts/inter-600.eot');
|
||||||
|
src: local('Inter SemiBold'), local('Inter-SemiBold'),
|
||||||
|
url('/dist/fonts/inter-600.woff2') format('woff2'),
|
||||||
|
url('/dist/fonts/inter-600.woff') format('woff'),
|
||||||
|
url('/dist/fonts/inter-600.ttf') format('truetype');
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'unna';
|
||||||
|
src: url('/dist/fonts/unna-400.eot');
|
||||||
|
src: local('Unna Regular'), local('Unna-Regular'),
|
||||||
|
url('/dist/fonts/unna-400.woff2') format('woff2'),
|
||||||
|
url('/dist/fonts/unna-400.woff') format('woff'),
|
||||||
|
url('/dist/fonts/unna-400.ttf') format('truetype');
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
46
app/assets/scss/_icons.scss
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
@use "mixin";
|
||||||
|
|
||||||
|
// https://icons.getbootstrap.com/
|
||||||
|
// https://icofont.com/icons
|
||||||
|
|
||||||
|
// https://jakearchibald.github.io/svgomg/
|
||||||
|
// https://yoksel.github.io/url-encoder/
|
||||||
|
// https://codepen.io/sosuke/pen/Pjoqqp
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: -0.125em;
|
||||||
|
fill: currentcolor;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center center;
|
||||||
|
background-size: 100% auto;
|
||||||
|
&--bookmark {
|
||||||
|
background-size: auto 100% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include mixin.icon('marreta', 'invert(43%) sepia(74%) saturate(2288%) hue-rotate(201deg) brightness(98%) contrast(97%)');
|
||||||
|
@include mixin.icon('bookmark', 'invert(80%) sepia(46%) saturate(1512%) hue-rotate(340deg) brightness(106%) contrast(97%)');
|
||||||
|
|
||||||
|
@include mixin.icon('android', 'invert(72%) sepia(34%) saturate(778%) hue-rotate(32deg) brightness(97%) contrast(88%)');
|
||||||
|
@include mixin.icon('apple', 'invert(0%) sepia(21%) saturate(7425%) hue-rotate(12deg) brightness(96%) contrast(96%)');
|
||||||
|
@include mixin.icon('bsky', 'invert(47%) sepia(66%) saturate(4445%) hue-rotate(195deg) brightness(98%) contrast(104%)');
|
||||||
|
@include mixin.icon('telegram', 'invert(61%) sepia(86%) saturate(1341%) hue-rotate(166deg) brightness(96%) contrast(85%)');
|
||||||
|
@include mixin.icon('chrome', 'invert(40%) sepia(90%) saturate(1163%) hue-rotate(203deg) brightness(104%) contrast(92%)');
|
||||||
|
@include mixin.icon('firefox', 'invert(57%) sepia(43%) saturate(1854%) hue-rotate(360deg) brightness(102%) contrast(106%)');
|
||||||
|
|
||||||
|
@include mixin.icon('link', 'invert(53%) sepia(12%) saturate(17%) hue-rotate(39deg) brightness(94%) contrast(91%)');
|
||||||
|
@include mixin.icon('refresh', 'invert(100%) sepia(32%) saturate(8%) hue-rotate(23deg) brightness(102%) contrast(100%)');
|
||||||
|
|
||||||
|
@include mixin.icon('error', 'invert(30%) sepia(58%) saturate(3703%) hue-rotate(336deg) brightness(90%) contrast(91%)');
|
||||||
|
@include mixin.icon('warning', 'invert(89%) sepia(25%) saturate(5861%) hue-rotate(353deg) brightness(101%) contrast(101%)');
|
||||||
|
|
||||||
|
@include mixin.icon('hamburguer', 'invert(0%) sepia(21%) saturate(7425%) hue-rotate(12deg) brightness(96%) contrast(96%)');
|
||||||
|
@include mixin.icon('close', 'invert(100%) sepia(32%) saturate(8%) hue-rotate(23deg) brightness(102%) contrast(100%)');
|
||||||
|
|
||||||
|
@include mixin.icon('paste', 'invert(0%) sepia(21%) saturate(7425%) hue-rotate(12deg) brightness(96%) contrast(96%)');
|
||||||
|
|
||||||
|
@include mixin.icon('sun', 'invert(0%) sepia(21%) saturate(7425%) hue-rotate(12deg) brightness(96%) contrast(96%)');
|
||||||
|
@include mixin.icon('moon', 'invert(0%) sepia(21%) saturate(7425%) hue-rotate(12deg) brightness(96%) contrast(96%)');
|
23
app/assets/scss/_mixin.scss
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
@use "sass:math";
|
||||||
|
@use "sass:color";
|
||||||
|
|
||||||
|
@mixin create-color($name, $hex) {
|
||||||
|
--#{$name}: #{$hex};
|
||||||
|
--#{$name}-lighten: #{color.adjust($hex, $lightness: 5%)};
|
||||||
|
--#{$name}-darken: #{color.adjust($hex, $lightness: -10%)};
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin devices($breakpoint) {
|
||||||
|
@if $breakpoint == desktop {
|
||||||
|
@media only screen and (min-width: 1200px) {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin icon($name, $filter) {
|
||||||
|
.icon--#{$name} {
|
||||||
|
background-image: url("/dist/icons/#{$name}.svg");
|
||||||
|
filter: #{$filter};
|
||||||
|
}
|
||||||
|
}
|
84
app/assets/scss/_root.scss
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
@use "mixin";
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--font-family-sans-serif: -apple-system,
|
||||||
|
BlinkMacSystemFont,
|
||||||
|
"Segoe UI",
|
||||||
|
Roboto,
|
||||||
|
"Helvetica Neue",
|
||||||
|
Arial,
|
||||||
|
"Noto Sans",
|
||||||
|
"Liberation Sans",
|
||||||
|
sans-serif,
|
||||||
|
"Apple Color Emoji",
|
||||||
|
"Segoe UI Emoji",
|
||||||
|
"Segoe UI Symbol",
|
||||||
|
"Noto Color Emoji";
|
||||||
|
--font-family-monospace: SFMono-Regular,
|
||||||
|
Menlo, Monaco,
|
||||||
|
Consolas,
|
||||||
|
"Liberation Mono",
|
||||||
|
"Courier New",
|
||||||
|
monospace;
|
||||||
|
--font-family-inter: "inter";
|
||||||
|
--font-family-unna: "unna";
|
||||||
|
|
||||||
|
--font-size: 16px;
|
||||||
|
--font-weight: 500;
|
||||||
|
--line-height: 160%;
|
||||||
|
|
||||||
|
/* Light theme colors */
|
||||||
|
@include mixin.create-color('marreta', #3B82F6);
|
||||||
|
@include mixin.create-color('text', #484848);
|
||||||
|
@include mixin.create-color('textmuted', #818181);
|
||||||
|
@include mixin.create-color('link', #3B82F6);
|
||||||
|
|
||||||
|
/* Theme-aware colors */
|
||||||
|
--background: #ffffff;
|
||||||
|
--surface: #F4F4F5;
|
||||||
|
--surface-hover: #e4e4e7;
|
||||||
|
--border: #e4e4e7;
|
||||||
|
--header-text: #000000;
|
||||||
|
--nav-mobile-bg: var(--marreta);
|
||||||
|
--nav-mobile-text: #ffffff;
|
||||||
|
--nav-desktop-text: #333333;
|
||||||
|
--nav-desktop-hover: #007bff;
|
||||||
|
--input-bg: #F4F4F5;
|
||||||
|
--toast-error: rgb(247, 102, 97);
|
||||||
|
--toast-warning: rgb(247, 152, 97);
|
||||||
|
|
||||||
|
--container_spacing: 24px;
|
||||||
|
@include mixin.devices(desktop) {
|
||||||
|
--container_spacing: 64px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark theme */
|
||||||
|
[data-theme="dark"] {
|
||||||
|
@include mixin.create-color('marreta', #60A5FA);
|
||||||
|
@include mixin.create-color('text', #e5e5e5);
|
||||||
|
@include mixin.create-color('textmuted', #a1a1aa);
|
||||||
|
@include mixin.create-color('link', #60A5FA);
|
||||||
|
|
||||||
|
--background: #000;
|
||||||
|
--surface: #1f1f1f;
|
||||||
|
--surface-hover: #2a2a2a;
|
||||||
|
--border: #2a2a2a;
|
||||||
|
--header-text: #ffffff;
|
||||||
|
--nav-mobile-bg: var(--marreta);
|
||||||
|
--nav-mobile-text: #ffffff;
|
||||||
|
--nav-desktop-text: #e5e5e5;
|
||||||
|
--nav-desktop-hover: #60A5FA;
|
||||||
|
--input-bg: #1f1f1f;
|
||||||
|
--toast-error: rgb(220, 38, 127);
|
||||||
|
--toast-warning: rgb(245, 158, 11);
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
@media screen and (prefers-reduced-motion: reduce) {
|
||||||
|
html {
|
||||||
|
scroll-behavior: auto;
|
||||||
|
}
|
||||||
|
}
|
722
app/assets/scss/home.scss
Normal file
|
@ -0,0 +1,722 @@
|
||||||
|
@use "mixin";
|
||||||
|
|
||||||
|
body {
|
||||||
|
padding: 42px 0 0 0;
|
||||||
|
@include mixin.devices(desktop) {
|
||||||
|
padding: 42px 0 52px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.toasty {
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0px 4px 6px 0px rgba(0, 0, 0, 0.05);
|
||||||
|
box-shadow: 0px 10px 15px 0px rgba(0, 0, 0, 0.1);
|
||||||
|
box-shadow: 0px 10px 10px 0px rgba(0, 0, 0, 0.04);
|
||||||
|
box-shadow: 0px 20px 25px 0px rgba(0, 0, 0, 0.1);
|
||||||
|
position: absolute;
|
||||||
|
top: 16px;
|
||||||
|
left: 16px;
|
||||||
|
right: 16px;
|
||||||
|
color: #fff;
|
||||||
|
z-index: 1000;
|
||||||
|
width: auto;
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.close {
|
||||||
|
.icon {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include mixin.devices(desktop) {
|
||||||
|
max-width: 480px;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -256px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--error {
|
||||||
|
background-color: var(--toast-error);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--warning {
|
||||||
|
background-color: var(--toast-warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr auto 1fr;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 0 42px 0;
|
||||||
|
|
||||||
|
@include mixin.devices(desktop) {
|
||||||
|
grid-template-columns: 1fr 2fr auto 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.open {
|
||||||
|
.extension {
|
||||||
|
&__toggle {
|
||||||
|
background-color: #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.open-nav {
|
||||||
|
.icon {
|
||||||
|
&--hamburguer {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
&--close {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nav {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.integration__menu {
|
||||||
|
.icon {
|
||||||
|
filter: invert(100%) sepia(32%) saturate(8%) hue-rotate(23deg) brightness(102%) contrast(100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.open-nav {
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
z-index: 501;
|
||||||
|
padding-right: 16px;
|
||||||
|
.icon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
&--hamburguer {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
&--close {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@include mixin.devices(desktop) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
margin-right: 6px;
|
||||||
|
|
||||||
|
&--marreta {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-family-unna);
|
||||||
|
color: var(--header-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fast_buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-controls {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 16px;
|
||||||
|
|
||||||
|
@include mixin.devices(desktop) {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-toggle {
|
||||||
|
background: none;
|
||||||
|
border: 2px solid var(--border);
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: var(--marreta);
|
||||||
|
background-color: var(--surface-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
position: absolute;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&--sun {
|
||||||
|
opacity: 1;
|
||||||
|
transform: rotate(0deg) scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--moon {
|
||||||
|
opacity: 0;
|
||||||
|
transform: rotate(180deg) scale(0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] & {
|
||||||
|
filter: invert(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] & {
|
||||||
|
.icon {
|
||||||
|
&--sun {
|
||||||
|
opacity: 0;
|
||||||
|
transform: rotate(-180deg) scale(0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--moon {
|
||||||
|
opacity: 1;
|
||||||
|
transform: rotate(0deg) scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: var(--nav-mobile-bg);
|
||||||
|
padding: var(--container_spacing) var(--container_spacing) calc(4*var(--container_spacing)) var(--container_spacing);
|
||||||
|
z-index: 500;
|
||||||
|
align-items: flex-end;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: end;
|
||||||
|
|
||||||
|
&> * {
|
||||||
|
width: 100%;
|
||||||
|
@include mixin.devices(desktop) {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include mixin.devices(desktop) {
|
||||||
|
opacity: 1;
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
left: initial;
|
||||||
|
right: initial;
|
||||||
|
bottom: initial;
|
||||||
|
top: initial;
|
||||||
|
background-color: transparent;
|
||||||
|
padding: 0;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: normal;
|
||||||
|
flex-direction:initial;
|
||||||
|
gap: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: block;
|
||||||
|
font-size: 24px;
|
||||||
|
padding: 16px 0;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.24);
|
||||||
|
color: var(--nav-mobile-text);
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
@include mixin.devices(desktop) {
|
||||||
|
color: var(--nav-desktop-text);
|
||||||
|
font-size: initial;
|
||||||
|
padding: 0;
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--nav-mobile-text);
|
||||||
|
@include mixin.devices(desktop) {
|
||||||
|
color: var(--nav-desktop-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.integration {
|
||||||
|
position: relative;
|
||||||
|
padding-top: 32px;
|
||||||
|
|
||||||
|
@include mixin.devices(desktop) {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__toggle {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: rgba(255,255,255,0.5);
|
||||||
|
padding: 0;
|
||||||
|
@include mixin.devices(desktop) {
|
||||||
|
color: var(--nav-desktop-text);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: rgba(255,255,255,0.5);
|
||||||
|
@include mixin.devices(desktop) {
|
||||||
|
color: var(--nav-desktop-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__menu {
|
||||||
|
@include mixin.devices(desktop) {
|
||||||
|
position: absolute;
|
||||||
|
top: 110%;
|
||||||
|
left: 0;
|
||||||
|
border-radius: 16px;
|
||||||
|
background-color: var(--surface);
|
||||||
|
border: 4px solid var(--surface);
|
||||||
|
z-index: 10;
|
||||||
|
box-shadow: 0px 4px 6px 0px rgba(0, 0, 0, 0.05);
|
||||||
|
box-shadow: 0px 10px 15px 0px rgba(0, 0, 0, 0.1);
|
||||||
|
box-shadow: 0px 10px 10px 0px rgba(0, 0, 0, 0.04);
|
||||||
|
box-shadow: 0px 20px 25px 0px rgba(0, 0, 0, 0.1);
|
||||||
|
transition: max-height 0.8s cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
max-height: 0;
|
||||||
|
opacity: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
font-size: 14px;
|
||||||
|
border-bottom: 0;
|
||||||
|
margin-top: 8px;
|
||||||
|
padding: 16px 0 0 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
@include mixin.devices(desktop) {
|
||||||
|
background-color: var(--background);
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: var(--font-size);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
border-top-left-radius: 16px;
|
||||||
|
border-top-right-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
border-bottom-left-radius: 16px;
|
||||||
|
border-bottom-right-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--marreta);
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
order: 1;
|
||||||
|
|
||||||
|
@include mixin.devices(desktop) {
|
||||||
|
order: 2;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
order: 2;
|
||||||
|
line-height: 1em;
|
||||||
|
padding-left: 12px;
|
||||||
|
|
||||||
|
@include mixin.devices(desktop) {
|
||||||
|
order: 1;
|
||||||
|
padding-left: 0;
|
||||||
|
width: 140px;
|
||||||
|
line-height: var(--line-height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.open {
|
||||||
|
.integration__menu {
|
||||||
|
max-height: 200px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow {
|
||||||
|
top: 1px;
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow {
|
||||||
|
display: none;
|
||||||
|
position: relative;
|
||||||
|
top: -3px;
|
||||||
|
content: "";
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
border-right: 2px solid black;
|
||||||
|
border-top: 2px solid black;
|
||||||
|
transform: rotate(135deg);
|
||||||
|
margin-right: 0;
|
||||||
|
margin-left: 16px;
|
||||||
|
|
||||||
|
@include mixin.devices(desktop) {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.extension {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
position: relative;
|
||||||
|
z-index: 501;
|
||||||
|
|
||||||
|
&__toggle {
|
||||||
|
background-color: var(--marreta);
|
||||||
|
border-radius: 40px;
|
||||||
|
border: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #FFF;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 12px 24px;
|
||||||
|
line-height: 1.3em;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--marreta-darken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__menu {
|
||||||
|
position: absolute;
|
||||||
|
top: 110%;
|
||||||
|
right: 0;
|
||||||
|
border-radius: 16px;
|
||||||
|
background-color: var(--surface);
|
||||||
|
border: 4px solid var(--surface);
|
||||||
|
z-index: 10;
|
||||||
|
box-shadow: 0px 4px 6px 0px rgba(0, 0, 0, 0.05);
|
||||||
|
box-shadow: 0px 10px 15px 0px rgba(0, 0, 0, 0.1);
|
||||||
|
box-shadow: 0px 10px 10px 0px rgba(0, 0, 0, 0.04);
|
||||||
|
box-shadow: 0px 20px 25px 0px rgba(0, 0, 0, 0.1);
|
||||||
|
transition: max-height 0.8s cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
max-height: 0;
|
||||||
|
opacity: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
a {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
border-top-left-radius: 16px;
|
||||||
|
border-top-right-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
border-bottom-left-radius: 16px;
|
||||||
|
border-bottom-right-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
color: var(--text);
|
||||||
|
font-weight: 600;
|
||||||
|
display: block;
|
||||||
|
padding: 8px 16px;
|
||||||
|
background-color: var(--background);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--marreta);
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
width: 140px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.open {
|
||||||
|
.extension__toggle {
|
||||||
|
background-color: var(--surface);
|
||||||
|
color: var(--textmuted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.extension__menu {
|
||||||
|
max-height: 200px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
.description {
|
||||||
|
position: relative;
|
||||||
|
z-index: 3;
|
||||||
|
font-family: var(--font-family-unna);
|
||||||
|
font-size: 64px;
|
||||||
|
line-height: 61.44px;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--header-text);
|
||||||
|
max-width: 512px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.walls_destroyed {
|
||||||
|
position: relative;
|
||||||
|
z-index: 3;
|
||||||
|
max-width: 512px;
|
||||||
|
margin: 22px auto;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: var(--textmuted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
z-index: 2;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.fields {
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
background-image: url(/assets/images/wall.png);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
width: 422px;
|
||||||
|
height: 306px;
|
||||||
|
position: absolute;
|
||||||
|
top: -110px;
|
||||||
|
right: -180px;
|
||||||
|
z-index: 1;
|
||||||
|
transition: filter 0.3s ease;
|
||||||
|
|
||||||
|
[data-theme="dark"] & {
|
||||||
|
filter: invert(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
max-width: 470px;
|
||||||
|
margin: 0 auto;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.input {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
padding-right: 28px;
|
||||||
|
padding-top: 2px;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
&--link {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 1rem;
|
||||||
|
margin-top: -6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
background-color: var(--input-bg);
|
||||||
|
padding: 16px 0 16px 44px;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 8px;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
|
line-height: 1.3em;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.paste {
|
||||||
|
background: var(--input-bg);
|
||||||
|
background: linear-gradient(90deg, transparent 0%, var(--input-bg) 30%, var(--input-bg) 100%);
|
||||||
|
align-items: center;
|
||||||
|
z-index: 3;
|
||||||
|
position: absolute;
|
||||||
|
top: 4px;
|
||||||
|
padding: 0 18px 0 22px;
|
||||||
|
right: 50px;
|
||||||
|
cursor: pointer;
|
||||||
|
height: 48px;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
transition: filter 0.3s ease;
|
||||||
|
|
||||||
|
[data-theme="dark"] & {
|
||||||
|
filter: invert(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
position: relative;
|
||||||
|
background-color: var(--marreta);
|
||||||
|
border-radius: 50%;
|
||||||
|
height: 56px;
|
||||||
|
width: 56px;
|
||||||
|
border: 0;
|
||||||
|
z-index: 3;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--marreta-darken);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
width: 23px;
|
||||||
|
height: 23px;
|
||||||
|
|
||||||
|
&--refresh,
|
||||||
|
&--marreta {
|
||||||
|
filter: invert(100%) sepia(32%) saturate(8%) hue-rotate(23deg) brightness(102%) contrast(100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.adblock {
|
||||||
|
color: var(--textmuted);
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.2em;
|
||||||
|
text-align: center;
|
||||||
|
max-width: 470px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 3;
|
||||||
|
margin: 22px auto 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plus {
|
||||||
|
z-index: 3;
|
||||||
|
position: relative;
|
||||||
|
background-color: var(--surface);
|
||||||
|
margin-left: calc(-1*var(--container_spacing));
|
||||||
|
margin-right: calc(-1*var(--container_spacing));
|
||||||
|
|
||||||
|
@include mixin.devices(desktop) {
|
||||||
|
background-color: transparent;
|
||||||
|
display: grid;
|
||||||
|
grid-auto-columns: 1fr;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 0px 38px;
|
||||||
|
align-items: start;
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 62px auto 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--textmuted);
|
||||||
|
padding-left: 26px;
|
||||||
|
|
||||||
|
ol {
|
||||||
|
padding-left: 16px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
padding-right: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.add_as_app {
|
||||||
|
margin-top: 62px;
|
||||||
|
padding: var(--container_spacing);
|
||||||
|
|
||||||
|
@include mixin.devices(desktop) {
|
||||||
|
padding: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookmarklet {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
@include mixin.devices(desktop) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
border: 2px solid var(--marreta);
|
||||||
|
color: var(--marreta);
|
||||||
|
border-radius: 40px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
margin-top: 16px;
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: var(--marreta-darken);
|
||||||
|
color: var(--marreta-darken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {}
|
10
app/assets/scss/index.scss
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
@forward "normalize.css/normalize";
|
||||||
|
|
||||||
|
@use "mixin";
|
||||||
|
@use "fonts";
|
||||||
|
|
||||||
|
@use "root";
|
||||||
|
@use "base";
|
||||||
|
@use "icons";
|
||||||
|
|
||||||
|
@use "home";
|
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="#3B82F6" d="M32 32l448 0c17.7 0 32 14.3 32 32l0 32c0 17.7-14.3 32-32 32L32 128C14.3 128 0 113.7 0 96L0 64C0 46.3 14.3 32 32 32zm0 128l448 0 0 256c0 35.3-28.7 64-64 64L96 480c-35.3 0-64-28.7-64-64l0-256zm128 80c0 8.8 7.2 16 16 16l160 0c8.8 0 16-7.2 16-16s-7.2-16-16-16l-160 0c-8.8 0-16 7.2-16 16z"/></svg>
|
|
Before Width: | Height: | Size: 378 B |
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path fill="#FBBF24" d="M0 48C0 21.5 21.5 0 48 0l0 48V441.4l130.1-92.9c8.3-6 19.6-6 27.9 0L336 441.4V48H48V0H336c26.5 0 48 21.5 48 48V488c0 9-5 17.2-13 21.3s-17.6 3.4-24.9-1.8L192 397.5 37.9 507.5c-7.3 5.2-16.9 5.9-24.9 1.8S0 497 0 488V48z"/></svg>
|
|
Before Width: | Height: | Size: 311 B |
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="#10B981" d="M240 32a32 32 0 1 1 64 0 32 32 0 1 1 -64 0zM192 48a32 32 0 1 1 0 64 32 32 0 1 1 0-64zm-32 80c17.7 0 32 14.3 32 32l8 0c13.3 0 24 10.7 24 24l0 16c0 1.7-.2 3.4-.5 5.1C280.3 229.6 320 286.2 320 352c0 88.4-71.6 160-160 160S0 440.4 0 352c0-65.8 39.7-122.4 96.5-146.9c-.4-1.6-.5-3.3-.5-5.1l0-16c0-13.3 10.7-24 24-24l8 0c0-17.7 14.3-32 32-32zm0 320a96 96 0 1 0 0-192 96 96 0 1 0 0 192zm192-96c0-25.9-5.1-50.5-14.4-73.1c16.9-32.9 44.8-59.1 78.9-73.9c-.4-1.6-.5-3.3-.5-5.1l0-16c0-13.3 10.7-24 24-24l8 0c0-17.7 14.3-32 32-32s32 14.3 32 32l8 0c13.3 0 24 10.7 24 24l0 16c0 1.7-.2 3.4-.5 5.1C600.3 229.6 640 286.2 640 352c0 88.4-71.6 160-160 160c-62 0-115.8-35.3-142.4-86.9c9.3-22.5 14.4-47.2 14.4-73.1zm224 0a96 96 0 1 0 -192 0 96 96 0 1 0 192 0zM368 0a32 32 0 1 1 0 64 32 32 0 1 1 0-64zm80 48a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
|
|
Before Width: | Height: | Size: 910 B |
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="#6B7280" d="M392.8 1.2c-17-4.9-34.7 5-39.6 22l-128 448c-4.9 17 5 34.7 22 39.6s34.7-5 39.6-22l128-448c4.9-17-5-34.7-22-39.6zm80.6 120.1c-12.5 12.5-12.5 32.8 0 45.3L562.7 256l-89.4 89.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l112-112c12.5-12.5 12.5-32.8 0-45.3l-112-112c-12.5-12.5-32.8-12.5-45.3 0zm-306.7 0c-12.5-12.5-32.8-12.5-45.3 0l-112 112c-12.5 12.5-12.5 32.8 0 45.3l112 112c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L77.3 256l89.4-89.4c12.5-12.5 12.5-32.8 0-45.3z"/></svg>
|
|
Before Width: | Height: | Size: 558 B |
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="#EF4444" d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336h80c13.3 0 24-10.7 24-24s-10.7-24-24-24h-80c-13.3 0-24 10.7-24 24s10.7 24 24 24zm-24-72h128c13.3 0 24-10.7 24-24s-10.7-24-24-24H192c-13.3 0-24 10.7-24 24s10.7 24 24 24z"/></svg>
|
|
Before Width: | Height: | Size: 321 B |
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="#3B82F6" d="M579.8 267.7c56.5-56.5 56.5-148 0-204.5c-50-50-128.8-56.5-186.3-15.4l-1.6 1.1c-14.4 10.3-17.7 30.3-7.4 44.6s30.3 17.7 44.6 7.4l1.6-1.1c32.1-22.9 76-19.3 103.8 8.6c31.5 31.5 31.5 82.5 0 114L422.3 334.8c-31.5 31.5-82.5 31.5-114 0c-27.9-27.9-31.5-71.8-8.6-103.8l1.1-1.6c10.3-14.4 6.9-34.4-7.4-44.6s-34.4-6.9-44.6 7.4l-1.1 1.6C206.5 251.2 213 330 263 380c56.5 56.5 148 56.5 204.5 0L579.8 267.7zM60.2 244.3c-56.5 56.5-56.5 148 0 204.5c50 50 128.8 56.5 186.3 15.4l1.6-1.1c14.4-10.3 17.7-30.3 7.4-44.6s-30.3-17.7-44.6-7.4l-1.6 1.1c-32.1 22.9-76 19.3-103.8-8.6C74 372 74 321 105.5 289.5L217.7 177.2c31.5-31.5 82.5-31.5 114 0c27.9 27.9 31.5 71.8 8.6 103.9l-1.1 1.6c-10.3 14.4-6.9 34.4 7.4 44.6s34.4 6.9 44.6-7.4l1.1-1.6C433.5 260.8 427 182 377 132c-56.5-56.5-148-56.5-204.5 0L60.2 244.3z"/></svg>
|
|
Before Width: | Height: | Size: 874 B |
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="#3B82F6" d="M283.9 378.6l18.3-60.1c18-4.1 34.2-16 43.1-33.8l64-128c10.5-21.1 8.4-45.2-3.7-63.6l52.7-76.6c3.7-5.4 10.4-8 16.7-6.5s11.2 6.7 12.2 13.1l16.2 104.1 105.1-7.4c6.5-.5 12.7 3.1 15.5 9s1.8 12.9-2.6 17.8L550.1 224l71.3 77.5c4.4 4.8 5.5 11.9 2.6 17.8s-9 9.5-15.5 9l-105.1-7.4L487.3 425c-1 6.5-5.9 11.7-12.2 13.1s-13-1.1-16.7-6.5l-59.7-86.7-91.4 52.2c-5.7 3.3-12.8 2.7-17.9-1.4s-7.2-10.9-5.3-17.2zm28.3-101.7c-9.3 10.9-25.2 14.4-38.6 7.7l-65.9-32.9s0 0 0 0L122 208.8s0 0 0 0L17.7 156.6C1.9 148.7-4.5 129.5 3.4 113.7l40-80C48.8 22.8 59.9 16 72 16l120 0c5 0 9.9 1.2 14.3 3.4l78.2 39.1 81.8 40.9c15.8 7.9 22.2 27.1 14.3 42.9l-64 128c-1.2 2.4-2.7 4.6-4.4 6.6zM107.6 237.4l85.9 42.9L90.9 485.5c-11.9 23.7-40.7 33.3-64.4 21.5S-6.8 466.2 5.1 442.5L107.6 237.4z"/></svg>
|
|
Before Width: | Height: | Size: 840 B |
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="#3B82F6" d="M105.1 202.6c7.7-21.8 20.2-42.3 37.8-59.8c62.5-62.5 163.8-62.5 226.3 0L386.3 160 352 160c-17.7 0-32 14.3-32 32s14.3 32 32 32l111.5 0c0 0 0 0 0 0l.4 0c17.7 0 32-14.3 32-32l0-112c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 35.2L414.4 97.6c-87.5-87.5-229.3-87.5-316.8 0C73.2 122 55.6 150.7 44.8 181.4c-5.9 16.7 2.9 34.9 19.5 40.8s34.9-2.9 40.8-19.5zM39 289.3c-5 1.5-9.8 4.2-13.7 8.2c-4 4-6.7 8.8-8.1 14c-.3 1.2-.6 2.5-.8 3.8c-.3 1.7-.4 3.4-.4 5.1L16 432c0 17.7 14.3 32 32 32s32-14.3 32-32l0-35.1 17.6 17.5c0 0 0 0 0 0c87.5 87.4 229.3 87.4 316.7 0c24.4-24.4 42.1-53.1 52.9-83.8c5.9-16.7-2.9-34.9-19.5-40.8s-34.9 2.9-40.8 19.5c-7.7 21.8-20.2 42.3-37.8 59.8c-62.5 62.5-163.8 62.5-226.3 0l-.1-.1L125.6 352l34.4 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L48.4 288c-1.6 0-3.2 .1-4.8 .3s-3.1 .5-4.6 1z"/></svg>
|
|
Before Width: | Height: | Size: 875 B |
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="#FFFFFF" d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z"/></svg>
|
|
Before Width: | Height: | Size: 328 B |
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="#FBBF24" d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480H40c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24V296c0 13.3 10.7 24 24 24s24-10.7 24-24V184c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"/></svg>
|
|
Before Width: | Height: | Size: 399 B |
211
app/bin/cleanup
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache Cleanup Script
|
||||||
|
*
|
||||||
|
* Removes *.gz files from the cache directory that are older than the number
|
||||||
|
* of days specified in the CLEANUP_DAYS environment variable.
|
||||||
|
* If CLEANUP_DAYS is not set, no files will be cleaned.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
|
use League\CLImate\CLImate;
|
||||||
|
use Dotenv\Dotenv;
|
||||||
|
use Aws\S3\S3Client;
|
||||||
|
use Aws\Exception\AwsException;
|
||||||
|
|
||||||
|
$climate = new CLImate();
|
||||||
|
$climate->bold()->out('Cache Cleanup Tool');
|
||||||
|
$climate->br();
|
||||||
|
|
||||||
|
$cleanupDays = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$dotenv = Dotenv::createImmutable(__DIR__ . '/..');
|
||||||
|
$dotenv->load();
|
||||||
|
$climate->out('Environment variables loaded');
|
||||||
|
$cleanupDays = $_ENV['CLEANUP_DAYS'];
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$climate->yellow()->out('Warning: ' . $e->getMessage());
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!defined('CACHE_DIR')) {
|
||||||
|
define('CACHE_DIR', __DIR__ . '/../cache');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($cleanupDays == 0) {
|
||||||
|
$climate->yellow()->out('CLEANUP_DAYS variable not set or 0. No files will be cleaned.');
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
$cleanupDays = (int)$cleanupDays;
|
||||||
|
if ($cleanupDays <= 0) {
|
||||||
|
$climate->red()->out('CLEANUP_DAYS must be a positive integer. No files will be cleaned.');
|
||||||
|
exit(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Calculate the cutoff timestamp
|
||||||
|
$cutoffTime = time() - ($cleanupDays * 86400);
|
||||||
|
|
||||||
|
// Check if S3 cache is enabled
|
||||||
|
$s3CacheEnabled = isset($_ENV['S3_CACHE_ENABLED']) && filter_var($_ENV['S3_CACHE_ENABLED'], FILTER_VALIDATE_BOOLEAN);
|
||||||
|
|
||||||
|
if ($s3CacheEnabled) {
|
||||||
|
// Clean S3 cache
|
||||||
|
cleanS3Cache($climate, $cutoffTime, $cleanupDays);
|
||||||
|
} else {
|
||||||
|
// Clean local disk cache
|
||||||
|
cleanDiskCache($climate, $cutoffTime, $cleanupDays);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean cache files from S3 bucket
|
||||||
|
*
|
||||||
|
* @param CLImate $climate CLImate instance for output
|
||||||
|
* @param int $cutoffTime Timestamp to use as cutoff for file age
|
||||||
|
* @param int $cleanupDays Number of days to keep files
|
||||||
|
*/
|
||||||
|
function cleanS3Cache($climate, $cutoffTime, $cleanupDays) {
|
||||||
|
$requiredVars = ['S3_ACCESS_KEY', 'S3_SECRET_KEY', 'S3_BUCKET'];
|
||||||
|
foreach ($requiredVars as $var) {
|
||||||
|
if (!isset($_ENV[$var]) || empty($_ENV[$var])) {
|
||||||
|
$climate->red()->out("$var environment variable is required for S3 cache cleaning.");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$climate->out("S3 cache enabled. Cleaning S3 cache files older than {$cleanupDays} days...");
|
||||||
|
|
||||||
|
$clientConfig = [
|
||||||
|
'version' => 'latest',
|
||||||
|
'region' => $_ENV['S3_REGION'] ?? 'us-east-1',
|
||||||
|
'credentials' => [
|
||||||
|
'key' => $_ENV['S3_ACCESS_KEY'],
|
||||||
|
'secret' => $_ENV['S3_SECRET_KEY'],
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!empty($_ENV['S3_ENDPOINT'])) {
|
||||||
|
$clientConfig['endpoint'] = $_ENV['S3_ENDPOINT'];
|
||||||
|
$clientConfig['use_path_style_endpoint'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$s3Client = new S3Client($clientConfig);
|
||||||
|
$bucket = $_ENV['S3_BUCKET'];
|
||||||
|
$prefix = $_ENV['S3_FOLDER'] ?? 'cache/';
|
||||||
|
|
||||||
|
$climate->out("Listing objects in bucket: {$bucket} with prefix: {$prefix}");
|
||||||
|
|
||||||
|
$objects = [];
|
||||||
|
$marker = null;
|
||||||
|
|
||||||
|
do {
|
||||||
|
$params = [
|
||||||
|
'Bucket' => $bucket,
|
||||||
|
'Prefix' => $prefix,
|
||||||
|
'MaxKeys' => 1000
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($marker) {
|
||||||
|
$params['Marker'] = $marker;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $s3Client->listObjects($params);
|
||||||
|
|
||||||
|
if (isset($result['Contents'])) {
|
||||||
|
foreach ($result['Contents'] as $object) {
|
||||||
|
if (substr($object['Key'], -3) === '.gz') {
|
||||||
|
$objects[] = $object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$marker = $result['NextMarker'] ?? ($result['IsTruncated'] ? end($result['Contents'])['Key'] : null);
|
||||||
|
} while ($marker);
|
||||||
|
|
||||||
|
$totalObjects = count($objects);
|
||||||
|
$climate->out("Found {$totalObjects} .gz objects in S3 bucket.");
|
||||||
|
|
||||||
|
if ($totalObjects === 0) {
|
||||||
|
$climate->out('No .gz objects found in S3 bucket.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$progress = $climate->progress()->total($totalObjects);
|
||||||
|
$deletedObjects = 0;
|
||||||
|
|
||||||
|
foreach ($objects as $index => $object) {
|
||||||
|
$progress->current($index + 1);
|
||||||
|
$lastModified = strtotime($object['LastModified']);
|
||||||
|
|
||||||
|
if ($lastModified < $cutoffTime) {
|
||||||
|
try {
|
||||||
|
$s3Client->deleteObject([
|
||||||
|
'Bucket' => $bucket,
|
||||||
|
'Key' => $object['Key']
|
||||||
|
]);
|
||||||
|
$deletedObjects++;
|
||||||
|
} catch (AwsException $e) {
|
||||||
|
$climate->red()->out("Failed to delete: " . $object['Key'] . " - " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$climate->br();
|
||||||
|
$climate->green()->out("S3 cleanup complete: {$deletedObjects} objects deleted.");
|
||||||
|
} catch (AwsException $e) {
|
||||||
|
$climate->red()->out("AWS Error: " . $e->getMessage());
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean cache files from local disk
|
||||||
|
*
|
||||||
|
* @param CLImate $climate CLImate instance for output
|
||||||
|
* @param int $cutoffTime Timestamp to use as cutoff for file age
|
||||||
|
* @param int $cleanupDays Number of days to keep files
|
||||||
|
*/
|
||||||
|
function cleanDiskCache($climate, $cutoffTime, $cleanupDays) {
|
||||||
|
$cacheDir = CACHE_DIR;
|
||||||
|
|
||||||
|
$climate->out("Cleaning cache files older than {$cleanupDays} days from: {$cacheDir}");
|
||||||
|
|
||||||
|
if (!is_dir($cacheDir)) {
|
||||||
|
$climate->red()->out("Cache directory not found: {$cacheDir}");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$gzFiles = glob($cacheDir . '/*.gz');
|
||||||
|
$totalFiles = count($gzFiles);
|
||||||
|
$deletedFiles = 0;
|
||||||
|
|
||||||
|
if ($totalFiles === 0) {
|
||||||
|
$climate->out('No .gz files found in cache directory.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$climate->out("Found {$totalFiles} .gz files in cache directory.");
|
||||||
|
|
||||||
|
$progress = $climate->progress()->total($totalFiles);
|
||||||
|
|
||||||
|
foreach ($gzFiles as $index => $file) {
|
||||||
|
$progress->current($index + 1);
|
||||||
|
$fileTime = filemtime($file);
|
||||||
|
|
||||||
|
if ($fileTime < $cutoffTime) {
|
||||||
|
if (unlink($file)) {
|
||||||
|
$deletedFiles++;
|
||||||
|
} else {
|
||||||
|
$climate->red()->out("Failed to delete: " . basename($file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$climate->br();
|
||||||
|
$climate->green()->out("Disk cleanup complete: {$deletedFiles} files deleted.");
|
||||||
|
}
|
196
app/bin/proxy
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy List Cache Updater
|
||||||
|
*
|
||||||
|
* Downloads proxy list from the URL specified in the PROXY_LIST environment variable
|
||||||
|
* and stores it in the cache directory for reuse.
|
||||||
|
* This script should be run daily via cron to keep the proxy list updated.
|
||||||
|
*
|
||||||
|
* Supported proxy list formats:
|
||||||
|
* 1. http://USER:PASSWORD@HOST:PORT
|
||||||
|
* 2. IP:PORT:USER:PASSWORD
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
|
use League\CLImate\CLImate;
|
||||||
|
use Dotenv\Dotenv;
|
||||||
|
use Curl\Curl;
|
||||||
|
|
||||||
|
$climate = new CLImate();
|
||||||
|
$climate->bold()->out('Proxy List Cache Updater');
|
||||||
|
$climate->br();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$dotenv = Dotenv::createImmutable(__DIR__ . '/..');
|
||||||
|
$dotenv->load();
|
||||||
|
$climate->out('Environment variables loaded');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$climate->yellow()->out('Warning: ' . $e->getMessage());
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!defined('CACHE_DIR')) {
|
||||||
|
define('CACHE_DIR', __DIR__ . '/../cache');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($_ENV['PROXY_LIST']) || empty($_ENV['PROXY_LIST'])) {
|
||||||
|
$climate->yellow()->out('PROXY_LIST environment variable not set. No proxies to cache.');
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
$proxyListUrl = $_ENV['PROXY_LIST'];
|
||||||
|
$proxyCachePath = CACHE_DIR . '/proxy_list.json';
|
||||||
|
|
||||||
|
// Download proxy list from URL
|
||||||
|
$climate->out('Downloading proxy list from: ' . $proxyListUrl);
|
||||||
|
$proxyList = downloadProxyList($proxyListUrl, $climate);
|
||||||
|
|
||||||
|
if ($proxyList === false) {
|
||||||
|
$climate->red()->out('Failed to download proxy list from URL: ' . $proxyListUrl);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$climate->green()->out('Proxy list downloaded successfully (' . strlen($proxyList) . ' bytes)');
|
||||||
|
|
||||||
|
if (!is_dir(CACHE_DIR)) {
|
||||||
|
if (!mkdir(CACHE_DIR, 0755, true)) {
|
||||||
|
$climate->red()->out('Failed to create cache directory: ' . CACHE_DIR);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$climate->out('Parsing proxy list from environment variable...');
|
||||||
|
$proxies = parseProxyList($proxyList);
|
||||||
|
|
||||||
|
if (empty($proxies)) {
|
||||||
|
$climate->red()->out('No valid proxies found in PROXY_LIST. Supported formats are:');
|
||||||
|
$climate->red()->out('1. http://USER:PASSWORD@HOST:PORT');
|
||||||
|
$climate->red()->out('2. IP:PORT:USER:PASSWORD');
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$climate->out('Found ' . count($proxies) . ' valid proxies.');
|
||||||
|
|
||||||
|
if (file_put_contents($proxyCachePath, json_encode($proxies))) {
|
||||||
|
$climate->green()->out('Proxy list successfully cached to: ' . $proxyCachePath);
|
||||||
|
} else {
|
||||||
|
$climate->red()->out('Failed to write proxy list to cache file: ' . $proxyCachePath);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse proxy list from environment variable
|
||||||
|
*
|
||||||
|
* @param string $proxyListString Proxy list in format http://USER:PASSWORD@HOST:PORT or IP:PORT:USER:PASSWORD
|
||||||
|
* @return array Array of valid proxy URLs
|
||||||
|
*/
|
||||||
|
function parseProxyList($proxyListString) {
|
||||||
|
$proxies = [];
|
||||||
|
$lines = preg_split('/[\r\n,]+/', $proxyListString);
|
||||||
|
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
$line = trim($line);
|
||||||
|
if (empty($line)) continue;
|
||||||
|
|
||||||
|
// Format 1: http://USER:PASSWORD@HOST:PORT
|
||||||
|
if (preg_match('/^https?:\/\/[^:]+:[^@]+@[^:]+:\d+$/i', $line)) {
|
||||||
|
$proxies[] = $line;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format 2: IP:PORT:USER:PASSWORD
|
||||||
|
if (preg_match('/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d+):([^:]+):(.+)$/', $line, $matches)) {
|
||||||
|
$ip = $matches[1];
|
||||||
|
$port = $matches[2];
|
||||||
|
$user = $matches[3];
|
||||||
|
$password = $matches[4];
|
||||||
|
|
||||||
|
// Convert to standard format
|
||||||
|
$proxies[] = "http://{$user}:{$password}@{$ip}:{$port}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $proxies;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download proxy list from URL using php-curl-class
|
||||||
|
*
|
||||||
|
* @param string $url URL to download proxy list from
|
||||||
|
* @param CLImate $climate CLImate instance for output
|
||||||
|
* @return string|false Downloaded content or false on failure
|
||||||
|
*/
|
||||||
|
function downloadProxyList($url, $climate = null) {
|
||||||
|
$curl = new Curl();
|
||||||
|
|
||||||
|
// Configure cURL options
|
||||||
|
$curl->setTimeout(30);
|
||||||
|
$curl->setConnectTimeout(10);
|
||||||
|
$curl->setUserAgent('Marreta Proxy Updater/1.0');
|
||||||
|
$curl->setHeader('Accept', 'text/plain, text/html, */*');
|
||||||
|
$curl->setHeader('Accept-Encoding', 'gzip, deflate');
|
||||||
|
$curl->setOpt(CURLOPT_FOLLOWLOCATION, true);
|
||||||
|
$curl->setOpt(CURLOPT_MAXREDIRS, 3);
|
||||||
|
$curl->setOpt(CURLOPT_SSL_VERIFYPEER, false);
|
||||||
|
$curl->setOpt(CURLOPT_SSL_VERIFYHOST, false);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ($climate) {
|
||||||
|
$climate->out('Making HTTP request with php-curl-class...');
|
||||||
|
}
|
||||||
|
|
||||||
|
$curl->get($url);
|
||||||
|
|
||||||
|
if ($curl->error) {
|
||||||
|
$errorMsg = 'cURL request failed: ' . $curl->errorMessage . ' (Code: ' . $curl->errorCode . ')';
|
||||||
|
|
||||||
|
if ($climate) {
|
||||||
|
$climate->red()->out($errorMsg);
|
||||||
|
} else {
|
||||||
|
error_log($errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$statusCode = $curl->httpStatusCode;
|
||||||
|
|
||||||
|
if ($climate) {
|
||||||
|
$climate->out('HTTP Status Code: ' . $statusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($statusCode === 200) {
|
||||||
|
$content = $curl->response;
|
||||||
|
|
||||||
|
if ($climate) {
|
||||||
|
$contentType = $curl->responseHeaders['Content-Type'] ?? 'unknown';
|
||||||
|
$climate->out('Content-Type: ' . $contentType);
|
||||||
|
$climate->out('Content-Length: ' . strlen($content) . ' bytes');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($climate) {
|
||||||
|
$climate->yellow()->out('Unexpected HTTP status code: ' . $statusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$errorMsg = 'Unexpected error during download: ' . $e->getMessage();
|
||||||
|
|
||||||
|
if ($climate) {
|
||||||
|
$climate->red()->out($errorMsg);
|
||||||
|
} else {
|
||||||
|
error_log($errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
$curl->close();
|
||||||
|
}
|
||||||
|
}
|
0
app/cache/database/.gitkeep
vendored
Normal file
|
@ -5,7 +5,8 @@
|
||||||
"php-curl-class/php-curl-class": "^11.0",
|
"php-curl-class/php-curl-class": "^11.0",
|
||||||
"php-webdriver/webdriver": "^1.15",
|
"php-webdriver/webdriver": "^1.15",
|
||||||
"monolog/monolog": "^3.8.1",
|
"monolog/monolog": "^3.8.1",
|
||||||
"nikic/fast-route": "^1.3"
|
"nikic/fast-route": "^1.3",
|
||||||
|
"league/climate": "^3.8"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
|
|
@ -1,84 +1,47 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main configuration file
|
* System configuration manager
|
||||||
* Arquivo de configuração principal
|
* - Loads and validates environment variables
|
||||||
*
|
* - Defines global constants for system settings
|
||||||
* This file contains all global system settings, including:
|
* - Manages security rules and external service configs
|
||||||
* Este arquivo contém todas as configurações globais do sistema, incluindo:
|
|
||||||
*
|
|
||||||
* - Environment variables loading / Carregamento de variáveis de ambiente
|
|
||||||
* - System constants definition / Definições de constantes do sistema
|
|
||||||
* - Security settings / Configurações de segurança
|
|
||||||
* - Bot and user agent settings / Configurações de bots e user agents
|
|
||||||
* - Blocked domains list / Lista de domínios bloqueados
|
|
||||||
* - S3 cache settings / Configurações de cache S3
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
require_once __DIR__ . '/vendor/autoload.php';
|
require_once __DIR__ . '/vendor/autoload.php';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Initialize environment variables
|
// Load environment variables
|
||||||
// Inicializa as variáveis de ambiente
|
|
||||||
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
|
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
|
||||||
$dotenv->load();
|
$dotenv->load();
|
||||||
|
|
||||||
// Validate required fields
|
// Validate required fields
|
||||||
// Valida campos obrigatórios
|
|
||||||
$dotenv->required([
|
$dotenv->required([
|
||||||
'SITE_NAME',
|
'SITE_NAME',
|
||||||
'SITE_DESCRIPTION',
|
'SITE_DESCRIPTION',
|
||||||
'SITE_URL'
|
'SITE_URL'
|
||||||
])->notEmpty();
|
])->notEmpty();
|
||||||
|
|
||||||
// Custom URL validation
|
// Core system settings
|
||||||
// Validação personalizada de URL
|
|
||||||
if (!filter_var($_ENV['SITE_URL'], FILTER_VALIDATE_URL)) {
|
|
||||||
throw new Exception('SITE_URL must be a valid URL');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Basic system settings
|
|
||||||
* Configurações básicas do sistema
|
|
||||||
*/
|
|
||||||
define('SITE_NAME', $_ENV['SITE_NAME']);
|
define('SITE_NAME', $_ENV['SITE_NAME']);
|
||||||
define('SITE_DESCRIPTION', $_ENV['SITE_DESCRIPTION']);
|
define('SITE_DESCRIPTION', $_ENV['SITE_DESCRIPTION']);
|
||||||
define('SITE_URL', $_ENV['SITE_URL']);
|
define('SITE_URL', $_ENV['SITE_URL']);
|
||||||
|
define('CLEANUP_DAYS', $_ENV['CLEANUP_DAYS'] ?? 0);
|
||||||
|
|
||||||
// Optional settings with default values
|
// Optional settings with defaults
|
||||||
// Configurações opcionais com valores padrão
|
|
||||||
define('DNS_SERVERS', $_ENV['DNS_SERVERS'] ?? '1.1.1.1, 8.8.8.8');
|
define('DNS_SERVERS', $_ENV['DNS_SERVERS'] ?? '1.1.1.1, 8.8.8.8');
|
||||||
define('DISABLE_CACHE', isset($_ENV['DISABLE_CACHE']) ?
|
define('DISABLE_CACHE', isset($_ENV['DISABLE_CACHE']) ? filter_var($_ENV['DISABLE_CACHE'], FILTER_VALIDATE_BOOLEAN) : false);
|
||||||
filter_var($_ENV['DISABLE_CACHE'], FILTER_VALIDATE_BOOLEAN) : false);
|
|
||||||
define('SELENIUM_HOST', $_ENV['SELENIUM_HOST'] ?? 'localhost:4444');
|
define('SELENIUM_HOST', $_ENV['SELENIUM_HOST'] ?? 'localhost:4444');
|
||||||
define('CACHE_DIR', __DIR__ . '/cache');
|
define('CACHE_DIR', __DIR__ . '/cache');
|
||||||
define('LANGUAGE', $_ENV['LANGUAGE'] ?? 'pt-br');
|
define('LANGUAGE', $_ENV['LANGUAGE'] ?? 'pt-br');
|
||||||
|
|
||||||
/**
|
// Logging configuration
|
||||||
* Redis settings
|
define('LOG_LEVEL', $_ENV['LOG_LEVEL'] ?? 'WARNING'); // DEBUG, INFO, WARNING, ERROR, CRITICAL
|
||||||
* Configurações do Redis
|
|
||||||
*/
|
|
||||||
define('REDIS_HOST', $_ENV['REDIS_HOST'] ?? 'localhost');
|
|
||||||
define('REDIS_PORT', $_ENV['REDIS_PORT'] ?? 6379);
|
|
||||||
define('REDIS_PREFIX', $_ENV['REDIS_PREFIX'] ?? 'marreta:');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Logging settings
|
|
||||||
* Configurações de log
|
|
||||||
*/
|
|
||||||
define('LOG_LEVEL', $_ENV['LOG_LEVEL'] ?? 'WARNING'); // Available: DEBUG, INFO, WARNING, ERROR, CRITICAL
|
|
||||||
define('LOG_DAYS_TO_KEEP', 7);
|
define('LOG_DAYS_TO_KEEP', 7);
|
||||||
|
|
||||||
/**
|
// S3 cache configuration
|
||||||
* S3 Cache settings
|
define('S3_CACHE_ENABLED', isset($_ENV['S3_CACHE_ENABLED']) ? filter_var($_ENV['S3_CACHE_ENABLED'], FILTER_VALIDATE_BOOLEAN) : false);
|
||||||
* Configurações de Cache S3
|
|
||||||
*/
|
|
||||||
define('S3_CACHE_ENABLED', isset($_ENV['S3_CACHE_ENABLED']) ?
|
|
||||||
filter_var($_ENV['S3_CACHE_ENABLED'], FILTER_VALIDATE_BOOLEAN) : false);
|
|
||||||
|
|
||||||
if (S3_CACHE_ENABLED) {
|
if (S3_CACHE_ENABLED) {
|
||||||
// Validate required S3 settings when S3 cache is enabled
|
|
||||||
// Valida configurações obrigatórias do S3 quando o cache S3 está ativado
|
|
||||||
$dotenv->required([
|
$dotenv->required([
|
||||||
'S3_ACCESS_KEY',
|
'S3_ACCESS_KEY',
|
||||||
'S3_SECRET_KEY',
|
'S3_SECRET_KEY',
|
||||||
|
@ -94,14 +57,21 @@ try {
|
||||||
define('S3_ENDPOINT', $_ENV['S3_ENDPOINT'] ?? null);
|
define('S3_ENDPOINT', $_ENV['S3_ENDPOINT'] ?? null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Load security rules
|
||||||
* Load system configurations
|
|
||||||
* Carrega as configurações do sistema
|
|
||||||
*/
|
|
||||||
define('BLOCKED_DOMAINS', require __DIR__ . '/data/blocked_domains.php');
|
define('BLOCKED_DOMAINS', require __DIR__ . '/data/blocked_domains.php');
|
||||||
define('DOMAIN_RULES', require __DIR__ . '/data/domain_rules.php');
|
define('DOMAIN_RULES', require __DIR__ . '/data/domain_rules.php');
|
||||||
define('GLOBAL_RULES', require __DIR__ . '/data/global_rules.php');
|
define('GLOBAL_RULES', require __DIR__ . '/data/global_rules.php');
|
||||||
|
|
||||||
|
// Load DMCA domains from JSON file
|
||||||
|
$dmcaDomainsFile = __DIR__ . '/cache/dmca_domains.json';
|
||||||
|
if (file_exists($dmcaDomainsFile)) {
|
||||||
|
$dmcaDomainsJson = file_get_contents($dmcaDomainsFile);
|
||||||
|
$dmcaDomains = json_decode($dmcaDomainsJson, true);
|
||||||
|
define('DMCA_DOMAINS', is_array($dmcaDomains) ? $dmcaDomains : []);
|
||||||
|
} else {
|
||||||
|
define('DMCA_DOMAINS', []);
|
||||||
|
}
|
||||||
|
|
||||||
} catch (Dotenv\Exception\ValidationException $e) {
|
} catch (Dotenv\Exception\ValidationException $e) {
|
||||||
die('Environment Error: ' . $e->getMessage());
|
die('Environment Error: ' . $e->getMessage());
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
|
|
|
@ -2,17 +2,18 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of blocked domains
|
* List of blocked domains
|
||||||
* Lista de domínios bloqueados
|
|
||||||
*
|
*
|
||||||
* Defines domains that cannot be accessed by the system
|
* Defines domains that cannot be accessed by the system
|
||||||
* due to usage policies or technical restrictions
|
* due to usage policies or technical restrictions
|
||||||
*
|
|
||||||
* Define os domínios que não podem ser acessados pelo sistema
|
|
||||||
* por questões de política de uso ou restrições técnicas
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
$host = parse_url(defined('SITE_URL') ? SITE_URL : '', PHP_URL_HOST);
|
||||||
return [
|
return [
|
||||||
// News sites / Sites de notícias
|
$host,
|
||||||
//-- Content behind login access / Conteúdo fica atrás de um acesso de login
|
'localhost',
|
||||||
|
'127.0.0.1',
|
||||||
|
// News sites
|
||||||
|
//-- Content behind login access/hard paywall
|
||||||
'wsj.com',
|
'wsj.com',
|
||||||
'piaui.folha.uol.com.br',
|
'piaui.folha.uol.com.br',
|
||||||
'economist.com',
|
'economist.com',
|
||||||
|
@ -31,14 +32,16 @@ return [
|
||||||
'mittelbayerische.de',
|
'mittelbayerische.de',
|
||||||
'josimarfootball.com',
|
'josimarfootball.com',
|
||||||
'nordsee-zeitung.de',
|
'nordsee-zeitung.de',
|
||||||
//-- Technical access blocking / Bloqueio técnico de acesso ao conteúdo
|
'zorgvisie.nl',
|
||||||
|
// List of common blocked sites to avoid unnecessary requests
|
||||||
|
//-- Technical access blocking
|
||||||
'bloomberg.com',
|
'bloomberg.com',
|
||||||
'sportskeeda.com',
|
'sportskeeda.com',
|
||||||
'kansascity.com',
|
'kansascity.com',
|
||||||
'fastcompany.com',
|
'fastcompany.com',
|
||||||
'expressnews.com',
|
'expressnews.com',
|
||||||
'nydailynews.com',
|
'nydailynews.com',
|
||||||
// Tracking services / Serviços de rastreamento
|
//-- Tracking services
|
||||||
'metaffiliation.com',
|
'metaffiliation.com',
|
||||||
'google-analytics.com',
|
'google-analytics.com',
|
||||||
'googletagmanager.com',
|
'googletagmanager.com',
|
||||||
|
@ -59,7 +62,7 @@ return [
|
||||||
'fullstory.com',
|
'fullstory.com',
|
||||||
'heap.io',
|
'heap.io',
|
||||||
'clearbrain.com',
|
'clearbrain.com',
|
||||||
// Social networks / Redes sociais
|
//-- Social networks
|
||||||
'facebook.com',
|
'facebook.com',
|
||||||
'instagram.com',
|
'instagram.com',
|
||||||
'twitter.com',
|
'twitter.com',
|
||||||
|
@ -73,7 +76,7 @@ return [
|
||||||
'redd.it',
|
'redd.it',
|
||||||
'bsky.app',
|
'bsky.app',
|
||||||
'threads.net',
|
'threads.net',
|
||||||
// Streaming services / Serviços de streaming
|
//-- Streaming services
|
||||||
'netflix.com',
|
'netflix.com',
|
||||||
'hulu.com',
|
'hulu.com',
|
||||||
'disneyplus.com',
|
'disneyplus.com',
|
||||||
|
@ -81,32 +84,32 @@ return [
|
||||||
'spotify.com',
|
'spotify.com',
|
||||||
'youtube.com',
|
'youtube.com',
|
||||||
'twitch.tv',
|
'twitch.tv',
|
||||||
// E-commerce sites / Sites de comércio eletrônico
|
//-- E-commerce sites
|
||||||
'amazon.com',
|
'amazon.com',
|
||||||
'ebay.com',
|
'ebay.com',
|
||||||
'aliexpress.com',
|
'aliexpress.com',
|
||||||
'mercadolivre.com.br',
|
'mercadolivre.com.br',
|
||||||
'shopify.com',
|
'shopify.com',
|
||||||
// File sharing / Compartilhamento de arquivos
|
//-- File sharing
|
||||||
'mega.nz',
|
'mega.nz',
|
||||||
'mediafire.com',
|
'mediafire.com',
|
||||||
'wetransfer.com',
|
'wetransfer.com',
|
||||||
'dropbox.com',
|
'dropbox.com',
|
||||||
'torrent9.pe',
|
'torrent9.pe',
|
||||||
'thepiratebay.org',
|
'thepiratebay.org',
|
||||||
// Adult sites / Sites adultos
|
//-- Adult sites
|
||||||
'pornhub.com',
|
'pornhub.com',
|
||||||
'xvideos.com',
|
'xvideos.com',
|
||||||
'xnxx.com',
|
'xnxx.com',
|
||||||
'onlyfans.com',
|
'onlyfans.com',
|
||||||
'privacy.com.br',
|
'privacy.com.br',
|
||||||
'fatalmodel.com',
|
'fatalmodel.com',
|
||||||
// Betting and gaming / Apostas e jogos
|
//-- Betting and gaming
|
||||||
'bet365.com',
|
'bet365.com',
|
||||||
'betfair.com',
|
'betfair.com',
|
||||||
'pokerstars.com',
|
'pokerstars.com',
|
||||||
'casino.com',
|
'casino.com',
|
||||||
// Other popular sites / Outros sites populares
|
//-- Other popular sites
|
||||||
'github.com',
|
'github.com',
|
||||||
'stackoverflow.com',
|
'stackoverflow.com',
|
||||||
'wikipedia.org',
|
'wikipedia.org',
|
||||||
|
@ -123,7 +126,6 @@ return [
|
||||||
'jusbrasil.com.br',
|
'jusbrasil.com.br',
|
||||||
'glassdoor.com.br',
|
'glassdoor.com.br',
|
||||||
'gov.br',
|
'gov.br',
|
||||||
'medium.com',
|
|
||||||
'stackoverflow.com',
|
'stackoverflow.com',
|
||||||
'hoteis.com',
|
'hoteis.com',
|
||||||
'amazon.com',
|
'amazon.com',
|
||||||
|
|
|
@ -2,50 +2,60 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specific rule configurations for individual domains
|
* Specific rule configurations for individual domains
|
||||||
* Configurações específicas de regras para domínios individuais
|
|
||||||
*
|
|
||||||
* This file contains custom rules for specific sites, allowing
|
|
||||||
* system behavior adjustment for each domain individually.
|
|
||||||
*
|
|
||||||
* Este arquivo contém regras personalizadas para sites específicos, permitindo
|
|
||||||
* ajustar o comportamento do sistema para cada domínio individualmente.
|
|
||||||
*
|
*
|
||||||
* Domain rule structure / Estrutura das regras por domínio:
|
* Domain rule structure / Estrutura das regras por domínio:
|
||||||
* - userAgent: Define custom User-Agent for the domain / Define um User-Agent personalizado para o domínio
|
* - userAgent: Define custom User-Agent for the domain
|
||||||
* - headers: Custom HTTP headers for requests / Headers HTTP personalizados para requisições
|
* - headers: Custom HTTP headers for requests
|
||||||
* - idElementRemove: Array of HTML IDs to be removed / Array de IDs HTML que devem ser removidos da página
|
* - idElementRemove: Array of HTML IDs to be removed
|
||||||
* - classElementRemove: Array of HTML classes to be removed / Array de classes HTML que devem ser removidas
|
* - classElementRemove: Array of HTML classes to be removed
|
||||||
* - scriptTagRemove: Array of scripts to be removed (partial match) / Array de scripts que devem ser removidos (partial match)
|
* - scriptTagRemove: Array of scripts to be removed (partial match)
|
||||||
* - cookies: Associative array of cookies to be set (null removes cookie) / Array associativo de cookies a serem definidos (null remove o cookie)
|
* - cookies: Associative array of cookies to be set (null removes cookie)
|
||||||
* - classAttrRemove: Array of classes to be removed from elements / Array de classes a serem removidas de elementos
|
* - classAttrRemove: Array of classes to be removed from elements
|
||||||
* - customCode: String containing custom JavaScript code / String contendo código JavaScript personalizado
|
* - customCode: String containing custom JavaScript code
|
||||||
* - customStyle: String containing custom CSS code / String contendo código CSS personalizado
|
* - customStyle: String containing custom CSS code
|
||||||
* - excludeGlobalRules: Associative array of global rules to exclude for this domain / Array associativo de regras globais a serem excluídas para este domínio
|
* - proxy: Enable proxy in Guzzle or Selenium requests
|
||||||
* Example / Exemplo:
|
* - excludeGlobalRules: Associative array of global rules to exclude for this domain
|
||||||
|
* Example:
|
||||||
* 'excludeGlobalRules' => [
|
* 'excludeGlobalRules' => [
|
||||||
* 'scriptTagRemove' => ['gtm.js', 'ga.js'], // Excludes specific scripts from global rules / Exclui scripts específicos das regras globais
|
* 'scriptTagRemove' => ['gtm.js', 'ga.js'],
|
||||||
* 'classElementRemove' => ['subscription'] // Excludes specific classes from global rules / Exclui classes específicas das regras globais
|
* 'classElementRemove' => ['subscription']
|
||||||
|
* ]
|
||||||
|
* - fetchStrategies: String indicating which fetch strategy to use. Available values:
|
||||||
|
* - fetchContent: Use standard fetch with domain rules
|
||||||
|
* - fetchFromWaybackMachine: Try to fetch from Internet Archive
|
||||||
|
* - fetchFromSelenium: Use Selenium for extraction
|
||||||
|
* - socialReferrers: Add random social media headers
|
||||||
|
* - fromGoogleBot: Adds simulation of request coming from Google Bot
|
||||||
|
* - removeElementsByTag: Remove specific elements via DOM
|
||||||
|
* - removeCustomAttr: Remove custom attributes from elements
|
||||||
|
* - urlMods: Modify the URL before fetching content.
|
||||||
|
* Example:
|
||||||
|
* 'urlMods' => [
|
||||||
|
* 'query' => [
|
||||||
|
* [
|
||||||
|
* 'key' => 'amp',
|
||||||
|
* 'value' => '1'
|
||||||
|
* ]
|
||||||
|
* ]
|
||||||
* ]
|
* ]
|
||||||
* - fetchStrategies: String indicating which fetch strategy to use. Available values: / String indicando qual estratégia de fetch usar. Valores disponíveis:
|
|
||||||
* - fetchContent: Use standard fetch with domain rules / Usa fetch padrão com regras do domínio
|
|
||||||
* - fetchFromWaybackMachine: Try to fetch from Internet Archive / Tenta buscar do Internet Archive
|
|
||||||
* - fetchFromSelenium: Use Selenium for extraction / Usa Selenium para extração
|
|
||||||
* - socialReferrers: Add random social media headers / Adiciona headers randomicos de redes sociais
|
|
||||||
* - fromGoogleBot: Adds simulation of request coming from Google Bot / Adiciona simulação de requisição vinda do Google Bot
|
|
||||||
* - removeElementsByTag: Remove specific elements via DOM / Remove elementos especificos via DOM
|
|
||||||
* - removeCustomAttr: Remove custom attributes from elements / Remove custom attributes from elements
|
|
||||||
*/
|
*/
|
||||||
return [
|
return [
|
||||||
'nsctotal.com.br' => [
|
'nsctotal.com.br' => [
|
||||||
'userAgent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
|
'userAgent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
|
||||||
],
|
],
|
||||||
'elcorreo.com' => [
|
'elcorreo.com' => [
|
||||||
'idElementRemove' => ['didomi-popup','engagement-top'],
|
'idElementRemove' => ['didomi-popup', 'engagement-top'],
|
||||||
'classElementRemove' => ['content-exclusive-bg'],
|
'classElementRemove' => ['content-exclusive-bg'],
|
||||||
'classAttrRemove' => ['didomi-popup-open','paywall'],
|
'classAttrRemove' => ['didomi-popup-open', 'paywall'],
|
||||||
'fromGoogleBot' => true,
|
'fromGoogleBot' => true,
|
||||||
'removeElementsByTag' => ['style'],
|
'removeElementsByTag' => ['style'],
|
||||||
'removeCustomAttr' => ['hidden','data-*']
|
'removeCustomAttr' => ['hidden', 'data-*']
|
||||||
|
],
|
||||||
|
'wired.com' => [
|
||||||
|
'scriptTagRemove' => ['.js'],
|
||||||
|
],
|
||||||
|
'newyorker.com' => [
|
||||||
|
'scriptTagRemove' => ['.js'],
|
||||||
],
|
],
|
||||||
'globo.com' => [
|
'globo.com' => [
|
||||||
'idElementRemove' => ['cookie-banner-lgpd', 'paywall-cpt', 'mc-read-more-wrapper', 'paywall-cookie-content', 'paywall-cpt'],
|
'idElementRemove' => ['cookie-banner-lgpd', 'paywall-cpt', 'mc-read-more-wrapper', 'paywall-cookie-content', 'paywall-cpt'],
|
||||||
|
@ -61,18 +71,33 @@ return [
|
||||||
'gauchazh.clicrbs.com.br' => [
|
'gauchazh.clicrbs.com.br' => [
|
||||||
'idElementRemove' => ['paywallTemplate'],
|
'idElementRemove' => ['paywallTemplate'],
|
||||||
'classAttrRemove' => ['m-paid-content', 'paid-content-apply'],
|
'classAttrRemove' => ['m-paid-content', 'paid-content-apply'],
|
||||||
'scriptTagRemove' => ['vendors-8'],
|
'scriptTagRemove' => ['vendors-9','vendors-10','vendors-11'],
|
||||||
'excludeGlobalRules' => [
|
'excludeGlobalRules' => [
|
||||||
'classElementRemove' => ['paid-content']
|
'classElementRemove' => ['paid-content']
|
||||||
],
|
],
|
||||||
'fetchStrategies' => 'fetchFromSelenium',
|
'proxy' => true,
|
||||||
],
|
],
|
||||||
'reuters.com' => [
|
'reuters.com' => [
|
||||||
'classElementRemove' => ['leaderboard__container'],
|
'classElementRemove' => ['leaderboard__container'],
|
||||||
'fetchStrategies' => 'fetchFromSelenium',
|
'fetchStrategies' => 'fetchFromSelenium',
|
||||||
],
|
],
|
||||||
|
'cnn.com' => [
|
||||||
|
'fetchStrategies' => 'fetchFromSelenium',
|
||||||
|
],
|
||||||
|
'lepoint.fr' => [
|
||||||
|
'classElementRemove' => ['paywall'],
|
||||||
|
],
|
||||||
|
'gamestar.de' => [
|
||||||
|
'classElementRemove' => ['plus-teaser'],
|
||||||
|
'classAttrRemove' => ['plus-'],
|
||||||
|
'idElementRemove' => ['commentReload']
|
||||||
|
],
|
||||||
|
'heise.de' => [
|
||||||
|
'classAttrRemove' => ['curtain__purchase-container'],
|
||||||
|
'removeElementsByTag' => ['a-gift']
|
||||||
|
],
|
||||||
'fortune.com' => [
|
'fortune.com' => [
|
||||||
'classElementRemove' => ['latest-popular-module','own','drawer-menu'],
|
'classElementRemove' => ['latest-popular-module', 'own', 'drawer-menu'],
|
||||||
'fetchStrategies' => 'fetchFromSelenium',
|
'fetchStrategies' => 'fetchFromSelenium',
|
||||||
'browser' => 'chrome',
|
'browser' => 'chrome',
|
||||||
'scriptTagRemove' => ['queryly.com'],
|
'scriptTagRemove' => ['queryly.com'],
|
||||||
|
@ -81,10 +106,6 @@ return [
|
||||||
'idElementRemove' => ['cboxOverlay'],
|
'idElementRemove' => ['cboxOverlay'],
|
||||||
'fetchStrategies' => 'fetchFromSelenium',
|
'fetchStrategies' => 'fetchFromSelenium',
|
||||||
],
|
],
|
||||||
'washingtonpost.com' => [
|
|
||||||
'classElementRemove' => ['paywall-overlay'],
|
|
||||||
'fetchStrategies' => 'fetchFromSelenium',
|
|
||||||
],
|
|
||||||
'oantagonista.com.br' => [
|
'oantagonista.com.br' => [
|
||||||
'fetchStrategies' => 'fetchFromSelenium',
|
'fetchStrategies' => 'fetchFromSelenium',
|
||||||
],
|
],
|
||||||
|
@ -137,6 +158,21 @@ return [
|
||||||
'paywall_access' => 'true'
|
'paywall_access' => 'true'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
'ftm.nl' => [
|
||||||
|
'fetchStrategies' => 'fetchFromSelenium',
|
||||||
|
'removeCustomAttr' => ['dialog', 'iframe'],
|
||||||
|
'classElementRemove' => ['modal'],
|
||||||
|
'scriptTagRemove' => ['footer.min', 'diffuser.js', 'insight.ftm.nl'],
|
||||||
|
'classAttrRemove' => ['hasBlockingOverlay', 'localstorage']
|
||||||
|
],
|
||||||
|
'denikn.cz' => [
|
||||||
|
'idElementRemove' => ['e_lock__hard']
|
||||||
|
],
|
||||||
|
'dtest.cz' => [
|
||||||
|
'fetchStrategies' => 'fetchFromSelenium',
|
||||||
|
'classAttrRemove' => ['is-hidden-compare'],
|
||||||
|
'classElementRemove' => ['cc-window']
|
||||||
|
],
|
||||||
'uol.com.br' => [
|
'uol.com.br' => [
|
||||||
'scriptTagRemove' => ['me.jsuol.com.br', 'c.jsuol.com.br'],
|
'scriptTagRemove' => ['me.jsuol.com.br', 'c.jsuol.com.br'],
|
||||||
'classElementRemove' => ['header-top-wrapper'],
|
'classElementRemove' => ['header-top-wrapper'],
|
||||||
|
@ -150,6 +186,12 @@ return [
|
||||||
'nzherald.co.nz' => [
|
'nzherald.co.nz' => [
|
||||||
'fetchStrategies' => 'fetchFromSelenium',
|
'fetchStrategies' => 'fetchFromSelenium',
|
||||||
],
|
],
|
||||||
|
'onetz.de' => [
|
||||||
|
'idElementRemove' => ['checkout-container'],
|
||||||
|
'classElementRemove' => ['tp-backdrop','dm-nobg'],
|
||||||
|
'classAttrRemove' => ['field-dnt-body-pp'],
|
||||||
|
'scriptTagRemove' => ['.js'],
|
||||||
|
],
|
||||||
'opovo.com.br' => [
|
'opovo.com.br' => [
|
||||||
'classElementRemove' => ['screen-loading', 'overlay-advise']
|
'classElementRemove' => ['screen-loading', 'overlay-advise']
|
||||||
],
|
],
|
||||||
|
@ -159,7 +201,8 @@ return [
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'theverge.com' => [
|
'theverge.com' => [
|
||||||
'fetchStrategies' => 'fetchFromSelenium',
|
'scriptTagRemove' => 'zephr',
|
||||||
|
'classElementRemove' => 'zephr'
|
||||||
],
|
],
|
||||||
'economist.com' => [
|
'economist.com' => [
|
||||||
'cookies' => [
|
'cookies' => [
|
||||||
|
@ -178,15 +221,8 @@ return [
|
||||||
}
|
}
|
||||||
'
|
'
|
||||||
],
|
],
|
||||||
'ft.com' => [
|
|
||||||
'cookies' => [
|
|
||||||
'next-flags' => null,
|
|
||||||
'next:ads' => null
|
|
||||||
],
|
|
||||||
'fromGoogleBot' => true
|
|
||||||
],
|
|
||||||
'nytimes.com' => [
|
'nytimes.com' => [
|
||||||
'idElementRemove' => ['gateway-content','site-index'],
|
'idElementRemove' => ['gateway-content', 'site-index', 'complianceOverlay'],
|
||||||
'customCode' => '
|
'customCode' => '
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
const walk = document.createTreeWalker(
|
const walk = document.createTreeWalker(
|
||||||
|
@ -235,7 +271,7 @@ return [
|
||||||
position: relative !important;
|
position: relative !important;
|
||||||
}
|
}
|
||||||
',
|
',
|
||||||
'fetchStrategies' => 'fetchFromSelenium',
|
'fetchStrategies' => 'fetchFromWaybackMachine',
|
||||||
'excludeGlobalRules' => [
|
'excludeGlobalRules' => [
|
||||||
'scriptTagRemove' => [
|
'scriptTagRemove' => [
|
||||||
'gtm.js',
|
'gtm.js',
|
||||||
|
@ -336,8 +372,358 @@ return [
|
||||||
'_pctx' => null
|
'_pctx' => null
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
'thestar.com' => [
|
||||||
// Domain test
|
'classElementRemove' => ['subscriber-offers', 'subscriber-only', 'subscription-required', 'redacted-overlay', 'subscriber-hide', 'tnt-ads-container'],
|
||||||
|
'customCode' => '
|
||||||
|
window.localStorage.clear();
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const paywall = document.querySelectorAll(\'div.subscriber-offers\');
|
||||||
|
paywall.forEach(el => { el.remove(); });
|
||||||
|
const subscriber_only = document.querySelectorAll(\'div.subscriber-only\');
|
||||||
|
for (const elem of subscriber_only) {
|
||||||
|
if (elem.classList.contains(\'encrypted-content\') && typeof DOMPurify !== \'undefined\' && typeof unscramble !== \'undefined\') {
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const doc = parser.parseFromString(\'<div>\' + DOMPurify.sanitize(unscramble(elem.innerText)) + \'</div>\', \'text/html\');
|
||||||
|
const content_new = doc.querySelector(\'div\');
|
||||||
|
elem.parentNode.replaceChild(content_new, elem);
|
||||||
|
}
|
||||||
|
elem.removeAttribute(\'style\');
|
||||||
|
elem.removeAttribute(\'class\');
|
||||||
|
}
|
||||||
|
const banners = document.querySelectorAll(\'div.subscription-required, div.redacted-overlay, div.subscriber-hide, div.tnt-ads-container\');
|
||||||
|
banners.forEach(el => { el.remove(); });
|
||||||
|
const ads = document.querySelectorAll(\'div.tnt-ads-container, div[class*="adLabelWrapper"]\');
|
||||||
|
ads.forEach(el => { el.remove(); });
|
||||||
|
const recommendations = document.querySelectorAll(\'div[id^="tncms-region-article"]\');
|
||||||
|
recommendations.forEach(el => { el.remove(); });
|
||||||
|
});
|
||||||
|
'
|
||||||
|
],
|
||||||
|
'niagarafallsreview.ca' => [
|
||||||
|
'classElementRemove' => ['subscriber-offers', 'subscriber-only', 'subscription-required', 'redacted-overlay', 'subscriber-hide', 'tnt-ads-container'],
|
||||||
|
'customCode' => '
|
||||||
|
window.localStorage.clear();
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const paywall = document.querySelectorAll(\'div.subscriber-offers\');
|
||||||
|
paywall.forEach(el => { el.remove(); });
|
||||||
|
const subscriber_only = document.querySelectorAll(\'div.subscriber-only\');
|
||||||
|
for (const elem of subscriber_only) {
|
||||||
|
if (elem.classList.contains(\'encrypted-content\') && typeof DOMPurify !== \'undefined\' && typeof unscramble !== \'undefined\') {
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const doc = parser.parseFromString(\'<div>\' + DOMPurify.sanitize(unscramble(elem.innerText)) + \'</div>\', \'text/html\');
|
||||||
|
const content_new = doc.querySelector(\'div\');
|
||||||
|
elem.parentNode.replaceChild(content_new, elem);
|
||||||
|
}
|
||||||
|
elem.removeAttribute(\'style\');
|
||||||
|
elem.removeAttribute(\'class\');
|
||||||
|
}
|
||||||
|
const banners = document.querySelectorAll(\'div.subscription-required, div.redacted-overlay, div.subscriber-hide, div.tnt-ads-container\');
|
||||||
|
banners.forEach(el => { el.remove(); });
|
||||||
|
const ads = document.querySelectorAll(\'div.tnt-ads-container, div[class*="adLabelWrapper"]\');
|
||||||
|
ads.forEach(el => { el.remove(); });
|
||||||
|
const recommendations = document.querySelectorAll(\'div[id^="tncms-region-article"]\');
|
||||||
|
recommendations.forEach(el => { el.remove(); });
|
||||||
|
});
|
||||||
|
'
|
||||||
|
],
|
||||||
|
'thepeterboroughexaminer.com' => [
|
||||||
|
'classElementRemove' => ['subscriber-offers', 'subscriber-only', 'subscription-required', 'redacted-overlay', 'subscriber-hide', 'tnt-ads-container'],
|
||||||
|
'customCode' => '
|
||||||
|
window.localStorage.clear();
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const paywall = document.querySelectorAll(\'div.subscriber-offers\');
|
||||||
|
paywall.forEach(el => { el.remove(); });
|
||||||
|
const subscriber_only = document.querySelectorAll(\'div.subscriber-only\');
|
||||||
|
for (const elem of subscriber_only) {
|
||||||
|
if (elem.classList.contains(\'encrypted-content\') && typeof DOMPurify !== \'undefined\' && typeof unscramble !== \'undefined\') {
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const doc = parser.parseFromString(\'<div>\' + DOMPurify.sanitize(unscramble(elem.innerText)) + \'</div>\', \'text/html\');
|
||||||
|
const content_new = doc.querySelector(\'div\');
|
||||||
|
elem.parentNode.replaceChild(content_new, elem);
|
||||||
|
}
|
||||||
|
elem.removeAttribute(\'style\');
|
||||||
|
elem.removeAttribute(\'class\');
|
||||||
|
}
|
||||||
|
const banners = document.querySelectorAll(\'div.subscription-required, div.redacted-overlay, div.subscriber-hide, div.tnt-ads-container\');
|
||||||
|
banners.forEach(el => { el.remove(); });
|
||||||
|
const ads = document.querySelectorAll(\'div.tnt-ads-container, div[class*="adLabelWrapper"]\');
|
||||||
|
ads.forEach(el => { el.remove(); });
|
||||||
|
const recommendations = document.querySelectorAll(\'div[id^="tncms-region-article"]\');
|
||||||
|
recommendations.forEach(el => { el.remove(); });
|
||||||
|
});
|
||||||
|
'
|
||||||
|
],
|
||||||
|
'therecord.com' => [
|
||||||
|
'classElementRemove' => ['subscriber-offers', 'subscriber-only', 'subscription-required', 'redacted-overlay', 'subscriber-hide', 'tnt-ads-container'],
|
||||||
|
'customCode' => '
|
||||||
|
window.localStorage.clear();
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const paywall = document.querySelectorAll(\'div.subscriber-offers\');
|
||||||
|
paywall.forEach(el => { el.remove(); });
|
||||||
|
const subscriber_only = document.querySelectorAll(\'div.subscriber-only\');
|
||||||
|
for (const elem of subscriber_only) {
|
||||||
|
if (elem.classList.contains(\'encrypted-content\') && typeof DOMPurify !== \'undefined\' && typeof unscramble !== \'undefined\') {
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const doc = parser.parseFromString(\'<div>\' + DOMPurify.sanitize(unscramble(elem.innerText)) + \'</div>\', \'text/html\');
|
||||||
|
const content_new = doc.querySelector(\'div\');
|
||||||
|
elem.parentNode.replaceChild(content_new, elem);
|
||||||
|
}
|
||||||
|
elem.removeAttribute(\'style\');
|
||||||
|
elem.removeAttribute(\'class\');
|
||||||
|
}
|
||||||
|
const banners = document.querySelectorAll(\'div.subscription-required, div.redacted-overlay, div.subscriber-hide, div.tnt-ads-container\');
|
||||||
|
banners.forEach(el => { el.remove(); });
|
||||||
|
const ads = document.querySelectorAll(\'div.tnt-ads-container, div[class*="adLabelWrapper"]\');
|
||||||
|
ads.forEach(el => { el.remove(); });
|
||||||
|
const recommendations = document.querySelectorAll(\'div[id^="tncms-region-article"]\');
|
||||||
|
recommendations.forEach(el => { el.remove(); });
|
||||||
|
});
|
||||||
|
'
|
||||||
|
],
|
||||||
|
'thespec.com' => [
|
||||||
|
'classElementRemove' => ['subscriber-offers', 'subscriber-only', 'subscription-required', 'redacted-overlay', 'subscriber-hide', 'tnt-ads-container'],
|
||||||
|
'customCode' => '
|
||||||
|
window.localStorage.clear();
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const paywall = document.querySelectorAll(\'div.subscriber-offers\');
|
||||||
|
paywall.forEach(el => { el.remove(); });
|
||||||
|
const subscriber_only = document.querySelectorAll(\'div.subscriber-only\');
|
||||||
|
for (const elem of subscriber_only) {
|
||||||
|
if (elem.classList.contains(\'encrypted-content\') && typeof DOMPurify !== \'undefined\' && typeof unscramble !== \'undefined\') {
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const doc = parser.parseFromString(\'<div>\' + DOMPurify.sanitize(unscramble(elem.innerText)) + \'</div>\', \'text/html\');
|
||||||
|
const content_new = doc.querySelector(\'div\');
|
||||||
|
elem.parentNode.replaceChild(content_new, elem);
|
||||||
|
}
|
||||||
|
elem.removeAttribute(\'style\');
|
||||||
|
elem.removeAttribute(\'class\');
|
||||||
|
}
|
||||||
|
const banners = document.querySelectorAll(\'div.subscription-required, div.redacted-overlay, div.subscriber-hide, div.tnt-ads-container\');
|
||||||
|
banners.forEach(el => { el.remove(); });
|
||||||
|
const ads = document.querySelectorAll(\'div.tnt-ads-container, div[class*="adLabelWrapper"]\');
|
||||||
|
ads.forEach(el => { el.remove(); });
|
||||||
|
const recommendations = document.querySelectorAll(\'div[id^="tncms-region-article"]\');
|
||||||
|
recommendations.forEach(el => { el.remove(); });
|
||||||
|
});
|
||||||
|
'
|
||||||
|
],
|
||||||
|
'wellandtribune.ca' => [
|
||||||
|
'classElementRemove' => ['subscriber-offers', 'subscriber-only', 'subscription-required', 'redacted-overlay', 'subscriber-hide', 'tnt-ads-container'],
|
||||||
|
'customCode' => '
|
||||||
|
window.localStorage.clear();
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const paywall = document.querySelectorAll(\'div.subscriber-offers\');
|
||||||
|
paywall.forEach(el => { el.remove(); });
|
||||||
|
const subscriber_only = document.querySelectorAll(\'div.subscriber-only\');
|
||||||
|
for (const elem of subscriber_only) {
|
||||||
|
if (elem.classList.contains(\'encrypted-content\') && typeof DOMPurify !== \'undefined\' && typeof unscramble !== \'undefined\') {
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const doc = parser.parseFromString(\'<div>\' + DOMPurify.sanitize(unscramble(elem.innerText)) + \'</div>\', \'text/html\');
|
||||||
|
const content_new = doc.querySelector(\'div\');
|
||||||
|
elem.parentNode.replaceChild(content_new, elem);
|
||||||
|
}
|
||||||
|
elem.removeAttribute(\'style\');
|
||||||
|
elem.removeAttribute(\'class\');
|
||||||
|
}
|
||||||
|
const banners = document.querySelectorAll(\'div.subscription-required, div.redacted-overlay, div.subscriber-hide, div.tnt-ads-container\');
|
||||||
|
banners.forEach(el => { el.remove(); });
|
||||||
|
const ads = document.querySelectorAll(\'div.tnt-ads-container, div[class*="adLabelWrapper"]\');
|
||||||
|
ads.forEach(el => { el.remove(); });
|
||||||
|
const recommendations = document.querySelectorAll(\'div[id^="tncms-region-article"]\');
|
||||||
|
recommendations.forEach(el => { el.remove(); });
|
||||||
|
});
|
||||||
|
'
|
||||||
|
],
|
||||||
|
'time.com' => [
|
||||||
|
'headers' => [
|
||||||
|
'User-Agent' => 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)',
|
||||||
|
'Cookie' => 'nyt-a=; nyt-gdpr=0; nyt-geo=DE; nyt-privacy=1',
|
||||||
|
'Referer' => 'https://www.google.com/'
|
||||||
|
],
|
||||||
|
'customCode' => '
|
||||||
|
window.localStorage.clear();
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const banners = document.querySelectorAll(\'div[data-testid="inline-message"], div[id^="ad-"], div[id^="leaderboard-"], div.expanded-dock, div.pz-ad-box, div[id="top-wrapper"], div[id="bottom-wrapper"]\');
|
||||||
|
banners.forEach(el => { el.remove(); });
|
||||||
|
});
|
||||||
|
'
|
||||||
|
],
|
||||||
|
'architecturaldigest.com' => [
|
||||||
|
'customCode' => '
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const banners = document.querySelectorAll(\'.paywall-bar, div[class^="MessageBannerWrapper-"\');
|
||||||
|
banners.forEach(el => { el.remove(); });
|
||||||
|
});
|
||||||
|
'
|
||||||
|
],
|
||||||
|
'bonappetit.com' => [
|
||||||
|
'customCode' => '
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const banners = document.querySelectorAll(\'.paywall-bar, div[class^="MessageBannerWrapper-"\');
|
||||||
|
banners.forEach(el => { el.remove(); });
|
||||||
|
});
|
||||||
|
'
|
||||||
|
],
|
||||||
|
'cntraveler.com' => [
|
||||||
|
'customCode' => '
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const banners = document.querySelectorAll(\'.paywall-bar, div[class^="MessageBannerWrapper-"\');
|
||||||
|
banners.forEach(el => { el.remove(); });
|
||||||
|
});
|
||||||
|
'
|
||||||
|
],
|
||||||
|
'epicurious.com' => [
|
||||||
|
'customCode' => '
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const banners = document.querySelectorAll(\'.paywall-bar, div[class^="MessageBannerWrapper-"\');
|
||||||
|
banners.forEach(el => { el.remove(); });
|
||||||
|
});
|
||||||
|
'
|
||||||
|
],
|
||||||
|
'gq.com' => [
|
||||||
|
'customCode' => '
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const banners = document.querySelectorAll(\'.paywall-bar, div[class^="MessageBannerWrapper-"\');
|
||||||
|
banners.forEach(el => { el.remove(); });
|
||||||
|
});
|
||||||
|
'
|
||||||
|
],
|
||||||
|
'vanityfair.com' => [
|
||||||
|
'customCode' => '
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const banners = document.querySelectorAll(\'.paywall-bar, div[class^="MessageBannerWrapper-"\');
|
||||||
|
banners.forEach(el => { el.remove(); });
|
||||||
|
});
|
||||||
|
'
|
||||||
|
],
|
||||||
|
'vogue.com' => [
|
||||||
|
'customCode' => '
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const banners = document.querySelectorAll(\'.paywall-bar, div[class^="MessageBannerWrapper-"\');
|
||||||
|
banners.forEach(el => { el.remove(); });
|
||||||
|
});
|
||||||
|
'
|
||||||
|
],
|
||||||
|
'americanbanker.com' => [
|
||||||
|
'customCode' => '
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const inlineGate = document.querySelector(\'.inline-gate\');
|
||||||
|
if (inlineGate) {
|
||||||
|
inlineGate.classList.remove(\'inline-gate\');
|
||||||
|
const inlineGated = document.querySelectorAll(\'.inline-gated\');
|
||||||
|
for (const elem of inlineGated) { elem.classList.remove(\'inline-gated\'); }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
'
|
||||||
|
],
|
||||||
|
'washingtonpost.com' => [
|
||||||
|
'classElementRemove' => ['paywall-overlay'],
|
||||||
|
'fetchStrategies' => 'fetchFromSelenium',
|
||||||
|
'customCode' => '
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
let paywall = document.querySelectorAll(\'div[data-qa$="-ad"], div[id="leaderboard-wrapper"], div[data-qa="subscribe-promo"]\');
|
||||||
|
paywall.forEach(el => { el.remove(); });
|
||||||
|
const images = document.querySelectorAll(\'img\');
|
||||||
|
images.forEach(image => { image.parentElement.style.filter = \'\'; });
|
||||||
|
const headimage = document.querySelectorAll(\'div .aspect-custom\');
|
||||||
|
headimage.forEach(image => { image.style.filter = \'\'; });
|
||||||
|
});
|
||||||
|
',
|
||||||
|
'idElementRemove' => ['wall-bottom-drawer-container']
|
||||||
|
],
|
||||||
|
'usatoday.com' => [
|
||||||
|
'customCode' => '
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const banners = document.querySelectorAll(\'div.roadblock-container, .gnt_nb, [aria-label="advertisement"], div[id="main-frame-error"]\');
|
||||||
|
banners.forEach(el => { el.remove(); });
|
||||||
|
});
|
||||||
|
'
|
||||||
|
],
|
||||||
|
'stcatharinesstandard.ca' => [
|
||||||
|
'proxy' => true,
|
||||||
|
'idElementRemove' => 'access-offers-modal',
|
||||||
|
'classElementRemove' => 'modal-backdrop',
|
||||||
|
'classAttrRemove' => ' modal-open'
|
||||||
|
],
|
||||||
|
'medium.com' => [
|
||||||
|
'headers' => [
|
||||||
|
'Referer' => 'https://t.co/x?amp=1',
|
||||||
|
'X-Forwarded-For' => 'none',
|
||||||
|
'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36',
|
||||||
|
'Content-Security-Policy' => 'script-src \'self\';'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'tagesspiegel.de' => [
|
||||||
|
'headers' => [
|
||||||
|
'Content-Security-Policy' => 'script-src \'self\';',
|
||||||
|
'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36'
|
||||||
|
],
|
||||||
|
'urlMods' => [
|
||||||
|
'query' => [
|
||||||
|
[
|
||||||
|
'key' => 'amp',
|
||||||
|
'value' => '1'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'nzz.ch' => [
|
||||||
|
'customCode' => '
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const paywall = document.querySelector(\'.dynamic-regwall\');
|
||||||
|
if (paywall) {
|
||||||
|
paywall.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
'
|
||||||
|
],
|
||||||
|
'demorgen.be' => [
|
||||||
|
'headers' => [
|
||||||
|
'Cookie' => 'isBot=true; authId=1',
|
||||||
|
'User-Agent' => 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Googlebot/2.1; Googlebot-News; +http://www.google.com/bot.html) Chrome/121.0.6140.0 Safari/537.36',
|
||||||
|
'X-Forwarded-For' => 'none',
|
||||||
|
'Referer' => 'https://news.google.com'
|
||||||
|
],
|
||||||
|
'customCode' => '
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
// remove paywall items
|
||||||
|
let paywall = document.querySelectorAll(\'script[src*="advertising-cdn.dpgmedia.cloud"], div[data-temptation-position="ARTICLE_BOTTOM"]\');
|
||||||
|
paywall.forEach(el => { el.remove(); });
|
||||||
|
// remove empty advert
|
||||||
|
const advert = document.querySelector(\'div[data-advert-placeholder-collapses]\');
|
||||||
|
if (advert) {
|
||||||
|
advert.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
'
|
||||||
|
],
|
||||||
|
'ft.com' => [
|
||||||
|
'cookies' => [
|
||||||
|
'next-flags' => null,
|
||||||
|
'next:ads' => null
|
||||||
|
],
|
||||||
|
'fromGoogleBot' => true,
|
||||||
|
'headers' => [
|
||||||
|
'Referer' => 'https://t.co/x?amp=1'
|
||||||
|
],
|
||||||
|
'customCode' => '
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const styleTags = document.querySelectorAll(\'link[rel="stylesheet"]\');
|
||||||
|
styleTags.forEach(el => {
|
||||||
|
const href = el.getAttribute(\'href\');
|
||||||
|
if (href && href.substring(0, 1) === \'/\') {
|
||||||
|
const updatedHref = href.substring(1).replace(/(https?:\\/\\/.+?)\\/{2,}/, \'$1/\');
|
||||||
|
el.setAttribute(\'href\', updatedHref);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setTimeout(() => {
|
||||||
|
const cookie = document.querySelectorAll(\'.o-cookie-message, .js-article-ribbon, .o-ads, .o-banner, .o-message, .article__content-sign-up\');
|
||||||
|
cookie.forEach(el => { el.remove(); });
|
||||||
|
}, 1000);
|
||||||
|
})
|
||||||
|
'
|
||||||
|
],
|
||||||
|
// Test domain
|
||||||
'altendorfme.github.io' => [
|
'altendorfme.github.io' => [
|
||||||
'userAgent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
'userAgent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
||||||
'headers' => [
|
'headers' => [
|
||||||
|
@ -345,6 +731,7 @@ return [
|
||||||
'Cache-Control' => 'no-cache',
|
'Cache-Control' => 'no-cache',
|
||||||
'Pragma' => 'no-cache'
|
'Pragma' => 'no-cache'
|
||||||
],
|
],
|
||||||
|
'proxy' => true,
|
||||||
'idElementRemove' => ['test-id-1', 'paywall'],
|
'idElementRemove' => ['test-id-1', 'paywall'],
|
||||||
'classElementRemove' => ['test-class-1'],
|
'classElementRemove' => ['test-class-1'],
|
||||||
'scriptTagRemove' => ['analytics.js', 'test-script.js', 'paywall.js'],
|
'scriptTagRemove' => ['analytics.js', 'test-script.js', 'paywall.js'],
|
||||||
|
@ -353,7 +740,7 @@ return [
|
||||||
'consent' => 'accepted',
|
'consent' => 'accepted',
|
||||||
'session_id' => null
|
'session_id' => null
|
||||||
],
|
],
|
||||||
'classAttrRemove' => ['test-attr-1','paywall'],
|
'classAttrRemove' => ['test-attr-1', 'paywall'],
|
||||||
'customCode' => '
|
'customCode' => '
|
||||||
console.log("worked");
|
console.log("worked");
|
||||||
',
|
',
|
||||||
|
|
|
@ -2,23 +2,13 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global rule configurations applied to all domains
|
* Global rule configurations applied to all domains
|
||||||
* Configurações globais de regras aplicadas a todos os domínios
|
|
||||||
*
|
|
||||||
* This file defines rules that are applied by default to all sites,
|
|
||||||
* organized into categories for better maintenance and understanding.
|
|
||||||
*
|
|
||||||
* Este arquivo define regras que são aplicadas por padrão a todos os sites,
|
|
||||||
* organizadas em categorias para melhor manutenção e compreensão.
|
|
||||||
*
|
*
|
||||||
* Note: These rules can be overridden or disabled for specific domains
|
* Note: These rules can be overridden or disabled for specific domains
|
||||||
* using the 'excludeGlobalRules' configuration in domain_rules.php
|
* using the 'excludeGlobalRules' configuration in domain_rules.php
|
||||||
*
|
|
||||||
* Nota: Estas regras podem ser sobrescritas ou desativadas para domínios específicos
|
|
||||||
* usando a configuração 'excludeGlobalRules' em domain_rules.php
|
|
||||||
*/
|
*/
|
||||||
return [
|
return [
|
||||||
// HTML classes to be removed from all pages
|
'proxy' => false,
|
||||||
// Classes HTML a serem removidas de todas as páginas
|
// Classes to be removed from all pages:
|
||||||
'classElementRemove' => [
|
'classElementRemove' => [
|
||||||
'subscription',
|
'subscription',
|
||||||
'subscriber-content',
|
'subscriber-content',
|
||||||
|
@ -40,8 +30,7 @@ return [
|
||||||
'signup-overlay',
|
'signup-overlay',
|
||||||
'onesignal-slidedown-container'
|
'onesignal-slidedown-container'
|
||||||
],
|
],
|
||||||
// Scripts to be removed from all pages
|
// Scripts to be removed from all pages:
|
||||||
// Scripts a serem removidos de todas as páginas
|
|
||||||
'scriptTagRemove' => [
|
'scriptTagRemove' => [
|
||||||
'gtm.js',
|
'gtm.js',
|
||||||
'ga.js',
|
'ga.js',
|
||||||
|
@ -80,6 +69,8 @@ return [
|
||||||
'getblue.io',
|
'getblue.io',
|
||||||
'smartocto.com',
|
'smartocto.com',
|
||||||
'cdn.pn.vg',
|
'cdn.pn.vg',
|
||||||
'static.vocstatic.com'
|
'static.vocstatic.com',
|
||||||
|
'recaptcha',
|
||||||
|
'intercom'
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
2
app/dist/css/style.css
vendored
Normal file
1
app/dist/css/style.css.map
vendored
Normal file
BIN
app/dist/fonts/inter-500.eot
vendored
Normal file
BIN
app/dist/fonts/inter-500.ttf
vendored
Normal file
BIN
app/dist/fonts/inter-500.woff
vendored
Normal file
BIN
app/dist/fonts/inter-500.woff2
vendored
Normal file
BIN
app/dist/fonts/inter-600.eot
vendored
Normal file
BIN
app/dist/fonts/inter-600.ttf
vendored
Normal file
BIN
app/dist/fonts/inter-600.woff
vendored
Normal file
BIN
app/dist/fonts/inter-600.woff2
vendored
Normal file
BIN
app/dist/fonts/unna-400.eot
vendored
Normal file
BIN
app/dist/fonts/unna-400.ttf
vendored
Normal file
BIN
app/dist/fonts/unna-400.woff
vendored
Normal file
BIN
app/dist/fonts/unna-400.woff2
vendored
Normal file
1
app/dist/icons/android.svg
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-android2"><path d="m10.213 1.471.691-1.26q.069-.124-.048-.192-.128-.057-.195.058l-.7 1.27A4.8 4.8 0 0 0 8.005.941q-1.032 0-1.956.404l-.7-1.27Q5.281-.037 5.154.02q-.117.069-.049.193l.691 1.259a4.25 4.25 0 0 0-1.673 1.476A3.7 3.7 0 0 0 3.5 5.02h9q0-1.125-.623-2.072a4.27 4.27 0 0 0-1.664-1.476ZM6.22 3.303a.37.37 0 0 1-.267.11.35.35 0 0 1-.263-.11.37.37 0 0 1-.107-.264.37.37 0 0 1 .107-.265.35.35 0 0 1 .263-.11q.155 0 .267.11a.36.36 0 0 1 .112.265.36.36 0 0 1-.112.264m4.101 0a.35.35 0 0 1-.262.11.37.37 0 0 1-.268-.11.36.36 0 0 1-.112-.264q0-.154.112-.265a.37.37 0 0 1 .268-.11q.155 0 .262.11a.37.37 0 0 1 .107.265q0 .153-.107.264M3.5 11.77q0 .441.311.75.311.306.76.307h.758l.01 2.182q0 .414.292.703a.96.96 0 0 0 .7.288.97.97 0 0 0 .71-.288.95.95 0 0 0 .292-.703v-2.182h1.343v2.182q0 .414.292.703a.97.97 0 0 0 .71.288.97.97 0 0 0 .71-.288.95.95 0 0 0 .292-.703v-2.182h.76q.436 0 .749-.308.31-.307.311-.75V5.365h-9zm10.495-6.587a.98.98 0 0 0-.702.278.9.9 0 0 0-.293.685v4.063q0 .406.293.69a.97.97 0 0 0 .702.284q.42 0 .712-.284a.92.92 0 0 0 .293-.69V6.146a.9.9 0 0 0-.293-.685 1 1 0 0 0-.712-.278m-12.702.283a1 1 0 0 1 .712-.283q.41 0 .702.283a.9.9 0 0 1 .293.68v4.063a.93.93 0 0 1-.288.69.97.97 0 0 1-.707.284 1 1 0 0 1-.712-.284.92.92 0 0 1-.293-.69V6.146q0-.396.293-.68"/></svg>
|
After Width: | Height: | Size: 1.3 KiB |
1
app/dist/icons/apple.svg
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-apple"><path d="M11.182.008C11.148-.03 9.923.023 8.857 1.18c-1.066 1.156-.902 2.482-.878 2.516s1.52.087 2.475-1.258.762-2.391.728-2.43m3.314 11.733c-.048-.096-2.325-1.234-2.113-3.422s1.675-2.789 1.698-2.854-.597-.79-1.254-1.157a3.7 3.7 0 0 0-1.563-.434c-.108-.003-.483-.095-1.254.116-.508.139-1.653.589-1.968.607-.316.018-1.256-.522-2.267-.665-.647-.125-1.333.131-1.824.328-.49.196-1.422.754-2.074 2.237-.652 1.482-.311 3.83-.067 4.56s.625 1.924 1.273 2.796c.576.984 1.34 1.667 1.659 1.899s1.219.386 1.843.067c.502-.308 1.408-.485 1.766-.472.357.013 1.061.154 1.782.539.571.197 1.111.115 1.652-.105.541-.221 1.324-1.059 2.238-2.758q.52-1.185.473-1.282"/><path d="M11.182.008C11.148-.03 9.923.023 8.857 1.18c-1.066 1.156-.902 2.482-.878 2.516s1.52.087 2.475-1.258.762-2.391.728-2.43m3.314 11.733c-.048-.096-2.325-1.234-2.113-3.422s1.675-2.789 1.698-2.854-.597-.79-1.254-1.157a3.7 3.7 0 0 0-1.563-.434c-.108-.003-.483-.095-1.254.116-.508.139-1.653.589-1.968.607-.316.018-1.256-.522-2.267-.665-.647-.125-1.333.131-1.824.328-.49.196-1.422.754-2.074 2.237-.652 1.482-.311 3.83-.067 4.56s.625 1.924 1.273 2.796c.576.984 1.34 1.667 1.659 1.899s1.219.386 1.843.067c.502-.308 1.408-.485 1.766-.472.357.013 1.061.154 1.782.539.571.197 1.111.115 1.652-.105.541-.221 1.324-1.059 2.238-2.758q.52-1.185.473-1.282"/></svg>
|
After Width: | Height: | Size: 1.4 KiB |
1
app/dist/icons/bookmark.svg
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M0 48C0 21.5 21.5 0 48 0v441.4l130.1-92.9c8.3-6 19.6-6 27.9 0l130 92.9V48H48V0h288c26.5 0 48 21.5 48 48v440c0 9-5 17.2-13 21.3s-17.6 3.4-24.9-1.8L192 397.5l-154.1 110c-7.3 5.2-16.9 5.9-24.9 1.8S0 497 0 488V48z"/></svg>
|
After Width: | Height: | Size: 289 B |
1
app/dist/icons/bsky.svg
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 568 501"><path fill="currentColor" d="M123.121 33.664C188.241 82.553 258.281 181.68 284 234.873c25.719-53.192 95.759-152.32 160.879-201.21C491.866-1.611 568-28.906 568 57.947c0 17.346-9.945 145.713-15.778 166.555-20.275 72.453-94.155 90.933-159.875 79.748 114.875 19.55 144.097 84.31 80.986 149.07-119.86 122.992-172.272-30.859-185.702-70.281-2.462-7.227-3.614-10.608-3.631-7.733-.017-2.875-1.169.506-3.631 7.733-13.43 39.422-65.842 193.273-185.702 70.281-63.111-64.76-33.89-129.52 80.986-149.071-65.72 11.185-139.6-7.295-159.875-79.748C9.945 203.659 0 75.291 0 57.946 0-28.906 76.135-1.612 123.121 33.664Z"/></svg>
|
After Width: | Height: | Size: 668 B |
1
app/dist/icons/chrome.svg
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-browser-chrome"><path fill-rule="evenodd" d="M16 8a8 8 0 0 1-7.022 7.94l1.902-7.098a3 3 0 0 0 .05-1.492A3 3 0 0 0 10.237 6h5.511A8 8 0 0 1 16 8M0 8a8 8 0 0 0 7.927 8l1.426-5.321a3 3 0 0 1-.723.255 3 3 0 0 1-1.743-.147 3 3 0 0 1-1.043-.7L.633 4.876A8 8 0 0 0 0 8m5.004-.167L1.108 3.936A8.003 8.003 0 0 1 15.418 5H8.066a3 3 0 0 0-1.252.243 2.99 2.99 0 0 0-1.81 2.59M8 10a2 2 0 1 0 0-4 2 2 0 0 0 0 4"/></svg>
|
After Width: | Height: | Size: 501 B |
1
app/dist/icons/close.svg
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-x-lg"><path d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"/></svg>
|
After Width: | Height: | Size: 284 B |
1
app/dist/icons/error.svg
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512a256 256 0 1 0 0-512 256 256 0 1 0 0 512zm-40-176h80c13.3 0 24-10.7 24-24s-10.7-24-24-24h-80c-13.3 0-24 10.7-24 24s10.7 24 24 24zm-24-72h128c13.3 0 24-10.7 24-24s-10.7-24-24-24H192c-13.3 0-24 10.7-24 24s10.7 24 24 24z"/></svg>
|
After Width: | Height: | Size: 305 B |
1
app/dist/icons/firefox.svg
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-browser-firefox"><path d="M13.384 3.408c.535.276 1.22 1.152 1.556 1.963a8 8 0 0 1 .503 3.897l-.009.077-.026.224A7.758 7.758 0 0 1 .006 8.257v-.04q.025-.545.114-1.082c.01-.074.075-.42.09-.489l.01-.051a6.6 6.6 0 0 1 1.041-2.35q.327-.465.725-.87.35-.358.758-.65a1.5 1.5 0 0 1 .26-.137c-.018.268-.04 1.553.268 1.943h.003a5.7 5.7 0 0 1 1.868-1.443 3.6 3.6 0 0 0 .021 1.896q.105.07.2.152c.107.09.226.207.454.433l.068.066.009.009a2 2 0 0 0 .213.18c.383.287.943.563 1.306.741.201.1.342.168.359.193l.004.008c-.012.193-.695.858-.933.858-2.206 0-2.564 1.335-2.564 1.335.087.997.714 1.839 1.517 2.357a4 4 0 0 0 .439.241q.114.05.228.094c.325.115.665.18 1.01.194 3.043.143 4.155-2.804 3.129-4.745v-.001a3 3 0 0 0-.731-.9 3 3 0 0 0-.571-.37l-.003-.002a2.68 2.68 0 0 1 1.87.454 3.92 3.92 0 0 0-3.396-1.983q-.116.001-.23.01l-.042.003V4.31h-.002a4 4 0 0 0-.8.14 7 7 0 0 0-.333-.314 2 2 0 0 0-.2-.152 4 4 0 0 1-.088-.383 5 5 0 0 1 1.352-.289l.05-.003c.052-.004.125-.01.205-.012C7.996 2.212 8.733.843 10.17.002l-.003.005.003-.001.002-.002h.002l.002-.002h.015a.02.02 0 0 1 .012.007 2.4 2.4 0 0 0 .206.48q.09.153.183.297c.49.774 1.023 1.379 1.543 1.968.771.874 1.512 1.715 2.036 3.02l-.001-.013a8 8 0 0 0-.786-2.353"/></svg>
|
After Width: | Height: | Size: 1.3 KiB |
1
app/dist/icons/hamburguer.svg
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-list"><path fill-rule="evenodd" d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5m0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5m0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5"/></svg>
|
After Width: | Height: | Size: 317 B |
1
app/dist/icons/link.svg
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path d="M579.8 267.7c56.5-56.5 56.5-148 0-204.5-50-50-128.8-56.5-186.3-15.4l-1.6 1.1c-14.4 10.3-17.7 30.3-7.4 44.6s30.3 17.7 44.6 7.4l1.6-1.1c32.1-22.9 76-19.3 103.8 8.6 31.5 31.5 31.5 82.5 0 114L422.3 334.8c-31.5 31.5-82.5 31.5-114 0-27.9-27.9-31.5-71.8-8.6-103.8l1.1-1.6c10.3-14.4 6.9-34.4-7.4-44.6s-34.4-6.9-44.6 7.4l-1.1 1.6C206.5 251.2 213 330 263 380c56.5 56.5 148 56.5 204.5 0l112.3-112.3zM60.2 244.3c-56.5 56.5-56.5 148 0 204.5 50 50 128.8 56.5 186.3 15.4l1.6-1.1c14.4-10.3 17.7-30.3 7.4-44.6s-30.3-17.7-44.6-7.4l-1.6 1.1c-32.1 22.9-76 19.3-103.8-8.6C74 372 74 321 105.5 289.5l112.2-112.3c31.5-31.5 82.5-31.5 114 0 27.9 27.9 31.5 71.8 8.6 103.9l-1.1 1.6c-10.3 14.4-6.9 34.4 7.4 44.6s34.4 6.9 44.6-7.4l1.1-1.6C433.5 260.8 427 182 377 132c-56.5-56.5-148-56.5-204.5 0L60.2 244.3z"/></svg>
|
After Width: | Height: | Size: 856 B |
1
app/dist/icons/marreta.svg
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path d="m283.9 378.6 18.3-60.1c18-4.1 34.2-16 43.1-33.8l64-128c10.5-21.1 8.4-45.2-3.7-63.6l52.7-76.6c3.7-5.4 10.4-8 16.7-6.5s11.2 6.7 12.2 13.1l16.2 104.1 105.1-7.4c6.5-.5 12.7 3.1 15.5 9s1.8 12.9-2.6 17.8L550.1 224l71.3 77.5c4.4 4.8 5.5 11.9 2.6 17.8s-9 9.5-15.5 9l-105.1-7.4L487.3 425c-1 6.5-5.9 11.7-12.2 13.1s-13-1.1-16.7-6.5l-59.7-86.7-91.4 52.2c-5.7 3.3-12.8 2.7-17.9-1.4s-7.2-10.9-5.3-17.2zm28.3-101.7c-9.3 10.9-25.2 14.4-38.6 7.7l-65.9-32.9-85.7-42.9-104.3-52.2c-15.8-7.9-22.2-27.1-14.3-42.9l40-80C48.8 22.8 59.9 16 72 16h120c5 0 9.9 1.2 14.3 3.4l78.2 39.1 81.8 40.9c15.8 7.9 22.2 27.1 14.3 42.9l-64 128c-1.2 2.4-2.7 4.6-4.4 6.6zm-204.6-39.5 85.9 42.9L90.9 485.5C79 509.2 50.2 518.8 26.5 507s-33.3-40.8-21.4-64.5l102.5-205.1z"/></svg>
|
After Width: | Height: | Size: 805 B |
3
app/dist/icons/moon.svg
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-moon-fill" viewBox="0 0 16 16">
|
||||||
|
<path d="M6 .278a.77.77 0 0 1 .08.858 7.2 7.2 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277q.792-.001 1.533-.16a.79.79 0 0 1 .81.316.73.73 0 0 1-.031.893A8.35 8.35 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.75.75 0 0 1 6 .278"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 394 B |
3
app/dist/icons/paste.svg
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M3.33333 2.66667H3.45333C3.72667 3.44 4.46 4 5.33333 4H6.66667C7.53333 4 8.26667 3.44 8.54667 2.66667H8.66667C9.58 2.66667 10.3733 3.28 10.6067 4.16667C10.7 4.52667 11.06 4.74 11.42 4.64667C11.7733 4.55333 11.9933 4.19333 11.9 3.83333C11.52 2.36 10.1933 1.33333 8.67333 1.33333H8.56C8.28667 0.56 7.54667 0 6.67333 0H5.34C4.47333 0 3.72667 0.56 3.45333 1.33333H3.34C1.49333 1.33333 0 2.82667 0 4.66667V12.6667C0 14.5067 1.49333 16 3.33333 16H5.33333C5.7 16 6 15.7 6 15.3333C6 14.9667 5.7 14.6667 5.33333 14.6667H3.33333C2.23333 14.6667 1.33333 13.7667 1.33333 12.6667V4.66667C1.33333 3.56667 2.23333 2.66667 3.33333 2.66667ZM5.33333 1.33333H6.66667C7.03333 1.33333 7.33333 1.63333 7.33333 2C7.33333 2.36667 7.03333 2.66667 6.66667 2.66667H5.33333C4.96667 2.66667 4.66667 2.36667 4.66667 2C4.66667 1.63333 4.96667 1.33333 5.33333 1.33333ZM13.3333 6H10C8.52667 6 7.33333 7.19333 7.33333 8.66667V13.3333C7.33333 14.8067 8.52667 16 10 16H13.3333C14.8067 16 16 14.8067 16 13.3333V8.66667C16 7.19333 14.8067 6 13.3333 6ZM14.6667 13.3333C14.6667 14.0667 14.0667 14.6667 13.3333 14.6667H10C9.26667 14.6667 8.66667 14.0667 8.66667 13.3333V8.66667C8.66667 7.93333 9.26667 7.33333 10 7.33333H13.3333C14.0667 7.33333 14.6667 7.93333 14.6667 8.66667V13.3333ZM13.3333 9.33333C13.3333 9.7 13.0333 10 12.6667 10H10.6667C10.3 10 10 9.7 10 9.33333C10 8.96667 10.3 8.66667 10.6667 8.66667H12.6667C13.0333 8.66667 13.3333 8.96667 13.3333 9.33333ZM13.3333 12C13.3333 12.3667 13.0333 12.6667 12.6667 12.6667H10.6667C10.3 12.6667 10 12.3667 10 12C10 11.6333 10.3 11.3333 10.6667 11.3333H12.6667C13.0333 11.3333 13.3333 11.6333 13.3333 12Z" fill="black"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
1
app/dist/icons/refresh.svg
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M105.1 202.6c7.7-21.8 20.2-42.3 37.8-59.8 62.5-62.5 163.8-62.5 226.3 0l17.1 17.2H352c-17.7 0-32 14.3-32 32s14.3 32 32 32h111.9c17.7 0 32-14.3 32-32V80c0-17.7-14.3-32-32-32s-32 14.3-32 32v35.2l-17.5-17.6c-87.5-87.5-229.3-87.5-316.8 0-24.4 24.4-42 53.1-52.8 83.8-5.9 16.7 2.9 34.9 19.5 40.8s34.9-2.9 40.8-19.5zM39 289.3c-5 1.5-9.8 4.2-13.7 8.2-4 4-6.7 8.8-8.1 14-.3 1.2-.6 2.5-.8 3.8-.3 1.7-.4 3.4-.4 5.1V432c0 17.7 14.3 32 32 32s32-14.3 32-32v-35.1l17.6 17.5c87.5 87.4 229.3 87.4 316.7 0 24.4-24.4 42.1-53.1 52.9-83.8 5.9-16.7-2.9-34.9-19.5-40.8s-34.9 2.9-40.8 19.5c-7.7 21.8-20.2 42.3-37.8 59.8-62.5 62.5-163.8 62.5-226.3 0l-.1-.1-17.1-17H160c17.7 0 32-14.3 32-32s-14.3-32-32-32H48.4c-1.6 0-3.2.1-4.8.3s-3.1.5-4.6 1z"/></svg>
|
After Width: | Height: | Size: 796 B |
3
app/dist/icons/sun.svg
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-brightness-high-fill" viewBox="0 0 16 16">
|
||||||
|
<path d="M12 8a4 4 0 1 1-8 0 4 4 0 0 1 8 0M8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0m0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13m8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5M3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8m10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0m-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0m9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707M4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 791 B |
1
app/dist/icons/telegram.svg
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-telegram"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M8.287 5.906q-1.168.486-4.666 2.01-.567.225-.595.442c-.03.243.275.339.69.47l.175.055c.408.133.958.288 1.243.294q.39.01.868-.32 3.269-2.206 3.374-2.23c.05-.012.12-.026.166.016s.042.12.037.141c-.03.129-1.227 1.241-1.846 1.817-.193.18-.33.307-.358.336a8 8 0 0 1-.188.186c-.38.366-.664.64.015 1.088.327.216.589.393.85.571.284.194.568.387.936.629q.14.092.27.187c.331.236.63.448.997.414.214-.02.435-.22.547-.82.265-1.417.786-4.486.906-5.751a1.4 1.4 0 0 0-.013-.315.34.34 0 0 0-.114-.217.53.53 0 0 0-.31-.093c-.3.005-.763.166-2.984 1.09"/></svg>
|
After Width: | Height: | Size: 687 B |
1
app/dist/icons/warning.svg
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7.2 40.1S486.3 480 472 480H40c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8.2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24v112c0 13.3 10.7 24 24 24s24-10.7 24-24V184c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0-64 0 32 32 0 1 0 64 0z"/></svg>
|
After Width: | Height: | Size: 380 B |
BIN
app/dist/images/opengraph.png
vendored
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
app/dist/images/opengraph.webp
vendored
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
app/dist/images/pwa/192x192.png
vendored
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
app/dist/images/pwa/192x192.webp
vendored
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
app/dist/images/pwa/512x512.png
vendored
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
app/dist/images/pwa/512x512.webp
vendored
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
app/dist/images/wall.png
vendored
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
app/dist/images/wall.webp
vendored
Normal file
After Width: | Height: | Size: 21 KiB |
2
app/dist/js/scripts.js
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
"serviceWorker"in navigator&&window.addEventListener("load",()=>{navigator.serviceWorker.register("/service-worker.js").then(()=>{}).catch(()=>{})}),document.addEventListener("DOMContentLoaded",function(){let t=document.querySelector(".integration");var e=document.querySelector(".integration__toggle");let o=document.querySelector(".extension");var n=document.querySelector(".extension__toggle");let a=e=>{e!==t&&t.classList.remove("open"),e!==o&&o.classList.remove("open")};e.addEventListener("click",e=>{e.stopPropagation(),a(t),t.classList.toggle("open")}),n.addEventListener("click",e=>{e.stopPropagation(),a(o),o.classList.toggle("open")}),t.addEventListener("click",e=>{e.stopPropagation()}),o.addEventListener("click",e=>{e.stopPropagation()}),document.addEventListener("click",()=>{t.classList.remove("open"),o.classList.remove("open")}),document.addEventListener("click",e=>{e=e.target.closest(".toasty");e&&e.remove()}),document.addEventListener("click",e=>{e.target.closest(".open-nav")&&((e=document.querySelector("header")).classList.contains("open")?e.classList.remove("open"):e.classList.add("open"))});e=document.getElementById("paste");let r=document.getElementById("url");e&&r&&e.addEventListener("click",async e=>{e.preventDefault();try{var t=await navigator.clipboard.readText();r.value=t.trim()}catch(e){console.error("Failed to read clipboard contents",e)}});n=document.getElementById("themeToggle");let c=document.documentElement;e=localStorage.getItem("theme")||"light";c.setAttribute("data-theme",e),n&&n.addEventListener("click",()=>{var e="dark"===c.getAttribute("data-theme")?"light":"dark";c.setAttribute("data-theme",e),localStorage.setItem("theme",e)})});
|
||||||
|
//# sourceMappingURL=scripts.js.map
|
1
app/dist/js/scripts.js.map
vendored
Normal file
|
@ -1,53 +1,34 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
namespace Inc;
|
||||||
|
|
||||||
use Inc\Cache\CacheStorageInterface;
|
use Inc\Cache\CacheStorageInterface;
|
||||||
use Inc\Cache\DiskStorage;
|
use Inc\Cache\DiskStorage;
|
||||||
use Inc\Cache\S3Storage;
|
use Inc\Cache\S3Storage;
|
||||||
use Inc\Cache\RedisStorage;
|
use Inc\Cache\SQLiteStorage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class responsible for system cache management
|
* System cache management with multiple storage backends (disk/S3)
|
||||||
* Classe responsável pelo gerenciamento de cache do sistema
|
* Uses SHA-256 hashed URLs as unique identifiers
|
||||||
*
|
* Implements gzip compression for space efficiency
|
||||||
* This class implements functionalities to store and retrieve
|
|
||||||
* cached content, supporting multiple storage backends (disk or S3).
|
|
||||||
* The cache is organized by URLs converted to unique IDs using SHA-256.
|
|
||||||
* Content is compressed using gzip to save space.
|
|
||||||
*
|
|
||||||
* Esta classe implementa funcionalidades para armazenar e recuperar
|
|
||||||
* conteúdo em cache, suportando múltiplos backends de armazenamento (disco ou S3).
|
|
||||||
* O cache é organizado por URLs convertidas em IDs únicos usando SHA-256.
|
|
||||||
* O conteúdo é comprimido usando gzip para economizar espaço.
|
|
||||||
*/
|
*/
|
||||||
class Cache
|
class Cache
|
||||||
{
|
{
|
||||||
/**
|
/** @var CacheStorageInterface Cache storage implementation */
|
||||||
* @var CacheStorageInterface Storage implementation for cache
|
|
||||||
* @var CacheStorageInterface Implementação de storage para o cache
|
|
||||||
*/
|
|
||||||
private $storage;
|
private $storage;
|
||||||
|
|
||||||
/**
|
/** @var SQLiteStorage SQLite instance for file counting */
|
||||||
* @var RedisStorage Redis instance for file counting
|
private $sqliteStorage;
|
||||||
* @var RedisStorage Instância do Redis para contagem de arquivos
|
|
||||||
*/
|
|
||||||
private $redisStorage;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class constructor
|
* Initializes storage based on configuration
|
||||||
* Construtor da classe
|
* Uses S3Storage if configured and enabled
|
||||||
*
|
* Defaults to SQLiteStorage otherwise (which delegates to DiskStorage)
|
||||||
* Initializes appropriate storage based on configuration
|
|
||||||
* Inicializa o storage apropriado baseado na configuração
|
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
// Initialize RedisStorage for file counting
|
$this->sqliteStorage = new SQLiteStorage(CACHE_DIR);
|
||||||
// Inicializa o RedisStorage para contagem de arquivos
|
|
||||||
$this->redisStorage = new RedisStorage(CACHE_DIR);
|
|
||||||
|
|
||||||
// If S3 is configured and active, use S3Storage
|
|
||||||
// Se S3 está configurado e ativo, usa S3Storage
|
|
||||||
if (defined('S3_CACHE_ENABLED') && S3_CACHE_ENABLED === true) {
|
if (defined('S3_CACHE_ENABLED') && S3_CACHE_ENABLED === true) {
|
||||||
$this->storage = new S3Storage([
|
$this->storage = new S3Storage([
|
||||||
'key' => S3_ACCESS_KEY,
|
'key' => S3_ACCESS_KEY,
|
||||||
|
@ -59,51 +40,30 @@ class Cache
|
||||||
'endpoint' => defined('S3_ENDPOINT') ? S3_ENDPOINT : null
|
'endpoint' => defined('S3_ENDPOINT') ? S3_ENDPOINT : null
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, use disk storage
|
$this->storage = $this->sqliteStorage;
|
||||||
// Caso contrário, usa o storage em disco
|
|
||||||
$this->storage = new DiskStorage(CACHE_DIR);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Gets total number of cached files */
|
||||||
* Gets the count of cached files
|
|
||||||
* Obtém a contagem de arquivos em cache
|
|
||||||
*
|
|
||||||
* @return int Number of files in cache / Número de arquivos em cache
|
|
||||||
*/
|
|
||||||
public function getCacheFileCount(): int
|
public function getCacheFileCount(): int
|
||||||
{
|
{
|
||||||
return $this->redisStorage->countCacheFiles();
|
return $this->sqliteStorage->countCacheFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a unique ID for a URL
|
* Generates unique cache ID from URL
|
||||||
* Gera um ID único para uma URL
|
* Normalizes URL by removing protocol and www
|
||||||
*
|
* Returns SHA-256 hash of normalized URL
|
||||||
* @param string $url URL for which the ID will be generated / URL para qual será gerado o ID
|
|
||||||
* @return string SHA-256 hash of the normalized URL / Hash SHA-256 da URL normalizada
|
|
||||||
*/
|
*/
|
||||||
public function generateId($url)
|
public function generateId($url)
|
||||||
{
|
{
|
||||||
// Remove protocol and www
|
|
||||||
// Remove protocolo e www
|
|
||||||
$url = preg_replace('#^https?://(www\.)?#', '', $url);
|
$url = preg_replace('#^https?://(www\.)?#', '', $url);
|
||||||
// Generate unique ID using SHA-256
|
|
||||||
// Gera ID único usando SHA-256
|
|
||||||
return hash('sha256', $url);
|
return hash('sha256', $url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Checks if cached version exists for URL */
|
||||||
* Checks if cache exists for a given URL
|
|
||||||
* Verifica se existe cache para uma determinada URL
|
|
||||||
*
|
|
||||||
* @param string $url URL to check / URL a ser verificada
|
|
||||||
* @return bool True if cache exists, False otherwise / True se existir cache, False caso contrário
|
|
||||||
*/
|
|
||||||
public function exists($url)
|
public function exists($url)
|
||||||
{
|
{
|
||||||
// If DISABLE_CACHE is active, always return false
|
|
||||||
// Se DISABLE_CACHE está ativo, sempre retorna false
|
|
||||||
if (DISABLE_CACHE) {
|
if (DISABLE_CACHE) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -111,17 +71,9 @@ class Cache
|
||||||
return $this->storage->exists($this->generateId($url));
|
return $this->storage->exists($this->generateId($url));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Retrieves cached content for URL */
|
||||||
* Retrieves cached content for a URL
|
|
||||||
* Recupera o conteúdo em cache de uma URL
|
|
||||||
*
|
|
||||||
* @param string $url URL of the content to retrieve / URL do conteúdo a ser recuperado
|
|
||||||
* @return string|null Cached content or null if it doesn't exist / Conteúdo em cache ou null se não existir
|
|
||||||
*/
|
|
||||||
public function get($url)
|
public function get($url)
|
||||||
{
|
{
|
||||||
// If DISABLE_CACHE is active, always return null
|
|
||||||
// Se DISABLE_CACHE está ativo, sempre retorna null
|
|
||||||
if (DISABLE_CACHE) {
|
if (DISABLE_CACHE) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -129,18 +81,9 @@ class Cache
|
||||||
return $this->storage->get($this->generateId($url));
|
return $this->storage->get($this->generateId($url));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Stores content in cache for URL */
|
||||||
* Stores content in cache for a URL
|
|
||||||
* Armazena conteúdo em cache para uma URL
|
|
||||||
*
|
|
||||||
* @param string $url URL associated with the content / URL associada ao conteúdo
|
|
||||||
* @param string $content Content to be cached / Conteúdo a ser armazenado em cache
|
|
||||||
* @return bool True if cache was saved successfully, False otherwise / True se o cache foi salvo com sucesso, False caso contrário
|
|
||||||
*/
|
|
||||||
public function set($url, $content)
|
public function set($url, $content)
|
||||||
{
|
{
|
||||||
// If DISABLE_CACHE is active, don't generate cache
|
|
||||||
// Se DISABLE_CACHE está ativo, não gera cache
|
|
||||||
if (DISABLE_CACHE) {
|
if (DISABLE_CACHE) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|