Merge remote-tracking branch 'origin/develop' into feature/docker-updates

This commit is contained in:
Ben Busby 2020-10-10 18:46:11 -04:00
commit 4ef45dba51
16 changed files with 138 additions and 55 deletions

View File

@ -10,8 +10,5 @@ assignees: ''
**Describe the feature you'd like to see added** **Describe the feature you'd like to see added**
A short description of the feature, and what it would accomplish. A short description of the feature, and what it would accomplish.
**Describe which parts of the project this would modify (front end/back end/configuration/etc)**
A short description of which aspects of Whoogle Search would need modification
**Additional context** **Additional context**
Add any other context or screenshots about the feature request here. Add any other context or screenshots about the feature request here.

2
.replit Normal file
View File

@ -0,0 +1,2 @@
language = "python3"
run = "pip install -r requirements.txt && ./run"

View File

@ -22,7 +22,7 @@ Contents
- No ads or sponsored content - No ads or sponsored content
- No javascript - No javascript
- No cookies - No cookies
- No tracking/linking of your personal IP address - No tracking/linking of your personal IP address\*
- No AMP links - No AMP links
- No URL tracking tags (i.e. utm=%s) - No URL tracking tags (i.e. utm=%s)
- No referrer header - No referrer header
@ -35,6 +35,8 @@ Contents
- Optional location-based searching (i.e. results near \<city\>) - Optional location-based searching (i.e. results near \<city\>)
- Optional NoJS mode to disable all Javascript in results - Optional NoJS mode to disable all Javascript in results
<sup>*If deployed to a remote server</sup>
## Dependencies ## Dependencies
If using Heroku Quick Deploy, **you can skip this section**. If using Heroku Quick Deploy, **you can skip this section**.
@ -56,19 +58,28 @@ There are a few different ways to begin using the app, depending on your prefere
Provides: Provides:
- Free deployment of app - Free deployment of app
- Free https url (https://\<your app name\>.herokuapp.com) - Free HTTPS url (https://\<your app name\>.herokuapp.com)
- Downtime after periods of inactivity \([solution](https://github.com/benbusby/whoogle-search#prevent-downtime-heroku-only)\) - Downtime after periods of inactivity \([solution](https://github.com/benbusby/whoogle-search#prevent-downtime-heroku-only)\)
### B) [pipx](https://github.com/pipxproject/pipx#install-pipx) ### B) [Repl.it](https://repl.it)
[![Run on Repl.it](https://repl.it/badge/github/benbusby/whoogle-search)](https://repl.it/github/benbusby/whoogle-search)
Provides:
- Free deployment of app (can be ran without account)
- Free HTTPS url (https://\<app name\>.\<username\>\.repl\.co)
- Supports custom domains
- Downtime after periods of inactivity \([solution 1](https://repl.it/talk/ask/use-this-pingmat1replco-just-enter/28821/101298), [solution 2](https://repl.it/talk/learn/How-to-use-and-setup-UptimeRobot/9003)\)
### C) [pipx](https://github.com/pipxproject/pipx#install-pipx)
Persistent install: Persistent install:
`pipx install git+https://github.com/benbusby/whoogle-search.git` `pipx install git+https://github.com/benbusby/whoogle-search.git`
Sandboxed temporary instance: Sandboxed temporary instance:
`pipx run git+https://github.com/benbusby/whoogle-search.git whoogle-search` `pipx run --spec git+https://github.com/benbusby/whoogle-search.git whoogle-search`
### C) pip ### D) pip
`pip install whoogle-search` `pip install whoogle-search`
```bash ```bash
@ -86,7 +97,7 @@ optional arguments:
--https-only Enforces HTTPS redirects for all requests (default False) --https-only Enforces HTTPS redirects for all requests (default False)
``` ```
### D) Manual ### E) Manual
Clone the repo and run the following commands to start the app in a local-only environment: Clone the repo and run the following commands to start the app in a local-only environment:
```bash ```bash
@ -125,7 +136,7 @@ sudo systemctl enable whoogle
sudo systemctl start whoogle sudo systemctl start whoogle
``` ```
### E) Manual (Docker) ### F) Manual (Docker)
1. Ensure the Docker daemon is running, and is accessible by your user account 1. Ensure the Docker daemon is running, and is accessible by your user account
- To add user permissions, you can execute `sudo usermod -aG docker yourusername` - To add user permissions, you can execute `sudo usermod -aG docker yourusername`
- Running `docker ps` should return something besides an error. If you encounter an error saying the daemon isn't running, try `sudo systemctl start docker` (Linux) or ensure the docker tool is running (Windows/macOS). - Running `docker ps` should return something besides an error. If you encounter an error saying the daemon isn't running, try `sudo systemctl start docker` (Linux) or ensure the docker tool is running (Windows/macOS).
@ -225,15 +236,23 @@ Update browser settings:
- Firefox (iOS) - Firefox (iOS)
- In the mobile app Settings page, tap "Search" within the "General" section. There should be an option titled "Add Search Engine" to select. It should prompt you to enter a title and search query url - use the following elements to fill out the form: - In the mobile app Settings page, tap "Search" within the "General" section. There should be an option titled "Add Search Engine" to select. It should prompt you to enter a title and search query url - use the following elements to fill out the form:
- Title: "Whoogle" - Title: "Whoogle"
- URL: "https://\<your whoogle url\>/search?q=%s" - URL: `http[s]://\<your whoogle url\>/search?q=%s`
- Firefox (Android) - Firefox (Android)
- Navigate to your app's url - Version <79.0.0
- Long-press on the search text field - Navigate to your app's url
- Click the "Add Search Engine" menu item - Long-press on the search text field
- Select a name and click ok - Click the "Add Search Engine" menu item
- Click the 3 dot menu in the top right - Select a name and click ok
- Navigate to the settings menu and select the "search" sub-menu - Click the 3 dot menu in the top right
- Select Whoogle and press "Set as default" - Navigate to the settings menu and select the "Search" sub-menu
- Select Whoogle and press "Set as default"
- Version >=79.0.0
- Click the 3 dot menu in the top right
- Navigate to the settings menu and select the "Search" sub-menu
- Click "Add search engine"
- Select the 'Other' radio button
- Name: "Whoogle"
- Search string to use: `https://\<your whoogle url\>/search?q=%s`
- [Alfred](https://www.alfredapp.com/) (Mac OS X) - [Alfred](https://www.alfredapp.com/) (Mac OS X)
1. Go to `Alfred Preferences` > `Features` > `Web Search` and click `Add Custom Search`. Then configure these settings 1. Go to `Alfred Preferences` > `Features` > `Web Search` and click `Add Custom Search`. Then configure these settings
- Search URL: `https://\<your whoogle url\>/search?q={query} - Search URL: `https://\<your whoogle url\>/search?q={query}

View File

@ -9,7 +9,7 @@ app.default_key_set = generate_user_keys()
app.no_cookie_ips = [] app.no_cookie_ips = []
app.config['SECRET_KEY'] = os.urandom(32) app.config['SECRET_KEY'] = os.urandom(32)
app.config['SESSION_TYPE'] = 'filesystem' app.config['SESSION_TYPE'] = 'filesystem'
app.config['VERSION_NUMBER'] = '0.2.0' app.config['VERSION_NUMBER'] = '0.2.1'
app.config['APP_ROOT'] = os.getenv('APP_ROOT', os.path.dirname(os.path.abspath(__file__))) app.config['APP_ROOT'] = os.getenv('APP_ROOT', os.path.dirname(os.path.abspath(__file__)))
app.config['STATIC_FOLDER'] = os.getenv('STATIC_FOLDER', os.path.join(app.config['APP_ROOT'], 'static')) app.config['STATIC_FOLDER'] = os.getenv('STATIC_FOLDER', os.path.join(app.config['APP_ROOT'], 'static'))
app.config['CONFIG_PATH'] = os.getenv('CONFIG_VOLUME', os.path.join(app.config['STATIC_FOLDER'], 'config')) app.config['CONFIG_PATH'] = os.getenv('CONFIG_VOLUME', os.path.join(app.config['STATIC_FOLDER'], 'config'))

View File

@ -144,18 +144,6 @@ class Filter:
except AttributeError: except AttributeError:
pass pass
# Set up dark mode if active
if self.dark:
soup.find('html')['style'] = 'scrollbar-color: #333 #111;color:#fff !important;background:#000 !important'
for input_element in soup.findAll('input'):
input_element['style'] = 'color:#fff;background:#000;'
for span_element in soup.findAll('span'):
span_element['style'] = 'color: white;'
for href_element in soup.findAll('a'):
href_element['style'] = 'color: white' if href_element['href'].startswith('/search') else ''
def update_link(self, link): def update_link(self, link):
# Replace href with only the intended destination (no "utm" type tags) # Replace href with only the intended destination (no "utm" type tags)
href = link['href'].replace('https://www.google.com', '') href = link['href'].replace('https://www.google.com', '')

View File

@ -2,7 +2,7 @@ class Config:
# Derived from here: # Derived from here:
# https://sites.google.com/site/tomihasa/google-language-codes#searchlanguage # https://sites.google.com/site/tomihasa/google-language-codes#searchlanguage
LANGUAGES = [ LANGUAGES = [
{'name': 'Default (use server location)', 'value': ''}, {'name': 'Default (none specified)', 'value': ''},
{'name': 'English', 'value': 'lang_en'}, {'name': 'English', 'value': 'lang_en'},
{'name': 'Afrikaans', 'value': 'lang_af'}, {'name': 'Afrikaans', 'value': 'lang_af'},
{'name': 'Arabic', 'value': 'lang_ar'}, {'name': 'Arabic', 'value': 'lang_ar'},
@ -52,7 +52,7 @@ class Config:
] ]
COUNTRIES = [ COUNTRIES = [
{'name': 'Default (use server location)', 'value': ''}, {'name': 'Default (none)', 'value': ''},
{'name': 'Afghanistan', 'value': 'countryAF'}, {'name': 'Afghanistan', 'value': 'countryAF'},
{'name': 'Albania', 'value': 'countryAL'}, {'name': 'Albania', 'value': 'countryAL'},
{'name': 'Algeria', 'value': 'countryDZ'}, {'name': 'Algeria', 'value': 'countryDZ'},

View File

@ -115,12 +115,11 @@ def opensearch():
if opensearch_url.endswith('/'): if opensearch_url.endswith('/'):
opensearch_url = opensearch_url[:-1] opensearch_url = opensearch_url[:-1]
template = render_template('opensearch.xml', return render_template(
main_url=opensearch_url, 'opensearch.xml',
request_type='get' if g.user_config.get_only else 'post') main_url=opensearch_url,
response = make_response(template) request_type='' if g.user_config.get_only else 'method="post"'
response.headers['Content-Type'] = 'application/xml' ), 200, {'Content-Disposition': 'attachment; filename="opensearch.xml"'}
return response
@app.route('/autocomplete', methods=['GET', 'POST']) @app.route('/autocomplete', methods=['GET', 'POST'])

View File

@ -0,0 +1,42 @@
html {
background-color: #000 !important;
}
body {
background-color: #222 !important;
}
div {
/*background-color: #111 !important;*/
color: #fff !important;
}
a:visited h3 div {
color: #bbbbff !important;
}
a:link h3 div {
color: #4b8eea !important;
}
a:link div {
color: #aaffaa !important;
}
div span {
color: #bbb !important;
}
input {
background-color: #111 !important;
color: #fff !important;
}
#search-bar {
color: #fff !important;
background-color: #000 !important;
}
.search-container {
background-color: #000 !important;
}

View File

@ -16,6 +16,7 @@ body {
left: 50%; left: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
max-width: 600px; max-width: 600px;
z-index: 15;
} }
.search-items { .search-items {
@ -45,7 +46,7 @@ body {
width: 100%; width: 100%;
height: 40px; height: 40px;
border: 1px solid #685e79; border: 1px solid #685e79;
background: #685e79; background: #685e79 !important;
text-align: center; text-align: center;
color: #fff; color: #fff;
cursor: pointer; cursor: pointer;
@ -127,10 +128,10 @@ footer {
bottom: 0%; bottom: 0%;
text-align: center; text-align: center;
width: 100%; width: 100%;
z-index: -1; z-index: 10;
} }
.info-text { .info-text {
font-style: italic; font-style: italic;
font-size: 12px; font-size: 12px;
} }

View File

@ -2,7 +2,7 @@ const handleUserInput = searchBar => {
let xhrRequest = new XMLHttpRequest(); let xhrRequest = new XMLHttpRequest();
xhrRequest.open("POST", "/autocomplete"); xhrRequest.open("POST", "/autocomplete");
xhrRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xhrRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhrRequest.onload = function() { xhrRequest.onload = function () {
if (xhrRequest.readyState === 4 && xhrRequest.status !== 200) { if (xhrRequest.readyState === 4 && xhrRequest.status !== 200) {
// Do nothing if failed to fetch autocomplete results // Do nothing if failed to fetch autocomplete results
return; return;
@ -18,6 +18,7 @@ const handleUserInput = searchBar => {
const autocomplete = (searchInput, autocompleteResults) => { const autocomplete = (searchInput, autocompleteResults) => {
let currentFocus; let currentFocus;
let originalSearch;
searchInput.addEventListener("input", function () { searchInput.addEventListener("input", function () {
let autocompleteList, autocompleteItem, i, val = this.value; let autocompleteList, autocompleteItem, i, val = this.value;
@ -53,9 +54,11 @@ const autocomplete = (searchInput, autocompleteResults) => {
let suggestion = document.getElementById(this.id + "-autocomplete-list"); let suggestion = document.getElementById(this.id + "-autocomplete-list");
if (suggestion) suggestion = suggestion.getElementsByTagName("div"); if (suggestion) suggestion = suggestion.getElementsByTagName("div");
if (e.keyCode === 40) { // down if (e.keyCode === 40) { // down
e.preventDefault();
currentFocus++; currentFocus++;
addActive(suggestion); addActive(suggestion);
} else if (e.keyCode === 38) { //up } else if (e.keyCode === 38) { //up
e.preventDefault();
currentFocus--; currentFocus--;
addActive(suggestion); addActive(suggestion);
} else if (e.keyCode === 13) { // enter } else if (e.keyCode === 13) { // enter
@ -63,17 +66,36 @@ const autocomplete = (searchInput, autocompleteResults) => {
if (currentFocus > -1) { if (currentFocus > -1) {
if (suggestion) suggestion[currentFocus].click(); if (suggestion) suggestion[currentFocus].click();
} }
} else {
originalSearch = document.getElementById("search-bar").value;
} }
}); });
const addActive = suggestion => { const addActive = suggestion => {
if (!suggestion || !suggestion[currentFocus]) return false; let searchBar = document.getElementById("search-bar");
// Handle navigation outside of suggestion list
if (!suggestion || !suggestion[currentFocus]) {
if (currentFocus >= suggestion.length) {
// Move selection back to the beginning
currentFocus = 0;
} else if (currentFocus < 0) {
// Retrieve original search and remove active suggestion selection
currentFocus = -1;
searchBar.value = originalSearch;
removeActive(suggestion);
return;
} else {
return;
}
}
removeActive(suggestion); removeActive(suggestion);
if (currentFocus >= suggestion.length) currentFocus = 0;
if (currentFocus < 0) currentFocus = (suggestion.length - 1);
suggestion[currentFocus].classList.add("autocomplete-active"); suggestion[currentFocus].classList.add("autocomplete-active");
// Autofill search bar with suggestion content
searchBar.value = suggestion[currentFocus].textContent;
searchBar.focus();
}; };
const removeActive = suggestion => { const removeActive = suggestion => {

View File

@ -8,6 +8,7 @@ CONFIG_STRS = [
"near", "url" "near", "url"
]; ];
const setupSearchLayout = () => { const setupSearchLayout = () => {
// Setup search field // Setup search field
const searchBar = document.getElementById("search-bar"); const searchBar = document.getElementById("search-bar");
@ -114,4 +115,8 @@ document.addEventListener("DOMContentLoaded", function() {
setupSearchLayout(); setupSearchLayout();
setupConfigLayout(); setupConfigLayout();
// Focusing on the search input field requires a delay for elements to finish
// loading (seemingly only on FF)
setTimeout(function() { document.getElementById("search-bar").focus(); }, 250);
}); });

View File

@ -8,6 +8,9 @@
<script type="text/javascript" src="/static/js/autocomplete.js"></script> <script type="text/javascript" src="/static/js/autocomplete.js"></script>
<link rel="stylesheet" href="/static/css/{{ 'search-dark' if dark_mode else 'search' }}.css"> <link rel="stylesheet" href="/static/css/{{ 'search-dark' if dark_mode else 'search' }}.css">
<link rel="stylesheet" href="/static/css/header.css"> <link rel="stylesheet" href="/static/css/header.css">
{% if dark_mode %}
<link rel="stylesheet" href="/static/css/dark-theme.css"/>
{% endif %}
<title>{{ query }} - Whoogle Search</title> <title>{{ query }} - Whoogle Search</title>
</head> </head>
<body> <body>

View File

@ -23,6 +23,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/static/css/{{ 'search-dark' if config.dark else 'search' }}.css"> <link rel="stylesheet" href="/static/css/{{ 'search-dark' if config.dark else 'search' }}.css">
<link rel="stylesheet" href="/static/css/main.css"> <link rel="stylesheet" href="/static/css/main.css">
{% if config.dark %}
<link rel="stylesheet" href="/static/css/dark-theme.css"/>
{% endif %}
<title>Whoogle Search</title> <title>Whoogle Search</title>
</head> </head>
<body id="main" style="display: none; background-color: {{ '#000' if config.dark else '#fff' }}"> <body id="main" style="display: none; background-color: {{ '#000' if config.dark else '#fff' }}">
@ -31,7 +34,7 @@
<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">
<input type="text" name="q" id="search-bar" autofocus="autofocus"> <input type="text" name="q" id="search-bar" autofocus="autofocus" autocomplete="off">
</div> </div>
<input type="submit" id="search-submit" value="Search"> <input type="submit" id="search-submit" value="Search">
</div> </div>
@ -42,7 +45,7 @@
<div class="config-fields"> <div class="config-fields">
<form id="config-form" action="/config" method="post"> <form id="config-form" action="/config" method="post">
<div class="config-div"> <div class="config-div">
<label for="config-ctry">Country: </label> <label for="config-ctry">Filter Results by Country: </label>
<select name="ctry" id="config-ctry"> <select name="ctry" id="config-ctry">
{% for ctry in countries %} {% for ctry in countries %}
<option value="{{ ctry.value }}" <option value="{{ ctry.value }}"
@ -53,6 +56,7 @@
</option> </option>
{% endfor %} {% endfor %}
</select> </select>
<div><span class="info-text"> — Note: If enabled, a website will only appear in the results if it is *hosted* in the selected country.</span></div>
</div> </div>
<div class="config-div"> <div class="config-div">
<label for="config-lang-interface">Interface Language: </label> <label for="config-lang-interface">Interface Language: </label>

File diff suppressed because one or more lines are too long

View File

@ -18,7 +18,7 @@ BLACKLIST = [
SITE_ALTS = { SITE_ALTS = {
'twitter.com': 'nitter.net', 'twitter.com': 'nitter.net',
'youtube.com': 'invidio.us', 'youtube.com': 'invidiou.site',
'instagram.com': 'bibliogram.art/u' 'instagram.com': 'bibliogram.art/u'
} }

View File

@ -8,7 +8,7 @@ setuptools.setup(
author='Ben Busby', author='Ben Busby',
author_email='benbusby@protonmail.com', author_email='benbusby@protonmail.com',
name='whoogle-search', name='whoogle-search',
version='0.2.0', version='0.2.1',
include_package_data=True, include_package_data=True,
install_requires=requirements, install_requires=requirements,
description='Self-hosted, ad-free, privacy-respecting Google metasearch engine', description='Self-hosted, ad-free, privacy-respecting Google metasearch engine',