Compare commits
12 Commits
6f336b18d3
...
main
Author | SHA1 | Date | |
---|---|---|---|
5032672341 | |||
eec2d36526 | |||
3f3c0d72f8 | |||
f6b6639905 | |||
3781ae935d | |||
ede223d9d5 | |||
dc1fa6d08a | |||
7df4b38d09 | |||
7644c2c810 | |||
b5e740a5c2 | |||
48a526f404 | |||
94a7880db3 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -6,6 +6,7 @@ yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
public/opensearch.xml
|
||||
|
||||
node_modules
|
||||
dist
|
||||
@ -24,6 +25,8 @@ dist-ssr
|
||||
*.sw?
|
||||
|
||||
# ENV
|
||||
docker/.env
|
||||
!docker/.env.example
|
||||
.env
|
||||
.env*
|
||||
!.env.example
|
||||
|
38
README.md
38
README.md
@ -9,38 +9,36 @@ This fork allows anyone to easily self host this service & add bangs for their o
|
||||
- Searxng *!s*
|
||||
- Gitea *!tea*
|
||||
- OpenWebUI *!ai*
|
||||
- Nextcloud App Store *!cloudapp*
|
||||
- Plex *!plex*
|
||||
- Overseerr *!ov*
|
||||
- Sonarr *!tv*
|
||||
- Radarr *!mv*
|
||||
- Lidarr *!mp3*
|
||||
- ~Overseerr *!ov*~ *Overseerr doesn't seem to work correctly*
|
||||
|
||||
*Note: We have replaced some of the default bangs from duckduckgo with our own bangs in order to have more simple bangs for the self-hosted websites.*
|
||||
|
||||
All you have to do is fill out the environment variables in the .env.example files located in the root directory & the docker directory & rename them to .env.
|
||||
|
||||
You can also easily add your own bangs by adding a new entry in the bangs.ts file.
|
||||
|
||||
## How to self host
|
||||
|
||||
1. Clone the repo & fill out the .env.example files in the root directory & the docker directory & rename them to .env.
|
||||
- *Note: Our docker compose assumes you plan to select an external network.*
|
||||
2. Run the bash script `host-bang` in the docker directory with the root directory of the project as an argument.
|
||||
- *Note: You can also simply run it from the root or docker directory without an argument.*
|
||||
|
||||
### How to update the website
|
||||
|
||||
1. Run the bash script `update-bang` in the docker directory with the root directory of the project as an argument.
|
||||
- *Note: You can also simply run it from the root or docker directory without an argument.*
|
||||
|
||||
## Theo's words
|
||||
|
||||
DuckDuckGo's bang redirects are too slow. Add the following URL as a custom search engine to your browser. Enables all of DuckDuckGo's bangs to work, but much faster.
|
||||
|
||||
```
|
||||
https://bang.gbrown.org?q=%s
|
||||
```
|
||||
|
||||
## How is it that much faster?
|
||||
### How is it that much faster?
|
||||
|
||||
DuckDuckGo does their redirects server side. Their DNS is...not always great. Result is that it often takes ages.
|
||||
|
||||
Theo solved this by doing all of the work client side. Once you've went to https://unduck.link once, the JS is all cache'd and will never need to be downloaded again. Your device does the redirects, not me.
|
||||
|
||||
### How to self host
|
||||
|
||||
1. Clone the repo
|
||||
2. Fill out the .env.example file & rename it to .env
|
||||
3. Run `pnpm install` from the root of the repo
|
||||
4. Run `pnpm build` from the root of the repo
|
||||
5. Navigate to the docker folder & run `sudo docker-compose up -d`
|
||||
|
||||
### How to update the website
|
||||
|
||||
1. Run `pnpm build` from the root of the repo
|
||||
2. Navigate to the docker folder & run `sudo docker compose down && sudo docker-compose up -d`
|
||||
|
3
docker/.env.example
Normal file
3
docker/.env.example
Normal file
@ -0,0 +1,3 @@
|
||||
PORT=5000
|
||||
DOMAIN=bang.mydomain.com
|
||||
NETWORK=bridge
|
@ -13,6 +13,7 @@ RUN pnpm install
|
||||
COPY . .
|
||||
|
||||
# Build the project
|
||||
RUN pnpm run prebuild
|
||||
RUN pnpm run build
|
||||
|
||||
# Stage 2: Serve the app using the same version of Node
|
||||
|
@ -1,23 +1,21 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
bang-web-server:
|
||||
build:
|
||||
context: ../ # point to the parent directory where package.json and source code reside
|
||||
dockerfile: docker/Dockerfile # specific path to the Dockerfile
|
||||
context: ../
|
||||
dockerfile: docker/Dockerfile
|
||||
container_name: bang
|
||||
hostname: bang.gib
|
||||
domainname: bang.gbrown.org
|
||||
hostname: bang
|
||||
networks:
|
||||
- node_apps
|
||||
ports:
|
||||
- "5000:5000"
|
||||
- nginx-bridge
|
||||
#ports:
|
||||
#- 5000:5000
|
||||
tty: true
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ../:/app # mount the parent directory to /app in the container
|
||||
- ../:/app
|
||||
command: serve -s /app/dist -l 5000
|
||||
|
||||
networks:
|
||||
node_apps:
|
||||
nginx-bridge:
|
||||
external: true
|
||||
|
51
docker/host-bang
Executable file
51
docker/host-bang
Executable file
@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Function to check if we're in the correct directory
|
||||
check_directory() {
|
||||
if [ -d "docker" ] && [ -f "package.json" ]; then
|
||||
return 0 # We're in the root directory
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# Initialize root_dir
|
||||
root_dir=""
|
||||
|
||||
# Check if argument is provided
|
||||
if [ $# -eq 1 ]; then
|
||||
# Use provided path
|
||||
if [ -d "$1" ]; then
|
||||
root_dir="$1"
|
||||
cd "$root_dir" || exit 1
|
||||
else
|
||||
echo "Error: Provided directory does not exist"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# No argument provided, try to determine location
|
||||
current_dir=$(basename "$(pwd)")
|
||||
|
||||
if [ "$current_dir" = "docker" ]; then
|
||||
cd .. || exit 1
|
||||
elif [ "$current_dir" != "Bang" ]; then
|
||||
echo "Error: Not in the correct directory and no valid path provided"
|
||||
echo "Please either:"
|
||||
echo "1. Run this script from the Bang root directory"
|
||||
echo "2. Run this script from the docker directory"
|
||||
echo "3. Provide the path to the Bang root directory as an argument"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Verify we're in the correct directory
|
||||
if ! check_directory; then
|
||||
echo "Error: Not in the correct directory structure"
|
||||
echo "Make sure you're in a directory with 'docker' folder and package.json"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
pnpm install
|
||||
pnpm build
|
||||
cd docker || exit 1
|
||||
sudo docker compose up -d
|
||||
cd ..
|
52
docker/update-bang
Executable file
52
docker/update-bang
Executable file
@ -0,0 +1,52 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Function to check if we're in the correct directory
|
||||
check_directory() {
|
||||
if [ -d "docker" ] && [ -f "package.json" ]; then
|
||||
return 0 # We're in the root directory
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# Initialize root_dir
|
||||
root_dir=""
|
||||
|
||||
# Check if argument is provided
|
||||
if [ $# -eq 1 ]; then
|
||||
# Use provided path
|
||||
if [ -d "$1" ]; then
|
||||
root_dir="$1"
|
||||
cd "$root_dir" || exit 1
|
||||
else
|
||||
echo "Error: Provided directory does not exist"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# No argument provided, try to determine location
|
||||
current_dir=$(basename "$(pwd)")
|
||||
|
||||
if [ "$current_dir" = "docker" ]; then
|
||||
cd .. || exit 1
|
||||
elif [ "$current_dir" != "Bang" ]; then
|
||||
echo "Error: Not in the correct directory and no valid path provided"
|
||||
echo "Please either:"
|
||||
echo "1. Run this script from the Bang root directory"
|
||||
echo "2. Run this script from the docker directory"
|
||||
echo "3. Provide the path to the Bang root directory as an argument"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Verify we're in the correct directory
|
||||
if ! check_directory; then
|
||||
echo "Error: Not in the correct directory structure"
|
||||
echo "Make sure you're in a directory with 'docker' folder and package.json"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git pull
|
||||
pnpm build
|
||||
cd docker || exit 1
|
||||
sudo docker compose down
|
||||
sudo docker compose up -d
|
||||
cd ..
|
@ -24,6 +24,12 @@
|
||||
media="print"
|
||||
onload="this.media='all'"
|
||||
/>
|
||||
<link
|
||||
rel="search"
|
||||
type="application/opensearchdescription+xml"
|
||||
title="Bang!"
|
||||
href="/opensearch.xml"
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Bang!</title>
|
||||
<meta
|
||||
|
14
package.json
14
package.json
@ -1,21 +1,25 @@
|
||||
{
|
||||
"name": "unduck",
|
||||
"name": "bang",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"generate-opensearch": "ts-node scripts/generateOpenSearch.ts",
|
||||
"prebuild": "pnpm generate-opensearch",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "~5.7.2",
|
||||
"vite": "^6.1.0"
|
||||
"@types/node": "^22.15.17",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "~5.7.3",
|
||||
"vite": "^6.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"vite-plugin-pwa": "^0.21.1"
|
||||
"vite-plugin-pwa": "^0.21.2"
|
||||
},
|
||||
"packageManager": "pnpm@10.5.2+sha512.da9dc28cd3ff40d0592188235ab25d3202add8a207afbedc682220e4a0029ffbff4562102b9e6e46b4e3f9e8bd53e6d05de48544b0c57d4b0179e22c76d1199b",
|
||||
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39",
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"esbuild"
|
||||
|
1904
pnpm-lock.yaml
generated
1904
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
10
public/opensearch.xml
Normal file
10
public/opensearch.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
|
||||
<ShortName>Bang!</ShortName>
|
||||
<Description>A better default search engine with bangs</Description>
|
||||
<InputEncoding>UTF-8</InputEncoding>
|
||||
<Image width="16" height="16" type="image/svg+xml">https://bang.gbrown.org/bang.svg</Image>
|
||||
<Url type="text/html" method="get" template="https://bang.gbrown.org?q={searchTerms}"/>
|
||||
<Url type="application/opensearchdescription+xml" rel="self" template="https://bang.gbrown.org/opensearch.xml"/>
|
||||
<moz:SearchForm xmlns:moz="http://www.mozilla.org/2006/browser/search/">https://bang.gbrown.org</moz:SearchForm>
|
||||
</OpenSearchDescription>
|
98
scripts/files_to_clipboard
Executable file
98
scripts/files_to_clipboard
Executable file
@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import pyperclip
|
||||
import questionary
|
||||
|
||||
# List of directories to exclude
|
||||
EXCLUDED_DIRS = {'node_modules', '.next', '.venv', '.git', '__pycache__', '.idea', '.vscode', 'ui'}
|
||||
|
||||
def collect_files(project_path):
|
||||
"""
|
||||
Collects files from the project directory, excluding specified directories and filtering by extensions.
|
||||
Returns a list of file paths relative to the project directory.
|
||||
"""
|
||||
collected_files = []
|
||||
|
||||
for root, dirs, files in os.walk(project_path):
|
||||
# Exclude specified directories
|
||||
dirs[:] = [d for d in dirs if d not in EXCLUDED_DIRS]
|
||||
|
||||
for file in files:
|
||||
file_path = Path(root) / file
|
||||
relative_path = file_path.relative_to(project_path)
|
||||
collected_files.append(relative_path)
|
||||
|
||||
return collected_files
|
||||
|
||||
def main():
|
||||
# Parse command-line arguments
|
||||
parser = argparse.ArgumentParser(description='Generate Markdown from selected files.')
|
||||
parser.add_argument('path', nargs='?', default='.', help='Path to the project directory')
|
||||
args = parser.parse_args()
|
||||
|
||||
project_path = Path(args.path).resolve()
|
||||
if not project_path.is_dir():
|
||||
print(f"Error: '{project_path}' is not a directory.")
|
||||
sys.exit(1)
|
||||
|
||||
# Collect files from the project directory
|
||||
file_list = collect_files(project_path)
|
||||
|
||||
if not file_list:
|
||||
print("No files found in the project directory with the specified extensions.")
|
||||
sys.exit(1)
|
||||
|
||||
# Sort file_list for better organization
|
||||
file_list.sort()
|
||||
|
||||
# Interactive file selection using questionary
|
||||
print("\nSelect the files you want to include:")
|
||||
selected_files = questionary.checkbox(
|
||||
"Press space to select files, and Enter when you're done:",
|
||||
choices=[str(f) for f in file_list]
|
||||
).ask()
|
||||
|
||||
if not selected_files:
|
||||
print("No files selected.")
|
||||
sys.exit(1)
|
||||
|
||||
# Generate markdown
|
||||
markdown_lines = []
|
||||
markdown_lines.append('')
|
||||
|
||||
for selected_file in selected_files:
|
||||
file_path = project_path / selected_file
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
# Determine the language for code block from file extension
|
||||
language = file_path.suffix.lstrip('.')
|
||||
markdown_lines.append(f'{selected_file}')
|
||||
markdown_lines.append(f'```{language}')
|
||||
markdown_lines.append(content)
|
||||
markdown_lines.append('```')
|
||||
markdown_lines.append('')
|
||||
except Exception as e:
|
||||
print(f"Error reading file {selected_file}: {e}")
|
||||
|
||||
# Copy markdown content to clipboard
|
||||
markdown_text = '\n'.join(markdown_lines)
|
||||
pyperclip.copy(markdown_text)
|
||||
print("Markdown content has been copied to the clipboard.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Check if required libraries are installed
|
||||
try:
|
||||
import questionary
|
||||
import pyperclip
|
||||
except ImportError as e:
|
||||
missing_module = e.name
|
||||
print(f"Error: Missing required module '{missing_module}'.")
|
||||
print(f"Please install it by running: pip install {missing_module}")
|
||||
sys.exit(1)
|
||||
|
||||
main()
|
33
scripts/generateOpenSearch.ts
Normal file
33
scripts/generateOpenSearch.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
// Get the current file's directory
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
// Get the Bang URL from environment or use a default for local development
|
||||
const bangUrl = process.env.VITE_BANG_URL || 'https://bang.gbrown.org';
|
||||
|
||||
// Create the OpenSearch XML content
|
||||
const openSearchXml = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
|
||||
<ShortName>Bang!</ShortName>
|
||||
<Description>A better default search engine with bangs</Description>
|
||||
<InputEncoding>UTF-8</InputEncoding>
|
||||
<Image width="16" height="16" type="image/svg+xml">${bangUrl}/bang.svg</Image>
|
||||
<Url type="text/html" method="get" template="${bangUrl}?q={searchTerms}"/>
|
||||
<Url type="application/opensearchdescription+xml" rel="self" template="${bangUrl}/opensearch.xml"/>
|
||||
<moz:SearchForm xmlns:moz="http://www.mozilla.org/2006/browser/search/">${bangUrl}</moz:SearchForm>
|
||||
</OpenSearchDescription>`;
|
||||
|
||||
// Ensure the public directory exists
|
||||
const publicDir = path.resolve(__dirname, '../public');
|
||||
if (!fs.existsSync(publicDir)) {
|
||||
fs.mkdirSync(publicDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Write the OpenSearch XML file
|
||||
fs.writeFileSync(path.join(publicDir, 'opensearch.xml'), openSearchXml);
|
||||
|
||||
console.log('OpenSearch XML file generated successfully!');
|
@ -3,7 +3,6 @@
|
||||
const SEARXNG_URL = import.meta.env.VITE_SEARXNG_URL as string;
|
||||
const GITEA_URL = import.meta.env.VITE_GITEA_URL as string;
|
||||
const OPENWEBUI_URL = import.meta.env.VITE_OPENWEBUI_URL as string;
|
||||
const NEXTCLOUD_APPS_URL = import.meta.env.VITE_NEXTCLOUD_URL as string ?? "https://apps.nextcloud.com";
|
||||
const PLEX_URL = import.meta.env.VITE_PLEX_URL as string;
|
||||
const OVERSEERR_URL = import.meta.env.VITE_OVERSEERR_URL as string;
|
||||
const SONARR_URL = import.meta.env.VITE_SONARR_URL as string;
|
||||
@ -41,7 +40,7 @@ export const bangs = [
|
||||
s: "Plex",
|
||||
sc: "Movies",
|
||||
t: "plex",
|
||||
u: `${PLEX_URL}/web/index.html#!/search?pivot=top&query=query={{{s}}}`,
|
||||
u: `${PLEX_URL}/web/index.html#!/search?pivot=top&query={{{s}}}`,
|
||||
},
|
||||
{
|
||||
c: "Entertainment",
|
||||
@ -73583,12 +73582,12 @@ export const bangs = [
|
||||
},
|
||||
{
|
||||
c: "Tech",
|
||||
d: NEXTCLOUD_APPS_URL,
|
||||
d: "apps.nextcloud.com",
|
||||
r: 0,
|
||||
s: "Nextcloud App Store",
|
||||
sc: "Sysadmin",
|
||||
t: "cloudapp",
|
||||
u: `${NEXTCLOUD_APPS_URL}/?q={{{s}}}`,
|
||||
u: "https://apps.nextcloud.com/search/?q={{{s}}}",
|
||||
},
|
||||
{
|
||||
c: "Entertainment",
|
||||
|
Reference in New Issue
Block a user