Encode config params in URL (#842)

Adds support for encoding (and optionally encrypting) user config values as
a single string that can be passed to any endpoint with the "preferences" url
param.

Co-authored-by: Ben Busby <contact@benbusby.com>
main
João 2022-09-22 22:14:56 +02:00 committed by GitHub
parent 11275a7796
commit 74503d542e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 249 additions and 39 deletions

View File

@ -14,7 +14,7 @@ RUN pip install --prefix /install --no-warn-script-location --no-cache-dir -r re
FROM python:3.11.0a5-alpine FROM python:3.11.0a5-alpine
RUN apk add --update --no-cache tor curl openrc RUN apk add --update --no-cache tor curl openrc libstdc++
# libcurl4-openssl-dev # libcurl4-openssl-dev
RUN apk -U upgrade RUN apk -U upgrade

View File

@ -389,23 +389,25 @@ There are a few optional environment variables available for customizing a Whoog
### Config Environment Variables ### Config Environment Variables
These environment variables allow setting default config values, but can be overwritten manually by using the home page config menu. These allow a shortcut for destroying/rebuilding an instance to the same config state every time. These environment variables allow setting default config values, but can be overwritten manually by using the home page config menu. These allow a shortcut for destroying/rebuilding an instance to the same config state every time.
| Variable | Description | | Variable | Description |
| ------------------------------ | --------------------------------------------------------------- | | ------------------------------------ | --------------------------------------------------------------- |
| WHOOGLE_CONFIG_DISABLE | Hide config from UI and disallow changes to config by client | | WHOOGLE_CONFIG_DISABLE | Hide config from UI and disallow changes to config by client |
| WHOOGLE_CONFIG_COUNTRY | Filter results by hosting country | | WHOOGLE_CONFIG_COUNTRY | Filter results by hosting country |
| WHOOGLE_CONFIG_LANGUAGE | Set interface language | | WHOOGLE_CONFIG_LANGUAGE | Set interface language |
| WHOOGLE_CONFIG_SEARCH_LANGUAGE | Set search result language | | WHOOGLE_CONFIG_SEARCH_LANGUAGE | Set search result language |
| WHOOGLE_CONFIG_BLOCK | Block websites from search results (use comma-separated list) | | WHOOGLE_CONFIG_BLOCK | Block websites from search results (use comma-separated list) |
| WHOOGLE_CONFIG_THEME | Set theme mode (light, dark, or system) | | WHOOGLE_CONFIG_THEME | Set theme mode (light, dark, or system) |
| WHOOGLE_CONFIG_SAFE | Enable safe searches | | WHOOGLE_CONFIG_SAFE | Enable safe searches |
| WHOOGLE_CONFIG_ALTS | Use social media site alternatives (nitter, invidious, etc) | | WHOOGLE_CONFIG_ALTS | Use social media site alternatives (nitter, invidious, etc) |
| WHOOGLE_CONFIG_NEAR | Restrict results to only those near a particular city | | WHOOGLE_CONFIG_NEAR | Restrict results to only those near a particular city |
| WHOOGLE_CONFIG_TOR | Use Tor routing (if available) | | WHOOGLE_CONFIG_TOR | Use Tor routing (if available) |
| WHOOGLE_CONFIG_NEW_TAB | Always open results in new tab | | WHOOGLE_CONFIG_NEW_TAB | Always open results in new tab |
| WHOOGLE_CONFIG_VIEW_IMAGE | Enable View Image option | | WHOOGLE_CONFIG_VIEW_IMAGE | Enable View Image option |
| WHOOGLE_CONFIG_GET_ONLY | Search using GET requests only | | WHOOGLE_CONFIG_GET_ONLY | Search using GET requests only |
| WHOOGLE_CONFIG_URL | The root url of the instance (`https://<your url>/`) | | WHOOGLE_CONFIG_URL | The root url of the instance (`https://<your url>/`) |
| WHOOGLE_CONFIG_STYLE | The custom CSS to use for styling (should be single line) | | WHOOGLE_CONFIG_STYLE | The custom CSS to use for styling (should be single line) |
| WHOOGLE_CONFIG_PREFERENCES_ENCRYPTED | Encrypt preferences token, requires preferences key |
| WHOOGLE_CONFIG_PREFERENCES_KEY | Key to encrypt preferences in URL (REQUIRED to show url) |
## Usage ## Usage
Same as most search engines, with the exception of filtering by time range. Same as most search engines, with the exception of filtering by time range.

View File

@ -174,6 +174,16 @@
"description": "[CONFIG] Custom CSS styling (paste in CSS or leave blank)", "description": "[CONFIG] Custom CSS styling (paste in CSS or leave blank)",
"value": ":root { /* LIGHT THEME COLORS */ --whoogle-background: #d8dee9; --whoogle-accent: #2e3440; --whoogle-text: #3B4252; --whoogle-contrast-text: #eceff4; --whoogle-secondary-text: #70757a; --whoogle-result-bg: #fff; --whoogle-result-title: #4c566a; --whoogle-result-url: #81a1c1; --whoogle-result-visited: #a3be8c; /* DARK THEME COLORS */ --whoogle-dark-background: #222; --whoogle-dark-accent: #685e79; --whoogle-dark-text: #fff; --whoogle-dark-contrast-text: #000; --whoogle-dark-secondary-text: #bbb; --whoogle-dark-result-bg: #000; --whoogle-dark-result-title: #1967d2; --whoogle-dark-result-url: #4b11a8; --whoogle-dark-result-visited: #bbbbff; }", "value": ":root { /* LIGHT THEME COLORS */ --whoogle-background: #d8dee9; --whoogle-accent: #2e3440; --whoogle-text: #3B4252; --whoogle-contrast-text: #eceff4; --whoogle-secondary-text: #70757a; --whoogle-result-bg: #fff; --whoogle-result-title: #4c566a; --whoogle-result-url: #81a1c1; --whoogle-result-visited: #a3be8c; /* DARK THEME COLORS */ --whoogle-dark-background: #222; --whoogle-dark-accent: #685e79; --whoogle-dark-text: #fff; --whoogle-dark-contrast-text: #000; --whoogle-dark-secondary-text: #bbb; --whoogle-dark-result-bg: #000; --whoogle-dark-result-title: #1967d2; --whoogle-dark-result-url: #4b11a8; --whoogle-dark-result-visited: #bbbbff; }",
"required": false "required": false
},
"WHOOGLE_CONFIG_PREFERENCES_ENCRYPTED": {
"description": "[CONFIG] Encrypt preferences token, requires WHOOGLE_CONFIG_PREFERENCES_KEY to be set",
"value": "",
"required": false
},
"WHOOGLE_CONFIG_PREFERENCES_KEY": {
"description": "[CONFIG] Key to encrypt preferences",
"value": "NEEDS_TO_BE_MODIFIED",
"required": false
} }
} }
} }

View File

@ -1,7 +1,13 @@
from inspect import Attribute
from app.utils.misc import read_config_bool from app.utils.misc import read_config_bool
from flask import current_app from flask import current_app
import os import os
import re import re
from base64 import urlsafe_b64encode, urlsafe_b64decode
import pickle
from cryptography.fernet import Fernet
import hashlib
import brotli
class Config: class Config:
@ -29,6 +35,9 @@ class Config:
self.view_image = read_config_bool('WHOOGLE_CONFIG_VIEW_IMAGE') self.view_image = read_config_bool('WHOOGLE_CONFIG_VIEW_IMAGE')
self.get_only = read_config_bool('WHOOGLE_CONFIG_GET_ONLY') self.get_only = read_config_bool('WHOOGLE_CONFIG_GET_ONLY')
self.anon_view = read_config_bool('WHOOGLE_CONFIG_ANON_VIEW') self.anon_view = read_config_bool('WHOOGLE_CONFIG_ANON_VIEW')
self.preferences_encrypted = read_config_bool('WHOOGLE_CONFIG_PREFERENCES_ENCRYPTED')
self.preferences_key = os.getenv('WHOOGLE_CONFIG_PREFERENCES_KEY', '')
self.accept_language = False self.accept_language = False
self.safe_keys = [ self.safe_keys = [
@ -42,7 +51,8 @@ class Config:
'block', 'block',
'safe', 'safe',
'nojs', 'nojs',
'anon_view' 'anon_view',
'preferences_encrypted'
] ]
# Skip setting custom config if there isn't one # Skip setting custom config if there isn't one
@ -71,6 +81,24 @@ class Config:
if not name.startswith("__") if not name.startswith("__")
and (type(attr) is bool or type(attr) is str)} and (type(attr) is bool or type(attr) is str)}
def get_attrs(self):
return {name: attr for name, attr in self.__dict__.items()
if not name.startswith("__")
and (type(attr) is bool or type(attr) is str)}
@property
def preferences(self) -> str:
# if encryption key is not set will uncheck preferences encryption
if self.preferences_encrypted:
self.preferences_encrypted = bool(self.preferences_key)
# add a tag for visibility if preferences token startswith 'e' it means
# the token is encrypted, 'u' means the token is unencrypted and can be
# used by other whoogle instances
encrypted_flag = "e" if self.preferences_encrypted else 'u'
preferences_digest = self._encode_preferences()
return f"{encrypted_flag}{preferences_digest}"
def is_safe_key(self, key) -> bool: def is_safe_key(self, key) -> bool:
"""Establishes a group of config options that are safe to set """Establishes a group of config options that are safe to set
in the url. in the url.
@ -109,6 +137,13 @@ class Config:
Returns: Returns:
Config -- a modified config object Config -- a modified config object
""" """
if 'preferences' in params:
params_new = self._decode_preferences(params['preferences'])
# if preferences leads to an empty dictionary it means preferences
# parameter was not decrypted successfully
if len(params_new):
params = params_new
for param_key in params.keys(): for param_key in params.keys():
if not self.is_safe_key(param_key): if not self.is_safe_key(param_key):
continue continue
@ -116,8 +151,9 @@ class Config:
if param_val == 'off': if param_val == 'off':
param_val = False param_val = False
elif param_val.isdigit(): elif isinstance(param_val, str):
param_val = int(param_val) if param_val.isdigit():
param_val = int(param_val)
self[param_key] = param_val self[param_key] = param_val
return self return self
@ -135,3 +171,40 @@ class Config:
param_str = param_str + f'&{safe_key}={self[safe_key]}' param_str = param_str + f'&{safe_key}={self[safe_key]}'
return param_str return param_str
def _get_fernet_key(self, password: str) -> bytes:
hash_object = hashlib.md5(password.encode())
key = urlsafe_b64encode(hash_object.hexdigest().encode())
return key
def _encode_preferences(self) -> str:
encoded_preferences = brotli.compress(pickle.dumps(self.get_attrs()))
if self.preferences_encrypted:
if self.preferences_key != '':
key = self._get_fernet_key(self.preferences_key)
encoded_preferences = Fernet(key).encrypt(encoded_preferences)
encoded_preferences = brotli.compress(encoded_preferences)
return urlsafe_b64encode(encoded_preferences).decode()
def _decode_preferences(self, preferences: str) -> dict:
mode = preferences[0]
preferences = preferences[1:]
if mode == 'e': # preferences are encrypted
try:
key = self._get_fernet_key(self.preferences_key)
config = Fernet(key).decrypt(
brotli.decompress(urlsafe_b64decode(preferences.encode()))
)
config = pickle.loads(brotli.decompress(config))
except Exception:
config = {}
elif mode == 'u': # preferences are not encrypted
config = pickle.loads(
brotli.decompress(urlsafe_b64decode(preferences.encode()))
)
else: # preferences are incorrectly formatted
config = {}
return config

View File

@ -193,6 +193,9 @@ def index():
session['error_message'] = '' session['error_message'] = ''
return render_template('error.html', error_message=error_message) return render_template('error.html', error_message=error_message)
# Update user config if specified in search args
g.user_config = g.user_config.from_params(g.request_params)
return render_template('index.html', return render_template('index.html',
has_update=app.config['HAS_UPDATE'], has_update=app.config['HAS_UPDATE'],
languages=app.config['LANGUAGES'], languages=app.config['LANGUAGES'],
@ -231,7 +234,8 @@ def opensearch():
main_url=opensearch_url, main_url=opensearch_url,
request_type='' if get_only else 'method="post"', request_type='' if get_only else 'method="post"',
search_type=request.args.get('tbm'), search_type=request.args.get('tbm'),
search_name=get_search_name(request.args.get('tbm')) search_name=get_search_name(request.args.get('tbm')),
preferences=g.user_config.preferences
), 200, {'Content-Type': 'application/xml'} ), 200, {'Content-Type': 'application/xml'}
@ -334,6 +338,7 @@ def search():
tabs = get_tabs_content(app.config['HEADER_TABS'], tabs = get_tabs_content(app.config['HEADER_TABS'],
search_util.full_query, search_util.full_query,
search_util.search_type, search_util.search_type,
g.user_config.preferences,
translation) translation)
# Feature to display currency_card # Feature to display currency_card
@ -342,6 +347,9 @@ def search():
html_soup = bsoup(str(response), 'html.parser') html_soup = bsoup(str(response), 'html.parser')
response = add_currency_card(html_soup, conversion) response = add_currency_card(html_soup, conversion)
preferences = g.user_config.preferences
home_url = f"home?preferences={preferences}" if preferences else "home"
return render_template( return render_template(
'display.html', 'display.html',
has_update=app.config['HAS_UPDATE'], has_update=app.config['HAS_UPDATE'],
@ -365,6 +373,7 @@ def search():
version_number=app.config['VERSION_NUMBER'], version_number=app.config['VERSION_NUMBER'],
search_header=render_template( search_header=render_template(
'header.html', 'header.html',
home_url=home_url,
config=g.user_config, config=g.user_config,
translation=translation, translation=translation,
languages=app.config['LANGUAGES'], languages=app.config['LANGUAGES'],

View File

@ -26,6 +26,9 @@
"config-tor": "Use Tor", "config-tor": "Use Tor",
"config-get-only": "GET Requests Only", "config-get-only": "GET Requests Only",
"config-url": "Root URL", "config-url": "Root URL",
"config-pref-url": "Preferences URL",
"config-pref-encryption": "Encrypt Preferences",
"config-pref-help": "Requires WHOOGLE_CONFIG_PREFERENCES_KEY, otherwise this will be ignored.",
"config-css": "Custom CSS", "config-css": "Custom CSS",
"load": "Load", "load": "Load",
"apply": "Apply", "apply": "Apply",
@ -72,6 +75,9 @@
"config-tor": "Gebruik Tor", "config-tor": "Gebruik Tor",
"config-get-only": "Alleen GET Requests", "config-get-only": "Alleen GET Requests",
"config-url": "Root URL", "config-url": "Root URL",
"config-pref-url": "Voorkeurs URL",
"config-pref-encryption": "Versleutel voorkeuren",
"config-pref-help": "Vereist WHOOGLE_CONFIG_PREFERENCES_KEY, anders wordt dit genegeerd.",
"config-css": "Eigen CSS", "config-css": "Eigen CSS",
"load": "Laden", "load": "Laden",
"apply": "Opslaan", "apply": "Opslaan",
@ -118,6 +124,9 @@
"config-tor": "Tor benutzen", "config-tor": "Tor benutzen",
"config-get-only": "Auschließlich GET-Anfragen", "config-get-only": "Auschließlich GET-Anfragen",
"config-url": "Root URL", "config-url": "Root URL",
"config-pref-url": "Einstellungs URL",
"config-pref-encryption": "Einstellungen verschlüsseln",
"config-pref-help": "Erfordert WHOOGLE_CONFIG_PREFERENCES_KEY, sonst wird dies ignoriert.",
"config-css": "Custom CSS", "config-css": "Custom CSS",
"load": "Laden", "load": "Laden",
"apply": "Übernehmen", "apply": "Übernehmen",
@ -164,6 +173,9 @@
"config-tor": "Usa Tor", "config-tor": "Usa Tor",
"config-get-only": "GET solo solicitudes", "config-get-only": "GET solo solicitudes",
"config-url": "URL raíz", "config-url": "URL raíz",
"config-pref-url": "URL de preferencias",
"config-pref-encryption": "Cifrar preferencias",
"config-pref-help": "Requiere WHOOGLE_CONFIG_PREFERENCES_KEY; de lo contrario, se ignorará.",
"config-css": "CSS personalizado", "config-css": "CSS personalizado",
"load": "Cargar", "load": "Cargar",
"apply": "Aplicar", "apply": "Aplicar",
@ -210,6 +222,9 @@
"config-tor": "Usa Tor", "config-tor": "Usa Tor",
"config-get-only": "Utilizza solo richieste GET", "config-get-only": "Utilizza solo richieste GET",
"config-url": "Root URL", "config-url": "Root URL",
"config-pref-url": "URL delle preferenze",
"config-pref-encryption": "Crittografa le preferenze",
"config-pref-help": "Richiede WHOOGLE_CONFIG_PREFERENCES_KEY, altrimenti verrà ignorato.",
"config-css": "CSS Personalizzato", "config-css": "CSS Personalizzato",
"load": "Carica", "load": "Carica",
"apply": "Applica", "apply": "Applica",
@ -256,6 +271,9 @@
"config-tor": "Usar Tor", "config-tor": "Usar Tor",
"config-get-only": "Apenas Pedidos GET", "config-get-only": "Apenas Pedidos GET",
"config-url": "URL Fonte", "config-url": "URL Fonte",
"config-pref-url": "URL de preferências",
"config-pref-encryption": "Criptografar preferências",
"config-pref-help": "Requer WHOOGLE_CONFIG_PREFERENCES_KEY, caso contrário, será ignorado.",
"config-css": "CSS Personalizado", "config-css": "CSS Personalizado",
"load": "Carregar", "load": "Carregar",
"apply": "Aplicar", "apply": "Aplicar",
@ -302,6 +320,9 @@
"config-tor": "Использовать Tor", "config-tor": "Использовать Tor",
"config-get-only": "Только GET-запросы", "config-get-only": "Только GET-запросы",
"config-url": "Корневой URL-адрес", "config-url": "Корневой URL-адрес",
"config-pref-url": "URL-адрес настроек",
"config-pref-encryption": "Зашифровать настройки",
"config-pref-help": "Требуется WHOOGLE_CONFIG_PREFERENCES_KEY, иначе это будет проигнорировано.",
"config-css": "Пользовательский CSS", "config-css": "Пользовательский CSS",
"load": "Загрузить", "load": "Загрузить",
"apply": "Применить", "apply": "Применить",
@ -348,6 +369,9 @@
"config-tor": "使用 Tor", "config-tor": "使用 Tor",
"config-get-only": "仅限 GET 请求", "config-get-only": "仅限 GET 请求",
"config-url": "站点根 URL", "config-url": "站点根 URL",
"config-pref-url": "首选项网址",
"config-pref-encryption": "加密首选项",
"config-pref-help": "需要 WHOOGLE_CONFIG_PREFERENCES_KEY否则将被忽略。",
"config-css": "自定义 CSS", "config-css": "自定义 CSS",
"load": "载入", "load": "载入",
"apply": "应用", "apply": "应用",
@ -394,6 +418,9 @@
"config-tor": "ටෝර් භාවිතා කරන්න", "config-tor": "ටෝර් භාවිතා කරන්න",
"config-get-only": "ඉල්ලීම් පමණක් ලබා ගන්න", "config-get-only": "ඉල්ලීම් පමණක් ලබා ගන්න",
"config-url": "ඒ.ස.නි.(URL) මූලය", "config-url": "ඒ.ස.නි.(URL) මූලය",
"config-pref-url": "මනාප URL",
"config-pref-encryption": "මනාප සංකේතනය කරන්න",
"config-pref-help": "WHOOGLE_CONFIG_PREFERENCES_KEY අවශ්‍ය වේ, එසේ නොමැතිනම් මෙය නොසලකා හරිනු ඇත.",
"config-css": "අභිරුචි සීඑස්එස්", "config-css": "අභිරුචි සීඑස්එස්",
"load": "පූරනය කරන්න", "load": "පූරනය කරන්න",
"apply": "යොදන්න", "apply": "යොදන්න",
@ -440,6 +467,9 @@
"config-tor": "Utiliser Tor", "config-tor": "Utiliser Tor",
"config-get-only": "Requêtes GET seulement", "config-get-only": "Requêtes GET seulement",
"config-url": "URL de la racine", "config-url": "URL de la racine",
"config-pref-url": "URL des préférences",
"config-pref-encryption": "Chiffrer les préférences",
"config-pref-help": "Nécessite WHOOGLE_CONFIG_PREFERENCES_KEY, sinon cela sera ignoré.",
"config-css": "CSS Personalisé", "config-css": "CSS Personalisé",
"load": "Charger", "load": "Charger",
"apply": "Appliquer", "apply": "Appliquer",
@ -486,6 +516,9 @@
"config-tor": "استفاده از تور", "config-tor": "استفاده از تور",
"config-get-only": "فقط درخواست‌های GET", "config-get-only": "فقط درخواست‌های GET",
"config-url": "آدرس ریشه‌ی سایت", "config-url": "آدرس ریشه‌ی سایت",
"config-pref-url": "URL تنظیمات برگزیده",
"config-pref-encryption": "رمزگذاری تنظیمات برگزیده",
"config-pref-help": "به WHOOGLE_CONFIG_PREFERENCES_KEY نیاز دارد، در غیر این صورت نادیده گرفته خواهد شد.",
"config-css": "CSS دلخواه", "config-css": "CSS دلخواه",
"load": "بارگذاری", "load": "بارگذاری",
"apply": "تایید", "apply": "تایید",
@ -532,6 +565,9 @@
"config-tor": "Používat Tor", "config-tor": "Používat Tor",
"config-get-only": "Pouze požadavky GET", "config-get-only": "Pouze požadavky GET",
"config-url": "Kořenová adresa URL", "config-url": "Kořenová adresa URL",
"config-pref-url": "Adresa URL předvoleb",
"config-pref-encryption": "Předvolby šifrování",
"config-pref-help": "Vyžaduje WHOOGLE_CONFIG_PREFERENCES_KEY, jinak bude ignorována.",
"config-css": "Vlastní CSS", "config-css": "Vlastní CSS",
"load": "Načíst", "load": "Načíst",
"apply": "Použít", "apply": "Použít",
@ -578,6 +614,9 @@
"config-tor": "使用 Tor", "config-tor": "使用 Tor",
"config-get-only": "僅限於 GET 要求", "config-get-only": "僅限於 GET 要求",
"config-url": "首頁網址", "config-url": "首頁網址",
"config-pref-url": "首選項網址",
"config-pref-encryption": "加密首選項",
"config-pref-help": "需要 WHOOGLE_CONFIG_PREFERENCES_KEY否則將被忽略。",
"config-css": "自定 CSS", "config-css": "自定 CSS",
"load": "載入", "load": "載入",
"apply": "套用", "apply": "套用",
@ -624,6 +663,9 @@
"config-tor": "Използвайте Tor", "config-tor": "Използвайте Tor",
"config-get-only": "Само GET заявки", "config-get-only": "Само GET заявки",
"config-url": "Основен URL адрес", "config-url": "Основен URL адрес",
"config-pref-url": "URL адрес на предпочитанията",
"config-pref-encryption": "Шифроване на предпочитанията",
"config-pref-help": "Изисква WHOOGLE_CONFIG_PREFERENCES_KEY, в противен случай това ще бъде игнорирано.",
"config-css": "Персонализиран CSS", "config-css": "Персонализиран CSS",
"load": "Зареди", "load": "Зареди",
"apply": "Приложи", "apply": "Приложи",
@ -670,6 +712,9 @@
"config-tor": "TOR का प्रयोग करें", "config-tor": "TOR का प्रयोग करें",
"config-get-only": "केवल GET अनुरोध", "config-get-only": "केवल GET अनुरोध",
"config-url": "रूट यूआरएल", "config-url": "रूट यूआरएल",
"config-pref-url": "वरीयताएँ URL",
"config-pref-encryption": "एन्क्रिप्ट प्राथमिकताएं",
"config-pref-help": "WHOOGLE_CONFIG_PREFERENCES_KEY की आवश्यकता है, अन्यथा इसे अनदेखा कर दिया जाएगा।",
"config-css": "कस्टम सीएसएस", "config-css": "कस्टम सीएसएस",
"load": "भार", "load": "भार",
"apply": "लागू करना", "apply": "लागू करना",
@ -716,6 +761,9 @@
"config-tor": "Torを使用", "config-tor": "Torを使用",
"config-get-only": "GETリクエストのみ", "config-get-only": "GETリクエストのみ",
"config-url": "ルートURL", "config-url": "ルートURL",
"config-pref-url": "設定 URL",
"config-pref-encryption": "設定を暗号化する",
"config-pref-help": "WHOOGLE_CONFIG_PREFERENCES_KEY が必要です。それ以外の場合、これは無視されます。",
"config-css": "カスタムCSS", "config-css": "カスタムCSS",
"load": "読み込み", "load": "読み込み",
"apply": "反映", "apply": "反映",
@ -762,6 +810,9 @@
"config-tor": "Tor 사용", "config-tor": "Tor 사용",
"config-get-only": "GET 요청만", "config-get-only": "GET 요청만",
"config-url": "루트 URL", "config-url": "루트 URL",
"config-pref-url": "환경설정 URL",
"config-pref-encryption": "암호화 환경 설정",
"config-pref-help": "WHOOGLE_CONFIG_PREFERENCES_KEY이 필요합니다. 그렇지 않으면 무시됩니다.",
"config-css": "커스텀 CSS", "config-css": "커스텀 CSS",
"load": "불러오기", "load": "불러오기",
"apply": "적용", "apply": "적용",
@ -808,6 +859,9 @@
"config-tor": "Tor bi kar bîne", "config-tor": "Tor bi kar bîne",
"config-get-only": "Daxwazan bi Dest Bixe", "config-get-only": "Daxwazan bi Dest Bixe",
"config-url": "Reha URL", "config-url": "Reha URL",
"config-pref-url": "Preferences URL",
"config-pref-encryption": "Vebijêrkên şîfre bikin",
"config-pref-help": "WHOOGLE_CONFIG_PREFERENCES_KEY hewce dike, wekî din ev ê were paşguh kirin.",
"config-css": "CSS kesane bike", "config-css": "CSS kesane bike",
"load": "Bar bike", "load": "Bar bike",
"apply": "Bisepîne", "apply": "Bisepîne",

View File

@ -4,13 +4,16 @@
<form class="search-form header" <form class="search-form header"
id="search-form" id="search-form"
method="{{ 'GET' if config.get_only else 'POST' }}"> method="{{ 'GET' if config.get_only else 'POST' }}">
<a class="logo-link mobile-logo" href="home"> <a class="logo-link mobile-logo" href="{{ home_url }}">
<div id="mobile-header-logo"> <div id="mobile-header-logo">
{{ logo|safe }} {{ logo|safe }}
</div> </div>
</a> </a>
<div class="H0PQec mobile-input-div"> <div class="H0PQec mobile-input-div">
<div class="autocomplete-mobile esbc autocomplete"> <div class="autocomplete-mobile esbc autocomplete">
{% if config.preferences %}
<input type="hidden" name="preferences" value="{{ config.preferences }}" />
{% endif %}
<input <input
id="search-bar" id="search-bar"
class="mobile-search-bar" class="mobile-search-bar"
@ -57,7 +60,7 @@
{% else %} {% else %}
<header> <header>
<div class="logo-div"> <div class="logo-div">
<a class="logo-link" href="home"> <a class="logo-link" href="{{ home_url }}">
<div class="desktop-header-logo"> <div class="desktop-header-logo">
{{ logo|safe }} {{ logo|safe }}
</div> </div>
@ -70,6 +73,9 @@
method="{{ 'GET' if config.get_only else 'POST' }}"> method="{{ 'GET' if config.get_only else 'POST' }}">
<div class="autocomplete header-autocomplete"> <div class="autocomplete header-autocomplete">
<div style="width: 100%; display: flex"> <div style="width: 100%; display: flex">
{% if config.preferences %}
<input type="hidden" name="preferences" value="{{ config.preferences }}" />
{% endif %}
<input <input
id="search-bar" id="search-bar"
autocapitalize="none" autocapitalize="none"

View File

@ -66,6 +66,9 @@
<form id="search-form" action="search" method="{{ 'get' if config.get_only else 'post' }}"> <form id="search-form" action="search" method="{{ 'get' if config.get_only else 'post' }}">
<div class="search-fields"> <div class="search-fields">
<div class="autocomplete"> <div class="autocomplete">
{% if config.preferences %}
<input type="hidden" name="preferences" value="{{ config.preferences }}" />
{% endif %}
<input <input
type="text" type="text"
name="q" name="q"
@ -233,6 +236,14 @@
{{ config.style.replace('\t', '') }} {{ config.style.replace('\t', '') }}
</textarea> </textarea>
</div> </div>
<div class="config-div config-div-pref-url">
<label for="config-pref-encryption">{{ translation['config-pref-encryption'] }}: </label>
<input type="checkbox" name="preferences_encrypted"
id="config-pref-encryption" {{ 'checked' if config.preferences_encrypted and config.preferences_key else '' }}>
<div><span class="info-text"> — {{ translation['config-pref-help'] }}</span></div>
<label for="config-pref-url">{{ translation['config-pref-url'] }}: </label>
<input type="text" name="pref-url" id="config-pref-url" value="{{ config.url }}?preferences={{ config.preferences }}">
</div>
</div> </div>
<div class="config-div config-buttons"> <div class="config-div config-buttons">
<input type="submit" id="config-load" value="{{ translation['load'] }}">&nbsp; <input type="submit" id="config-load" value="{{ translation['load'] }}">&nbsp;

View File

@ -17,6 +17,9 @@
{% if search_type %} {% if search_type %}
<Param name="tbm" value="{{ search_type }}"/> <Param name="tbm" value="{{ search_type }}"/>
{% endif %} {% endif %}
{% if preferences %}
<Param name="preferences" value="{{ preferences }}"/>
{% endif %}
</Url> </Url>
<Url type="application/x-suggestions+json" {{ request_type|safe }} template="{{ main_url }}/autocomplete"> <Url type="application/x-suggestions+json" {{ request_type|safe }} template="{{ main_url }}/autocomplete">
<Param name="q" value="{searchTerms}"/> <Param name="q" value="{searchTerms}"/>

View File

@ -393,6 +393,7 @@ def add_currency_card(soup: BeautifulSoup,
def get_tabs_content(tabs: dict, def get_tabs_content(tabs: dict,
full_query: str, full_query: str,
search_type: str, search_type: str,
preferences: str,
translation: dict) -> dict: translation: dict) -> dict:
"""Takes the default tabs content and updates it according to the query. """Takes the default tabs content and updates it according to the query.
@ -417,6 +418,9 @@ def get_tabs_content(tabs: dict,
if tab_content['tbm'] is not None: if tab_content['tbm'] is not None:
query = f"{query}&tbm={tab_content['tbm']}" query = f"{query}&tbm={tab_content['tbm']}"
if preferences:
query = f"{query}&preferences={preferences}"
tab_content['href'] = tab_content['href'].format(query=query) tab_content['href'] = tab_content['href'].format(query=query)
# update if selected tab (default all tab is selected) # update if selected tab (default all tab is selected)

View File

@ -47,21 +47,23 @@ conf: {}
# WHOOGLE_AUTOCOMPLETE: "" # Controls visibility of autocomplete/search suggestions. Default on -- use '0' to disable # WHOOGLE_AUTOCOMPLETE: "" # Controls visibility of autocomplete/search suggestions. Default on -- use '0' to disable
# WHOOGLE_MINIMAL: "" # Remove everything except basic result cards from all search queries. # WHOOGLE_MINIMAL: "" # Remove everything except basic result cards from all search queries.
# WHOOGLE_CONFIG_DISABLE: "" # Hide config from UI and disallow changes to config by client # WHOOGLE_CONFIG_DISABLE: "" # Hide config from UI and disallow changes to config by client
# WHOOGLE_CONFIG_COUNTRY: "" # Filter results by hosting country # WHOOGLE_CONFIG_COUNTRY: "" # Filter results by hosting country
# WHOOGLE_CONFIG_LANGUAGE: "" # Set interface language # WHOOGLE_CONFIG_LANGUAGE: "" # Set interface language
# WHOOGLE_CONFIG_SEARCH_LANGUAGE: "" # Set search result language # WHOOGLE_CONFIG_SEARCH_LANGUAGE: "" # Set search result language
# WHOOGLE_CONFIG_BLOCK: "" # Block websites from search results (use comma-separated list) # WHOOGLE_CONFIG_BLOCK: "" # Block websites from search results (use comma-separated list)
# WHOOGLE_CONFIG_THEME: "" # Set theme mode (light, dark, or system) # WHOOGLE_CONFIG_THEME: "" # Set theme mode (light, dark, or system)
# WHOOGLE_CONFIG_SAFE: "" # Enable safe searches # WHOOGLE_CONFIG_SAFE: "" # Enable safe searches
# WHOOGLE_CONFIG_ALTS: "" # Use social media site alternatives (nitter, invidious, etc) # WHOOGLE_CONFIG_ALTS: "" # Use social media site alternatives (nitter, invidious, etc)
# WHOOGLE_CONFIG_NEAR: "" # Restrict results to only those near a particular city # WHOOGLE_CONFIG_NEAR: "" # Restrict results to only those near a particular city
# WHOOGLE_CONFIG_TOR: "" # Use Tor routing (if available) # WHOOGLE_CONFIG_TOR: "" # Use Tor routing (if available)
# WHOOGLE_CONFIG_NEW_TAB: "" # Always open results in new tab # WHOOGLE_CONFIG_NEW_TAB: "" # Always open results in new tab
# WHOOGLE_CONFIG_VIEW_IMAGE: "" # Enable View Image option # WHOOGLE_CONFIG_VIEW_IMAGE: "" # Enable View Image option
# WHOOGLE_CONFIG_GET_ONLY: "" # Search using GET requests only # WHOOGLE_CONFIG_GET_ONLY: "" # Search using GET requests only
# WHOOGLE_CONFIG_URL: "" # The root url of the instance (https://<your url>/) # WHOOGLE_CONFIG_URL: "" # The root url of the instance (https://<your url>/)
# WHOOGLE_CONFIG_STYLE: "" # The custom CSS to use for styling (should be single line) # WHOOGLE_CONFIG_STYLE: "" # The custom CSS to use for styling (should be single line)
# WHOOGLE_CONFIG_PREFERENCES_ENCRYPTED: "" # Encrypt preferences token, requires key
# WHOOGLE_CONFIG_PREFERENCES_KEY: "" # Key to encrypt preferences in URL (REQUIRED to show url)
podAnnotations: {} podAnnotations: {}
podSecurityContext: {} podSecurityContext: {}

View File

@ -72,6 +72,8 @@ services:
# - WHOOGLE_CONFIG_SEARCH_LANGUAGE=lang_en # - WHOOGLE_CONFIG_SEARCH_LANGUAGE=lang_en
# - WHOOGLE_CONFIG_GET_ONLY=1 # - WHOOGLE_CONFIG_GET_ONLY=1
# - WHOOGLE_CONFIG_COUNTRY=FR # - WHOOGLE_CONFIG_COUNTRY=FR
# - WHOOGLE_CONFIG_PREFERENCES_ENCRYPTED=1
# - WHOOGLE_CONFIG_PREFERENCES_KEY="NEEDS_TO_BE_MODIFIED"
#env_file: # Alternatively, load variables from whoogle.env #env_file: # Alternatively, load variables from whoogle.env
#- whoogle.env #- whoogle.env
ports: ports:

View File

@ -1,5 +1,6 @@
attrs==19.3.0 attrs==19.3.0
beautifulsoup4==4.10.0 beautifulsoup4==4.10.0
brotli==1.0.9
cachelib==0.4.1 cachelib==0.4.1
certifi==2020.4.5.1 certifi==2020.4.5.1
cffi==1.15.0 cffi==1.15.0

View File

@ -5,6 +5,20 @@ from app.models.endpoint import Endpoint
from app.utils.session import generate_user_key, valid_user_session from app.utils.session import generate_user_key, valid_user_session
JAPAN_PREFS = 'uG-gGIJwHdqxl6DrS3mnu_511HlQcRpxYlG03Xs-' \
+ '_znXNiJWI9nLOkRLkiiFwIpeUYMTGfUF5-t9fP5DGmzDLEt04DCx703j3nPf' \
+ '29v_RWkU7gXw_44m2oAFIaKGmYlu4Z0bKyu9k5WXfL9Dy6YKKnpcR5CiaFsG' \
+ 'rccNRkAPYm-eYGAFUV8M59f8StsGd_M-gHKGS9fLok7EhwBWjHxBJ2Kv8hsT' \
+ '87zftP2gMJOevTdNnezw2Y5WOx-ZotgeheCW1BYCFcRqatlov21PHp22NGVG' \
+ '8ZuBNAFW0bE99WSdyT7dUIvzeWCLJpbdSsq-3FUUZkxbRdFYlGd8vY1UgVAp' \
+ 'OSie2uAmpgLFXygO-VfNBBZ68Q7gAap2QtzHCiKD5cFYwH3LPgVJ-DoZvJ6k' \
+ 'alt34TaYiJphgiqFKV4SCeVmLWTkr0SF3xakSR78yYJU_d41D2ng-TojA9XZ' \
+ 'uR2ZqjSvPKOWvjimu89YhFOgJxG1Po8Henj5h9OL9VXXvdvlJwBSAKw1E3FV' \
+ '7UHWiglMxPblfxqou1cYckMYkFeIMCD2SBtju68mBiQh2k328XRPTsQ_ocby' \
+ 'cgVKnleGperqbD6crRk3Z9xE5sVCjujn9JNVI-7mqOITMZ0kntq9uJ3R5n25' \
+ 'Vec0TJ0P19nEtvjY0nJIrIjtnBg=='
def test_generate_user_keys(): def test_generate_user_keys():
key = generate_user_key() key = generate_user_key()
assert Fernet(key) assert Fernet(key)
@ -49,3 +63,16 @@ def test_query_decryption(client):
with client.session_transaction() as session: with client.session_transaction() as session:
assert valid_user_session(session) assert valid_user_session(session)
def test_prefs_url(client):
base_url = f'/{Endpoint.search}?q=wikipedia'
rv = client.get(base_url)
assert rv._status_code == 200
assert b'wikipedia.org' in rv.data
assert b'ja.wikipedia.org' not in rv.data
rv = client.get(f'{base_url}&preferences={JAPAN_PREFS}')
assert rv._status_code == 200
assert b'ja.wikipedia.org' in rv.data

View File

@ -85,3 +85,9 @@
# Set custom CSS styling/theming # Set custom CSS styling/theming
#WHOOGLE_CONFIG_STYLE=":root { /* LIGHT THEME COLORS */ --whoogle-background: #d8dee9; --whoogle-accent: #2e3440; --whoogle-text: #3B4252; --whoogle-contrast-text: #eceff4; --whoogle-secondary-text: #70757a; --whoogle-result-bg: #fff; --whoogle-result-title: #4c566a; --whoogle-result-url: #81a1c1; --whoogle-result-visited: #a3be8c; /* DARK THEME COLORS */ --whoogle-dark-background: #222; --whoogle-dark-accent: #685e79; --whoogle-dark-text: #fff; --whoogle-dark-contrast-text: #000; --whoogle-dark-secondary-text: #bbb; --whoogle-dark-result-bg: #000; --whoogle-dark-result-title: #1967d2; --whoogle-dark-result-url: #4b11a8; --whoogle-dark-result-visited: #bbbbff; }" #WHOOGLE_CONFIG_STYLE=":root { /* LIGHT THEME COLORS */ --whoogle-background: #d8dee9; --whoogle-accent: #2e3440; --whoogle-text: #3B4252; --whoogle-contrast-text: #eceff4; --whoogle-secondary-text: #70757a; --whoogle-result-bg: #fff; --whoogle-result-title: #4c566a; --whoogle-result-url: #81a1c1; --whoogle-result-visited: #a3be8c; /* DARK THEME COLORS */ --whoogle-dark-background: #222; --whoogle-dark-accent: #685e79; --whoogle-dark-text: #fff; --whoogle-dark-contrast-text: #000; --whoogle-dark-secondary-text: #bbb; --whoogle-dark-result-bg: #000; --whoogle-dark-result-title: #1967d2; --whoogle-dark-result-url: #4b11a8; --whoogle-dark-result-visited: #bbbbff; }"
# Enable preferences encryption (requires key)
#WHOOGLE_CONFIG_PREFERENCES_ENCRYPTED=1
# Set Key to encode config in url
#WHOOGLE_CONFIG_PREFERENCES_KEY="NEEDS_TO_BE_MODIFIED"