0
0
mirror of https://github.com/neon-mmd/websurfx.git synced 2024-11-22 14:08:23 -05:00

Merge branch 'rolling' into improve-async-multithreading

This commit is contained in:
neon_arch 2023-07-15 19:50:31 +03:00
commit 74e4fc6169
51 changed files with 548 additions and 339 deletions

16
.cspell.json Normal file
View File

@ -0,0 +1,16 @@
{
"ignorePaths": [
"**/node_modules/**",
"**/vscode-extension/**",
"**/.git/**",
"**/.pnpm-lock.json",
".vscode",
"megalinter",
"package-lock.json",
"report"
],
"language": "en",
"noConfigSearch": true,
"words": ["megalinter", "oxsecurity"],
"version": "0.2"
}

View File

@ -1,3 +1,4 @@
---
name: Contributors List name: Contributors List
on: on:

View File

@ -1,3 +1,4 @@
---
name: Welcome first time contributors name: Welcome first time contributors
on: on:

View File

@ -1,3 +1,4 @@
---
name: Import open source standard labels name: Import open source standard labels
on: on:

89
.github/workflows/mega-linter.yml vendored Normal file
View File

@ -0,0 +1,89 @@
---
# MegaLinter GitHub Action configuration file
# More info at https://megalinter.io
name: MegaLinter
on:
# Trigger mega-linter at every push. Action will also be visible from Pull Requests to rolling
push: # Comment this line to trigger action only on pull-requests (not recommended if you don't pay for GH Actions)
pull_request:
branches: [rolling]
env: # Comment env block if you do not want to apply fixes
# Apply linter fixes configuration
APPLY_FIXES: all # When active, APPLY_FIXES must also be defined as environment variable (in github/workflows/mega-linter.yml or other CI tool)
APPLY_FIXES_EVENT: pull_request # Decide which event triggers application of fixes in a commit or a PR (pull_request, push, all)
APPLY_FIXES_MODE: commit # If APPLY_FIXES is used, defines if the fixes are directly committed (commit) or posted in a PR (pull_request)
concurrency:
group: ${{ github.ref }}-${{ github.workflow }}
cancel-in-progress: true
jobs:
build:
name: MegaLinter
runs-on: ubuntu-latest
permissions:
# Give the default GITHUB_TOKEN write permission to commit and push, comment issues & post new PR
# Remove the ones you do not need
contents: write
issues: write
pull-requests: write
steps:
# Git Checkout
- name: Checkout Code
uses: actions/checkout@v3
with:
token: ${{ secrets.PAT || secrets.GITHUB_TOKEN }}
# MegaLinter
- name: MegaLinter
id: ml
# You can override MegaLinter flavor used to have faster performances
# More info at https://megalinter.io/flavors/
uses: oxsecurity/megalinter/flavors/cupcake@v7.1.0
env:
# All available variables are described in documentation
# https://megalinter.io/configuration/
VALIDATE_ALL_CODEBASE: true # Set ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} to validate only diff with main branch
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# ADD YOUR CUSTOM ENV VARIABLES HERE TO OVERRIDE VALUES OF .mega-linter.yml AT THE ROOT OF YOUR REPOSITORY
# Upload MegaLinter artifacts
- name: Archive production artifacts
if: ${{ success() }} || ${{ failure() }}
uses: actions/upload-artifact@v3
with:
name: MegaLinter reports
path: |
megalinter-reports
mega-linter.log
# Create pull request if applicable (for now works only on PR from same repository, not from forks)
- name: Create Pull Request with applied fixes
id: cpr
if: steps.ml.outputs.has_updated_sources == 1 && (env.APPLY_FIXES_EVENT == 'all' || env.APPLY_FIXES_EVENT == github.event_name) && env.APPLY_FIXES_MODE == 'pull_request' && (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository) && !contains(github.event.head_commit.message, 'skip fix')
uses: peter-evans/create-pull-request@v5
with:
token: ${{ secrets.PAT || secrets.GITHUB_TOKEN }}
commit-message: "[MegaLinter] Apply linters automatic fixes"
title: "[MegaLinter] Apply linters automatic fixes"
labels: bot
- name: Create PR output
if: steps.ml.outputs.has_updated_sources == 1 && (env.APPLY_FIXES_EVENT == 'all' || env.APPLY_FIXES_EVENT == github.event_name) && env.APPLY_FIXES_MODE == 'pull_request' && (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository) && !contains(github.event.head_commit.message, 'skip fix')
run: |
echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}"
echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}"
# Push new commit if applicable (for now works only on PR from same repository, not from forks)
- name: Prepare commit
if: steps.ml.outputs.has_updated_sources == 1 && (env.APPLY_FIXES_EVENT == 'all' || env.APPLY_FIXES_EVENT == github.event_name) && env.APPLY_FIXES_MODE == 'commit' && github.ref != 'refs/heads/main' && (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository) && !contains(github.event.head_commit.message, 'skip fix')
run: sudo chown -Rc $UID .git/
- name: Commit and push applied linter fixes
if: steps.ml.outputs.has_updated_sources == 1 && (env.APPLY_FIXES_EVENT == 'all' || env.APPLY_FIXES_EVENT == github.event_name) && env.APPLY_FIXES_MODE == 'commit' && github.ref != 'refs/heads/main' && (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository) && !contains(github.event.head_commit.message, 'skip fix')
uses: stefanzweifel/git-auto-commit-action@v4
with:
branch: ${{ github.event.pull_request.head.ref || github.head_ref || github.ref }}
commit_message: "[MegaLinter] Apply linters fixes"
commit_user_name: megalinter-bot
commit_user_email: nicolas.vuillamy@ox.security

View File

@ -1,3 +1,4 @@
---
name: Rust name: Rust
on: on:

View File

@ -1,3 +1,4 @@
---
name: Rust format and clippy checks name: Rust format and clippy checks
on: on:
push: push:

View File

@ -1,3 +1,4 @@
---
# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time. # This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
# #
# You can adjust the behavior by modifying this file. # You can adjust the behavior by modifying this file.

1
.gitignore vendored
View File

@ -3,3 +3,4 @@ package.json
package-lock.json package-lock.json
dump.rdb dump.rdb
.vscode .vscode
megalinter-reports/

44
.gitpod.yml Normal file
View File

@ -0,0 +1,44 @@
---
image: gitpod/workspace-base
# Commands that will run on workspace start
tasks:
- name: Setup, Install & Build
before: apt install cargo redis-server nodejs npm -y && cargo test
init: cargo install cargo-watch
command: redis-server --port 8080 & cargo watch -q -w "." -x "run"
# Ports to expose on workspace startup
ports:
- name: Website
description: Website Preview
port: 8080
onOpen: open-preview
# vscode IDE setup
vscode:
extensions:
- vadimcn.vscode-lldb
- cschleiden.vscode-github-actions
- rust-lang.rust
- bungcip.better-toml
- serayuzgur.crates
- usernamehw.errorlens
- DavidAnson.vscode-markdownlint
- esbenp.prettier-vscode
- stylelint.vscode-stylelint
- dbaeumer.vscode-eslint
- evgeniypeshkov.syntax-highlighter
- redhat.vscode-yaml
- ms-azuretools.vscode-docker
- Catppuccin.catppuccin-vsc
- PKief.material-icon-theme
- oderwat.indent-rainbow
- formulahendry.auto-rename-tag
- eamodio.gitlens
github:
prebuilds:
master: true
branches: true
pullRequests: true
pullRequestsFromForks: true
addCheck: true
addComment: false
addBadge: true

22
.mega-linter.yml Normal file
View File

@ -0,0 +1,22 @@
---
# Configuration file for MegaLinter
# See all available variables at https://megalinter.io/configuration/ and in linters documentation
APPLY_FIXES: all # all, none, or list of linter keys
# ENABLE: # If you use ENABLE variable, all other languages/formats/tooling-formats will be disabled by default
ENABLE_LINTERS: # If you use ENABLE_LINTERS variable, all other linters will be disabled by default
- RUST_CLIPPY
- JAVASCRIPT_ES
- CSS_STYLELINT
- MARKDOWN_MARKDOWNLINT
- YAML_YAMLLINT
- HTML_DJLINT
- ACTION_ACTIONLINT
- DOCKERFILE_HADOLINT
- SPELL_CSPELL
# DISABLE:
# - COPYPASTE # Uncomment to disable checks of excessive copy-pastes
# - SPELL # Uncomment to disable checks of spelling mistakes
SHOW_ELAPSED_TIME: true
FILEIO_REPORTER: false
# DISABLE_ERRORS: true # Uncomment if you want MegaLinter to detect errors but not block CI to pass

View File

@ -14,7 +14,7 @@ Know how to fix or improve a github action?. Consider Submitting a Pull request
## Source Code ## Source Code
You should know atleast one of the things below to start contributing: You should know at least one of the things below to start contributing:
- Rust basics - Rust basics
- Actix-web crate basics - Actix-web crate basics

View File

@ -3,7 +3,7 @@ FROM rust:latest AS chef
# it will be cached from the second build onwards # it will be cached from the second build onwards
RUN cargo install cargo-chef RUN cargo install cargo-chef
WORKDIR app WORKDIR /app
FROM chef AS planner FROM chef AS planner
COPY . . COPY . .
@ -20,7 +20,7 @@ RUN cargo install --path .
# We do not need the Rust toolchain to run the binary! # We do not need the Rust toolchain to run the binary!
FROM gcr.io/distroless/cc-debian11 FROM gcr.io/distroless/cc-debian11
COPY --from=builder ./public/ ./public/ COPY --from=builder /app/public/ /opt/websurfx/public/
COPY --from=builder ./websurfx/ ./websurfx/ COPY --from=builder /app/websurfx/config.lua /etc/xdg/websurfx/config.lua
COPY --from=builder /usr/local/cargo/bin/* /usr/local/bin/ COPY --from=builder /usr/local/cargo/bin/* /usr/local/bin/
CMD ["websurfx"] CMD ["websurfx"]

View File

@ -16,7 +16,7 @@
## Author's checklist ## Author's checklist
<!-- additional notes for reviewiers --> <!-- additional notes for reviewers -->
## Related issues ## Related issues

View File

@ -59,7 +59,7 @@
- **Community** - **Community**
- [📊 System Requirements](#system-requirements-) - [📊 System Requirements](#system-requirements-)
- [🗨️ FAQ (Frequently Asked Questions)](#faq-frequently-asked-questions-) - [🗨️ FAQ (Frequently Asked Questions)](#faq-frequently-asked-questions-)
- [📣 More Contributers Wanted](#more-contributers-wanted-) - [📣 More Contributors Wanted](#more-contributors-wanted-)
- [💖 Supporting Websurfx](#supporting-websurfx-) - [💖 Supporting Websurfx](#supporting-websurfx-)
- [📘 Documentation](#documentation-) - [📘 Documentation](#documentation-)
- [🛣️ Roadmap](#roadmap-) - [🛣️ Roadmap](#roadmap-)
@ -165,7 +165,7 @@ Websurfx is based on Rust due to its memory safety features, which prevents vuln
**[⬆️ Back to Top](#--)** **[⬆️ Back to Top](#--)**
# More Contributers Wanted 📣 # More Contributors Wanted 📣
We are looking for more willing contributors to help grow this project. For more information on how you can contribute, check out the [project board](https://github.com/neon-mmd/websurfx/projects?query=is%3Aopen) and the [CONTRIBUTING.md](CONTRIBUTING.md) file for guidelines and rules for making contributions. We are looking for more willing contributors to help grow this project. For more information on how you can contribute, check out the [project board](https://github.com/neon-mmd/websurfx/projects?query=is%3Aopen) and the [CONTRIBUTING.md](CONTRIBUTING.md) file for guidelines and rules for making contributions.

View File

@ -1,3 +1,4 @@
---
version: "3.9" version: "3.9"
services: services:
app: app:

View File

@ -1,11 +1,11 @@
:root { :root {
--bg: #1e1e2e; --background-color: #1e1e2e;
--fg: #cdd6f4; --foreground-color: #cdd6f4;
--1: #45475a; --color-one: #45475a;
--2: #f38ba8; --color-two: #f38ba8;
--3: #a6e3a1; --color-three: #a6e3a1;
--4: #f9e2af; --color-four: #f9e2af;
--5: #89b4fa; --color-five: #89b4fa;
--6: #f5c2e7; --color-six: #f5c2e7;
--7: #ffffff; --color-seven: #ffffff;
} }

View File

@ -1,11 +1,11 @@
:root { :root {
--bg: #000000; --background-color: #000000;
--fg: #ffffff; --foreground-color: #ffffff;
--1: #121212; --color-one: #121212;
--2: #808080; --color-two: #808080;
--3: #999999; --color-three: #999999;
--4: #666666; --color-four: #666666;
--5: #bfbfbf; --color-five: #bfbfbf;
--6: #e0e0e0; --color-six: #e0e0e0;
--7: #555555; --color-seven: #555555;
} }

View File

@ -1,11 +1,11 @@
:root { :root {
--bg: #44475a; --background-color: #44475a;
--fg: #8be9fd; --foreground-color: #8be9fd;
--1: #ff5555; --color-one: #ff5555;
--2: #50fa7b; --color-two: #50fa7b;
--3: #ffb86c; --color-three: #ffb86c;
--4: #bd93f9; --color-four: #bd93f9;
--5: #ff79c6; --color-five: #ff79c6;
--6: #94a3a5; --color-six: #94a3a5;
--7: #ffffff; --color-seven: #ffffff;
} }

View File

@ -1,11 +1,11 @@
:root { :root {
--bg: #282828; --background-color: #1d2021;
--fg: #ebdbb2; --foreground-color: #ebdbb2;
--1: #cc241d; --color-one: #282828;
--2: #98971a; --color-two: #98971a;
--3: #d79921; --color-three: #d79921;
--4: #458588; --color-four: #458588;
--5: #b16286; --color-five: #b16286;
--6: #689d6a; --color-six: #689d6a;
--7: #ffffff; --color-seven: #ffffff;
} }

View File

@ -1,11 +1,11 @@
:root { :root {
--bg: #403e41; --background-color: #49483Eff;
--fg: #fcfcfa; --foreground-color: #FFB269;
--1: #ff6188; --color-one: #272822ff;
--2: #a9dc76; --color-two: #61AFEF;
--3: #ffd866; --color-three: #ffd866;
--4: #fc9867; --color-four: #fc9867;
--5: #ab9df2; --color-five: #ab9df2;
--6: #78dce8; --color-six: #78dce8;
--7: #ffffff; --color-seven: #ffffff;
} }

View File

@ -1,11 +1,11 @@
:root { :root {
--bg: #2e3440; --background-color: #122736ff;
--fg: #d8dee9; --foreground-color: #a2e2a9;
--1: #3b4252; --color-one: #121B2Cff;
--2: #bf616a; --color-two: #f08282;
--3: #a3be8c; --color-three: #ABC5AAff;
--4: #ebcb8b; --color-four: #e6d2d2;
--5: #81a1c1; --color-five: #81a1c1;
--6: #b48ead; --color-six: #e2ecd6;
--7: #ffffff; --color-seven: #fff;
} }

View File

@ -1,11 +1,11 @@
:root { :root {
--bg: #1b2b34; --background-color: #1b2b34;
--fg: #d8dee9; --foreground-color: #d8dee9;
--1: #343d46; --color-one: #343d46;
--2: #ec5f67; --color-two: #5FB3B3ff;
--3: #99c794; --color-three: #69Cf;
--4: #fac863; --color-four: #99c794;
--5: #6699cc; --color-five: #69c;
--6: #c594c5; --color-six: #c594c5;
--7: #ffffff; --color-seven: #D8DEE9ff;
} }

View File

@ -1,11 +1,11 @@
:root { :root {
--bg: #282c34; --background-color: #282c34;
--fg: #abb2bf; --foreground-color: #abb2bf;
--1: #3b4048; --color-one: #3b4048;
--2: #a3be8c; --color-two: #a3be8c;
--3: #b48ead; --color-three: #b48ead;
--4: #c8ccd4; --color-four: #c8ccd4;
--5: #e06c75; --color-five: #e06c75;
--6: #61afef; --color-six: #61afef;
--7: #be5046; --color-seven: #be5046;
} }

View File

@ -1,11 +1,11 @@
:root { :root {
--bg: #002b36; --background-color: #002b36;
--fg: #839496; --foreground-color: #c9e0e6;
--1: #073642; --color-one: #073642;
--2: #dc322f; --color-two: #2AA198ff;
--3: #859900; --color-three: #2AA198ff;
--4: #b58900; --color-four: #EEE8D5ff;
--5: #268bd2; --color-five: #268bd2;
--6: #d33682; --color-six: #d33682;
--7: #ffffff; --color-seven: #fff;
} }

View File

@ -1,11 +1,11 @@
:root { :root {
--bg: #fdf6e3; --background-color: #EEE8D5ff;
--fg: #657b83; --foreground-color: #b1ab97;
--1: #073642; --color-one: #fdf6e3;
--2: #dc322f; --color-two: #DC322Fff;
--3: #859900; --color-three: #586E75ff;
--4: #b58900; --color-four: #b58900;
--5: #268bd2; --color-five: #268bd2;
--6: #d33682; --color-six: #d33682;
--7: #ffffff; --color-seven: #fff;
} }

View File

@ -1,11 +1,11 @@
:root { :root {
--bg: #1a1b26; --background-color: #1a1b26;
--fg: #c0caf5; --foreground-color: #c0caf5;
--1: #32364a; --color-one: #32364a;
--2: #a9b1d6; --color-two: #a9b1d6;
--3: #5a5bb8; --color-three: #5a5bb8;
--4: #6b7089; --color-four: #6b7089;
--5: #e2afff; --color-five: #e2afff;
--6: #a9a1e1; --color-six: #a9a1e1;
--7: #988bc7; --color-seven: #988bc7;
} }

View File

@ -1,11 +1,11 @@
:root { :root {
--bg: #1d1f21; --background-color: #35383Cff;
--fg: #c5c8c6; --foreground-color: #D7DAD8ff;
--1: #cc6666; --color-one: #1d1f21;
--2: #b5bd68; --color-two: #D77C79ff;
--3: #f0c674; --color-three: #f0c674;
--4: #81a2be; --color-four: #92B2CAff;
--5: #b294bb; --color-five: #C0A7C7ff;
--6: #8abeb7; --color-six: #9AC9C4ff;
--7: #ffffff; --color-seven: #fff;
} }

View File

@ -1,15 +1,26 @@
// This function is executed when any page on the website finishes loading and /**
// this function retrieves the cookies if it is present on the user's machine. * This function is executed when any page on the website finishes loading and
// If it is available then the saved cookies is display in the cookies tab * this function retrieves the cookies if it is present on the user's machine.
// otherwise an appropriate message is displayed if it is not available. * If it is available then the saved cookies is display in the cookies tab
* otherwise an appropriate message is displayed if it is not available.
*
* @function
* @listens DOMContentLoaded
* @returns {void}
*/
document.addEventListener( document.addEventListener(
'DOMContentLoaded', 'DOMContentLoaded',
() => { () => {
try { try {
// Decode the cookie value
let cookie = decodeURIComponent(document.cookie) let cookie = decodeURIComponent(document.cookie)
// Set the value of the input field to the decoded cookie value if it is not empty
// Otherwise, display a message indicating that no cookies have been saved on the user's system
document.querySelector('.cookies input').value = document.querySelector('.cookies input').value =
cookie !== '' ? cookie : 'No cookies have been saved on your system' cookie !== '' ? cookie : 'No cookies have been saved on your system'
} catch (error) { } catch (error) {
// If there is an error decoding the cookie, log the error to the console
// and display an error message in the input field
console.error('Error decoding cookie:', error) console.error('Error decoding cookie:', error)
document.querySelector('.cookies input').value = 'Error decoding cookie' document.querySelector('.cookies input').value = 'Error decoding cookie'
} }

View File

@ -1,5 +1,5 @@
/** /**
* Navigates to the next page by incrementing the current page number in the URL query parameters. * Navigates to the next page by incrementing the current page number in the URL query string.
* @returns {void} * @returns {void}
*/ */
function navigate_forward() { function navigate_forward() {
@ -19,7 +19,7 @@ function navigate_forward() {
} }
/** /**
* Navigates to the previous page by decrementing the current page number in the URL query parameters. * Navigates to the previous page by decrementing the current page number in the URL query string.
* @returns {void} * @returns {void}
*/ */
function navigate_backward() { function navigate_backward() {
@ -30,8 +30,8 @@ function navigate_backward() {
let page = parseInt(searchParams.get('page')); let page = parseInt(searchParams.get('page'));
if (isNaN(page)) { if (isNaN(page)) {
page = 1; page = 0;
} else if (page > 1) { } else if (page > 0) {
page--; page--;
} }

View File

@ -1,5 +1,7 @@
// This function handles the toggling of selections of all upstream search engines /**
// options in the settings page under the tab engines. * This function handles the toggling of selections of all upstream search engines
* options in the settings page under the tab engines.
*/
function toggleAllSelection() { function toggleAllSelection() {
document document
.querySelectorAll('.engine') .querySelectorAll('.engine')
@ -10,25 +12,36 @@ function toggleAllSelection() {
) )
} }
// This function adds the functionality to sidebar buttons to only show settings /**
// related to that tab. * This function adds the functionality to sidebar buttons to only show settings
* related to that tab.
* @param {HTMLElement} current_tab - The current tab that was clicked.
*/
function setActiveTab(current_tab) { function setActiveTab(current_tab) {
// Remove the active class from all tabs and buttons
document document
.querySelectorAll('.tab') .querySelectorAll('.tab')
.forEach((tab) => tab.classList.remove('active')) .forEach((tab) => tab.classList.remove('active'))
document document
.querySelectorAll('.btn') .querySelectorAll('.btn')
.forEach((tab) => tab.classList.remove('active')) .forEach((tab) => tab.classList.remove('active'))
// Add the active class to the current tab and its corresponding settings
current_tab.classList.add('active') current_tab.classList.add('active')
document document
.querySelector(`.${current_tab.innerText.toLowerCase().replace(' ', '_')}`) .querySelector(`.${current_tab.innerText.toLowerCase().replace(' ', '_')}`)
.classList.add('active') .classList.add('active')
} }
// This function adds the functionality to save all the user selected preferences /**
// to be saved in a cookie on the users machine. * This function adds the functionality to save all the user selected preferences
* to be saved in a cookie on the users machine.
*/
function setClientSettings() { function setClientSettings() {
// Create an object to store the user's preferences
let cookie_dictionary = new Object() let cookie_dictionary = new Object()
// Loop through all select tags and add their values to the cookie dictionary
document.querySelectorAll('select').forEach((select_tag) => { document.querySelectorAll('select').forEach((select_tag) => {
if (select_tag.name === 'themes') { if (select_tag.name === 'themes') {
cookie_dictionary['theme'] = select_tag.value cookie_dictionary['theme'] = select_tag.value
@ -36,6 +49,8 @@ function setClientSettings() {
cookie_dictionary['colorscheme'] = select_tag.value cookie_dictionary['colorscheme'] = select_tag.value
} }
}) })
// Loop through all engine checkboxes and add their values to the cookie dictionary
let engines = [] let engines = []
document.querySelectorAll('.engine').forEach((engine_checkbox) => { document.querySelectorAll('.engine').forEach((engine_checkbox) => {
if (engine_checkbox.checked === true) { if (engine_checkbox.checked === true) {
@ -43,33 +58,44 @@ function setClientSettings() {
} }
}) })
cookie_dictionary['engines'] = engines cookie_dictionary['engines'] = engines
// Set the expiration date for the cookie to 1 year from the current date
let expiration_date = new Date() let expiration_date = new Date()
expiration_date.setFullYear(expiration_date.getFullYear() + 1) expiration_date.setFullYear(expiration_date.getFullYear() + 1)
// Save the cookie to the user's machine
document.cookie = `appCookie=${JSON.stringify( document.cookie = `appCookie=${JSON.stringify(
cookie_dictionary cookie_dictionary
)}; expires=${expiration_date.toUTCString()}` )}; expires=${expiration_date.toUTCString()}`
// Display a success message to the user
document.querySelector('.message').innerText = document.querySelector('.message').innerText =
'✅ The settings have been saved sucessfully!!' '✅ The settings have been saved sucessfully!!'
// Clear the success message after 10 seconds
setTimeout(() => { setTimeout(() => {
document.querySelector('.message').innerText = '' document.querySelector('.message').innerText = ''
}, 10000) }, 10000)
} }
// This functions gets the saved cookies if it is present on the user's machine If it /**
// is available then it is parsed and converted to an object which is then used to * This functions gets the saved cookies if it is present on the user's machine If it
// retrieve the preferences that the user had selected previously and is then loaded in the * is available then it is parsed and converted to an object which is then used to
// website otherwise the function does nothing and the default server side settings are loaded. * retrieve the preferences that the user had selected previously and is then loaded in the
* website otherwise the function does nothing and the default server side settings are loaded.
*/
function getClientSettings() { function getClientSettings() {
// Get the appCookie from the user's machine
let cookie = decodeURIComponent(document.cookie) let cookie = decodeURIComponent(document.cookie)
// If the cookie is not empty, parse it and use it to set the user's preferences
if (cookie !== '') { if (cookie !== '') {
let cookie_value = decodeURIComponent(document.cookie) let cookie_value = decodeURIComponent(document.cookie)
.split(';') .split(';')
.map((item) => item.split('=')) .map((item) => item.split('='))
.reduce((acc, [_, v]) => (acc = JSON.parse(v)) && acc, {}) .reduce((acc, [_, v]) => (acc = JSON.parse(v)) && acc, {})
// Loop through all link tags and update their href values to match the user's preferences
let links = Array.from(document.querySelectorAll('link')).forEach( let links = Array.from(document.querySelectorAll('link')).forEach(
(item) => { (item) => {
if (item.href.includes('static/themes')) { if (item.href.includes('static/themes')) {

View File

@ -16,7 +16,7 @@ body {
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
height: 100vh; height: 100vh;
background: var(--1); background: var(--color-one);
} }
/* styles for the index page */ /* styles for the index page */
@ -46,7 +46,7 @@ body {
outline: none; outline: none;
border: none; border: none;
box-shadow: rgba(0, 0, 0, 1); box-shadow: rgba(0, 0, 0, 1);
background: var(--fg); background: var(--foreground-color);
} }
.search_bar button { .search_bar button {
@ -59,8 +59,8 @@ body {
outline: none; outline: none;
border: none; border: none;
gap: 0; gap: 0;
background: var(--bg); background: var(--background-color);
color: var(--3); color: var(--color-three);
font-weight: 600; font-weight: 600;
letter-spacing: 0.1rem; letter-spacing: 0.1rem;
} }
@ -73,7 +73,7 @@ body {
/* styles for the footer and header */ /* styles for the footer and header */
header { header {
background: var(--bg); background: var(--background-color);
width: 100%; width: 100%;
display: flex; display: flex;
justify-content: right; justify-content: right;
@ -96,7 +96,7 @@ footer ul li a,
header ul li a:visited, header ul li a:visited,
footer ul li a:visited { footer ul li a:visited {
text-decoration: none; text-decoration: none;
color: var(--2); color: var(--color-two);
text-transform: capitalize; text-transform: capitalize;
letter-spacing: 0.1rem; letter-spacing: 0.1rem;
} }
@ -107,12 +107,12 @@ header ul li a {
header ul li a:hover, header ul li a:hover,
footer ul li a:hover { footer ul li a:hover {
color: var(--5); color: var(--color-five);
} }
footer div span { footer div span {
font-size: 1.5rem; font-size: 1.5rem;
color: var(--4); color: var(--color-four);
} }
footer div { footer div {
@ -121,7 +121,7 @@ footer div {
} }
footer { footer {
background: var(--bg); background: var(--background-color);
width: 100%; width: 100%;
padding: 1rem; padding: 1rem;
display: flex; display: flex;
@ -158,28 +158,28 @@ footer {
.results_aggregated .result h1 a { .results_aggregated .result h1 a {
font-size: 1.5rem; font-size: 1.5rem;
color: var(--2); color: var(--color-two);
text-decoration: none; text-decoration: none;
letter-spacing: 0.1rem; letter-spacing: 0.1rem;
} }
.results_aggregated .result h1 a:hover { .results_aggregated .result h1 a:hover {
color: var(--5); color: var(--color-five);
} }
.results_aggregated .result h1 a:visited { .results_aggregated .result h1 a:visited {
color: var(--bg); color: var(--background-color);
} }
.results_aggregated .result small { .results_aggregated .result small {
color: var(--3); color: var(--color-three);
font-size: 1.1rem; font-size: 1.1rem;
word-wrap: break-word; word-wrap: break-word;
line-break: anywhere; line-break: anywhere;
} }
.results_aggregated .result p { .results_aggregated .result p {
color: var(--fg); color: var(--foreground-color);
font-size: 1.2rem; font-size: 1.2rem;
margin-top: 0.3rem; margin-top: 0.3rem;
word-wrap: break-word; word-wrap: break-word;
@ -190,7 +190,7 @@ footer {
text-align: right; text-align: right;
font-size: 1.2rem; font-size: 1.2rem;
padding: 1rem; padding: 1rem;
color: var(--5); color: var(--color-five);
} }
/* Styles for the 404 page */ /* Styles for the 404 page */
@ -233,12 +233,12 @@ footer {
.error_content p a, .error_content p a,
.error_content p a:visited { .error_content p a:visited {
color: var(--2); color: var(--color-two);
text-decoration: none; text-decoration: none;
} }
.error_content p a:hover { .error_content p a:hover {
color: var(--5); color: var(--color-five);
} }
.page_navigation { .page_navigation {
@ -249,8 +249,8 @@ footer {
} }
.page_navigation button { .page_navigation button {
background: var(--bg); background: var(--background-color);
color: var(--fg); color: var(--foreground-color);
padding: 1rem; padding: 1rem;
border-radius: 0.5rem; border-radius: 0.5rem;
outline: none; outline: none;
@ -265,12 +265,12 @@ footer {
.about-container article { .about-container article {
font-size: 1.5rem; font-size: 1.5rem;
color: var(--fg); color: var(--foreground-color);
padding-bottom: 10px; padding-bottom: 10px;
} }
.about-container article h1 { .about-container article h1 {
color: var(--2); color: var(--color-two);
font-size: 2.8rem; font-size: 2.8rem;
} }
@ -279,17 +279,17 @@ footer {
} }
.about-container a { .about-container a {
color: var(--3); color: var(--color-three);
} }
.about-container article h2 { .about-container article h2 {
color: var(--3); color: var(--color-three);
font-size: 1.8rem; font-size: 1.8rem;
padding-bottom: 10px; padding-bottom: 10px;
} }
.about-container p { .about-container p {
color: var(--fg); color: var(--foreground-color);
font-size: 1.6rem; font-size: 1.6rem;
padding-bottom: 10px; padding-bottom: 10px;
} }
@ -310,12 +310,12 @@ footer {
} }
.settings h1 { .settings h1 {
color: var(--2); color: var(--color-two);
font-size: 2.5rem; font-size: 2.5rem;
} }
.settings hr { .settings hr {
border-color: var(--3); border-color: var(--color-three);
margin: 0.3rem 0 1rem 0; margin: 0.3rem 0 1rem 0;
} }
@ -331,7 +331,7 @@ footer {
border-radius: 5px; border-radius: 5px;
font-weight: bold; font-weight: bold;
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
color: var(--fg); color: var(--foreground-color);
text-transform: capitalize; text-transform: capitalize;
gap: 1.5rem; gap: 1.5rem;
} }
@ -342,12 +342,12 @@ footer {
} }
.settings_container .sidebar .btn.active { .settings_container .sidebar .btn.active {
background-color: var(--2); background-color: var(--color-two);
} }
.settings_container .main_container { .settings_container .main_container {
width: 70%; width: 70%;
border-left: 1.5px solid var(--3); border-left: 1.5px solid var(--color-three);
padding-left: 3rem; padding-left: 3rem;
} }
@ -365,8 +365,8 @@ footer {
margin-top: 1rem; margin-top: 1rem;
padding: 1rem 2rem; padding: 1rem 2rem;
font-size: 1.5rem; font-size: 1.5rem;
background: var(--3); background: var(--color-three);
color: var(--bg); color: var(--background-color);
border-radius: 0.5rem; border-radius: 0.5rem;
border: 2px solid transparent; border: 2px solid transparent;
font-weight: bold; font-weight: bold;
@ -383,13 +383,13 @@ footer {
.settings_container .main_container .message { .settings_container .main_container .message {
font-size: 1.5rem; font-size: 1.5rem;
color: var(--fg); color: var(--foreground-color);
} }
.settings_container .tab h3 { .settings_container .tab h3 {
font-size: 2rem; font-size: 2rem;
font-weight: bold; font-weight: bold;
color: var(--4); color: var(--color-four);
margin-top: 1.5rem; margin-top: 1.5rem;
text-transform: capitalize; text-transform: capitalize;
} }
@ -397,14 +397,14 @@ footer {
.settings_container .tab .description { .settings_container .tab .description {
font-size: 1.5rem; font-size: 1.5rem;
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
color: var(--fg); color: var(--foreground-color);
} }
.settings_container .user_interface select { .settings_container .user_interface select {
margin: 0.7rem 0; margin: 0.7rem 0;
width: 20rem; width: 20rem;
background-color: var(--bg); background-color: var(--background-color);
color: var(--fg); color: var(--foreground-color);
padding: 1rem 2rem; padding: 1rem 2rem;
border-radius: 0.5rem; border-radius: 0.5rem;
outline: none; outline: none;
@ -413,7 +413,7 @@ footer {
} }
.settings_container .user_interface option:hover { .settings_container .user_interface option:hover {
background-color: var(--1); background-color: var(--color-one);
} }
.settings_container .engines .engine_selection { .settings_container .engines .engine_selection {
@ -425,7 +425,7 @@ footer {
} }
.settings_container .engines .toggle_btn { .settings_container .engines .toggle_btn {
color: var(--fg); color: var(--foreground-color);
font-size: 1.5rem; font-size: 1.5rem;
display: flex; display: flex;
gap: 0.5rem; gap: 0.5rem;
@ -464,7 +464,7 @@ footer {
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
background-color: var(--bg); background-color: var(--background-color);
-webkit-transition: 0.4s; -webkit-transition: 0.4s;
transition: 0.4s; transition: 0.4s;
} }
@ -476,17 +476,17 @@ footer {
width: 2.6rem; width: 2.6rem;
left: 0.4rem; left: 0.4rem;
bottom: 0.4rem; bottom: 0.4rem;
background-color: var(--fg); background-color: var(--foreground-color);
-webkit-transition: 0.4s; -webkit-transition: 0.4s;
transition: 0.4s; transition: 0.4s;
} }
input:checked + .slider { input:checked + .slider {
background-color: var(--3); background-color: var(--color-three);
} }
input:focus + .slider { input:focus + .slider {
box-shadow: 0 0 1px var(--3); box-shadow: 0 0 1px var(--color-three);
} }
input:checked + .slider:before { input:checked + .slider:before {

View File

@ -4,12 +4,12 @@
<div class="results_aggregated"> <div class="results_aggregated">
{{#each results}} {{#each results}}
<div class="result"> <div class="result">
<h1><a href="{{this.visitingUrl}}">{{{this.title}}}</a></h1> <h1><a href="/{{{this.visitingUrl}}}">{{{this.title}}}</a></h1>
<small>{{this.url}}</small> <small>{{{this.url}}}</small>
<p>{{{this.description}}}</p> <p>{{{this.description}}}</p>
<div class="upstream_engines"> <div class="upstream_engines">
{{#each engine}} {{#each engine}}
<span>{{this}}</span> <span>{{{this}}}</span>
{{/each}} {{/each}}
</div> </div>
</div> </div>

View File

@ -5,7 +5,7 @@
use std::net::TcpListener; use std::net::TcpListener;
use websurfx::{config_parser::parser::Config, run}; use websurfx::{config::parser::Config, run};
/// The function that launches the main server and registers all the routes of the website. /// The function that launches the main server and registers all the routes of the website.
/// ///
@ -26,7 +26,7 @@ async fn main() -> std::io::Result<()> {
log::info!("started server on port {}", config.port); log::info!("started server on port {}", config.port);
let listener = TcpListener::bind((config.binding_ip_addr.clone(), config.port))?; let listener = TcpListener::bind((config.binding_ip.clone(), config.port))?;
run(listener, config)?.await run(listener, config)?.await
} }

8
src/cache/cacher.rs vendored
View File

@ -32,7 +32,7 @@ impl RedisCache {
/// # Arguments /// # Arguments
/// ///
/// * `url` - It takes an url as string. /// * `url` - It takes an url as string.
fn compute_url_hash(url: &str) -> String { fn hash_url(url: &str) -> String {
format!("{:?}", compute(url)) format!("{:?}", compute(url))
} }
@ -41,8 +41,8 @@ impl RedisCache {
/// # Arguments /// # Arguments
/// ///
/// * `url` - It takes an url as a string. /// * `url` - It takes an url as a string.
pub fn cached_results_json(&mut self, url: &str) -> Result<String, Box<dyn std::error::Error>> { pub fn cached_json(&mut self, url: &str) -> Result<String, Box<dyn std::error::Error>> {
let hashed_url_string = Self::compute_url_hash(url); let hashed_url_string = Self::hash_url(url);
Ok(self.connection.get(hashed_url_string)?) Ok(self.connection.get(hashed_url_string)?)
} }
@ -59,7 +59,7 @@ impl RedisCache {
json_results: String, json_results: String,
url: &str, url: &str,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
let hashed_url_string = Self::compute_url_hash(url); let hashed_url_string = Self::hash_url(url);
// put results_json into cache // put results_json into cache
self.connection.set(&hashed_url_string, json_results)?; self.connection.set(&hashed_url_string, json_results)?;

View File

@ -14,9 +14,9 @@ static CONFIG_FILE_NAME: &str = "config.lua";
/// # Fields /// # Fields
// //
/// * `port` - It stores the parsed port number option on which the server should launch. /// * `port` - It stores the parsed port number option on which the server should launch.
/// * `binding_ip_addr` - It stores the parsed ip address option on which the server should launch /// * `binding_ip` - It stores the parsed ip address option on which the server should launch
/// * `style` - It stores the theming options for the website. /// * `style` - It stores the theming options for the website.
/// * `redis_connection_url` - It stores the redis connection url address on which the redis /// * `redis_url` - It stores the redis connection url address on which the redis
/// client should connect. /// client should connect.
/// * `aggregator` - It stores the option to whether enable or disable production use. /// * `aggregator` - It stores the option to whether enable or disable production use.
/// * `logging` - It stores the option to whether enable or disable logs. /// * `logging` - It stores the option to whether enable or disable logs.
@ -25,10 +25,10 @@ static CONFIG_FILE_NAME: &str = "config.lua";
#[derive(Clone)] #[derive(Clone)]
pub struct Config { pub struct Config {
pub port: u16, pub port: u16,
pub binding_ip_addr: String, pub binding_ip: String,
pub style: Style, pub style: Style,
pub redis_connection_url: String, pub redis_url: String,
pub aggregator: AggreatorConfig, pub aggregator: AggregatorConfig,
pub logging: bool, pub logging: bool,
pub debug: bool, pub debug: bool,
pub upstream_search_engines: Vec<String>, pub upstream_search_engines: Vec<String>,
@ -41,47 +41,38 @@ pub struct Config {
/// * `random_delay` - It stores the option to whether enable or disable random delays between /// * `random_delay` - It stores the option to whether enable or disable random delays between
/// requests. /// requests.
#[derive(Clone)] #[derive(Clone)]
pub struct AggreatorConfig { pub struct AggregatorConfig {
pub random_delay: bool, pub random_delay: bool,
} }
impl Config { impl Config {
/// A function which parses the config.lua file and puts all the parsed options in the newly /// A function which parses the config.lua file and puts all the parsed options in the newly
/// contructed Config struct and returns it. /// constructed Config struct and returns it.
/// ///
/// # Error /// # Error
/// ///
/// Returns a lua parse error if parsing of the config.lua file fails or has a syntax error /// Returns a lua parse error if parsing of the config.lua file fails or has a syntax error
/// or io error if the config.lua file doesn't exists otherwise it returns a newly contructed /// or io error if the config.lua file doesn't exists otherwise it returns a newly constructed
/// Config struct with all the parsed config options from the parsed config file. /// Config struct with all the parsed config options from the parsed config file.
pub fn parse() -> Result<Self, Box<dyn std::error::Error>> { pub fn parse() -> Result<Self, Box<dyn std::error::Error>> {
Lua::new().context(|context| -> Result<Self, Box<dyn std::error::Error>> { Lua::new().context(|context| -> Result<Self, Box<dyn std::error::Error>> {
let globals = context.globals(); let globals = context.globals();
context context
.load(&fs::read_to_string( .load(&fs::read_to_string(Config::config_path()?)?)
Config::handle_different_config_file_path()?,
)?)
.exec()?; .exec()?;
let production_use = globals.get::<_, bool>("production_use")?;
let aggregator_config = if production_use {
AggreatorConfig { random_delay: true }
} else {
AggreatorConfig {
random_delay: false,
}
};
Ok(Config { Ok(Config {
port: globals.get::<_, u16>("port")?, port: globals.get::<_, u16>("port")?,
binding_ip_addr: globals.get::<_, String>("binding_ip_addr")?, binding_ip: globals.get::<_, String>("binding_ip")?,
style: Style::new( style: Style::new(
globals.get::<_, String>("theme")?, globals.get::<_, String>("theme")?,
globals.get::<_, String>("colorscheme")?, globals.get::<_, String>("colorscheme")?,
), ),
redis_connection_url: globals.get::<_, String>("redis_connection_url")?, redis_url: globals.get::<_, String>("redis_url")?,
aggregator: aggregator_config, aggregator: AggregatorConfig {
random_delay: globals.get::<_, bool>("production_use")?,
},
logging: globals.get::<_, bool>("logging")?, logging: globals.get::<_, bool>("logging")?,
debug: globals.get::<_, bool>("debug")?, debug: globals.get::<_, bool>("debug")?,
upstream_search_engines: globals upstream_search_engines: globals
@ -104,35 +95,37 @@ impl Config {
/// one (3). /// one (3).
/// 3. `websurfx/` (under project folder ( or codebase in other words)) if it is not present /// 3. `websurfx/` (under project folder ( or codebase in other words)) if it is not present
/// here then it returns an error as mentioned above. /// here then it returns an error as mentioned above.
fn handle_different_config_file_path() -> Result<String, Box<dyn std::error::Error>> { fn config_path() -> Result<String, Box<dyn std::error::Error>> {
if Path::new( // check user config
format!(
"{}/.config/{}/config.lua", let path = format!(
std::env::var("HOME").unwrap(), "{}/.config/{}/config.lua",
COMMON_DIRECTORY_NAME std::env::var("HOME").unwrap(),
) COMMON_DIRECTORY_NAME
.as_str(), );
) if Path::new(path.as_str()).exists() {
.exists() return Ok(format!(
{
Ok(format!(
"{}/.config/{}/{}", "{}/.config/{}/{}",
std::env::var("HOME").unwrap(), std::env::var("HOME").unwrap(),
COMMON_DIRECTORY_NAME, COMMON_DIRECTORY_NAME,
CONFIG_FILE_NAME CONFIG_FILE_NAME
)) ));
} else if Path::new( }
format!("/etc/xdg/{}/{}", COMMON_DIRECTORY_NAME, CONFIG_FILE_NAME).as_str(),
) // look for config in /etc/xdg
.exists() if Path::new(format!("/etc/xdg/{}/{}", COMMON_DIRECTORY_NAME, CONFIG_FILE_NAME).as_str())
{
Ok("/etc/xdg/websurfx/config.lua".to_string())
} else if Path::new(format!("./{}/{}", COMMON_DIRECTORY_NAME, CONFIG_FILE_NAME).as_str())
.exists() .exists()
{ {
Ok("./websurfx/config.lua".to_string()) return Ok("/etc/xdg/websurfx/config.lua".to_string());
} else {
Err("Config file not found!!".to_string().into())
} }
// use dev config
if Path::new(format!("./{}/{}", COMMON_DIRECTORY_NAME, CONFIG_FILE_NAME).as_str()).exists()
{
return Ok("./websurfx/config.lua".to_string());
}
// if no of the configs above exist, return error
Err("Config file not found!!".to_string().into())
} }
} }

View File

@ -1,5 +1,5 @@
//! This module provides public models for handling, storing and serializing parsed config file //! This module provides public models for handling, storing and serializing parsed config file
//! options from config.lua by grouping them togather. //! options from config.lua by grouping them together.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

View File

@ -7,7 +7,7 @@ use std::collections::HashMap;
use reqwest::header::{HeaderMap, CONTENT_TYPE, COOKIE, REFERER, USER_AGENT}; use reqwest::header::{HeaderMap, CONTENT_TYPE, COOKIE, REFERER, USER_AGENT};
use scraper::{Html, Selector}; use scraper::{Html, Selector};
use crate::search_results_handler::aggregation_models::RawSearchResult; use crate::results::aggregation_models::RawSearchResult;
use super::engine_models::{EngineError, SearchEngine}; use super::engine_models::{EngineError, SearchEngine};

View File

@ -1,7 +1,7 @@
//! This module provides the error enum to handle different errors associated while requesting data from //! This module provides the error enum to handle different errors associated while requesting data from
//! the upstream search engines with the search query provided by the user. //! the upstream search engines with the search query provided by the user.
use crate::search_results_handler::aggregation_models::RawSearchResult; use crate::results::aggregation_models::RawSearchResult;
use error_stack::{IntoReport, Result, ResultExt}; use error_stack::{IntoReport, Result, ResultExt};
use std::{collections::HashMap, fmt, time::Duration}; use std::{collections::HashMap, fmt, time::Duration};
@ -14,7 +14,7 @@ use std::{collections::HashMap, fmt, time::Duration};
/// search engines. /// search engines.
/// * `UnexpectedError` - This variant handles all the errors which are unexpected or occur rarely /// * `UnexpectedError` - This variant handles all the errors which are unexpected or occur rarely
/// and are errors mostly related to failure in initialization of HeaderMap, Selector errors and /// and are errors mostly related to failure in initialization of HeaderMap, Selector errors and
/// all other errors occuring within the code handling the `upstream search engines`. /// all other errors occurring within the code handling the `upstream search engines`.
#[derive(Debug)] #[derive(Debug)]
pub enum EngineError { pub enum EngineError {
EmptyResultSet, EmptyResultSet,

View File

@ -6,7 +6,7 @@ use reqwest::header::{HeaderMap, CONTENT_TYPE, COOKIE, REFERER, USER_AGENT};
use scraper::{Html, Selector}; use scraper::{Html, Selector};
use std::collections::HashMap; use std::collections::HashMap;
use crate::search_results_handler::aggregation_models::RawSearchResult; use crate::results::aggregation_models::RawSearchResult;
use super::engine_models::{EngineError, SearchEngine}; use super::engine_models::{EngineError, SearchEngine};
use error_stack::{IntoReport, Report, Result, ResultExt}; use error_stack::{IntoReport, Report, Result, ResultExt};

View File

@ -1 +1 @@
pub mod public_path_handler; pub mod public_paths;

View File

@ -17,15 +17,17 @@ static PUBLIC_DIRECTORY_NAME: &str = "public";
/// 1. `/opt/websurfx` if it not present here then it fallbacks to the next one (2) /// 1. `/opt/websurfx` if it not present here then it fallbacks to the next one (2)
/// 2. Under project folder ( or codebase in other words) if it is not present /// 2. Under project folder ( or codebase in other words) if it is not present
/// here then it returns an error as mentioned above. /// here then it returns an error as mentioned above.
pub fn handle_different_public_path() -> Result<String, Error> { pub fn public_path() -> Result<String, Error> {
if Path::new(format!("/opt/websurfx/{}/", PUBLIC_DIRECTORY_NAME).as_str()).exists() { if Path::new(format!("/opt/websurfx/{}/", PUBLIC_DIRECTORY_NAME).as_str()).exists() {
Ok(format!("/opt/websurfx/{}", PUBLIC_DIRECTORY_NAME)) return Ok(format!("/opt/websurfx/{}", PUBLIC_DIRECTORY_NAME));
} else if Path::new(format!("./{}/", PUBLIC_DIRECTORY_NAME).as_str()).exists() {
Ok(format!("./{}", PUBLIC_DIRECTORY_NAME))
} else {
Err(Error::new(
std::io::ErrorKind::NotFound,
"Themes (public) folder not found!!",
))
} }
if Path::new(format!("./{}/", PUBLIC_DIRECTORY_NAME).as_str()).exists() {
return Ok(format!("./{}", PUBLIC_DIRECTORY_NAME));
}
Err(Error::new(
std::io::ErrorKind::NotFound,
"Themes (public) folder not found!!",
))
} }

View File

@ -2,10 +2,10 @@
//! and register all the routes for the `websurfx` meta search engine website. //! and register all the routes for the `websurfx` meta search engine website.
pub mod cache; pub mod cache;
pub mod config_parser; pub mod config;
pub mod engines; pub mod engines;
pub mod handler; pub mod handler;
pub mod search_results_handler; pub mod results;
pub mod server; pub mod server;
use std::net::TcpListener; use std::net::TcpListener;
@ -14,9 +14,9 @@ use crate::server::routes;
use actix_files as fs; use actix_files as fs;
use actix_web::{dev::Server, middleware::Logger, web, App, HttpServer}; use actix_web::{dev::Server, middleware::Logger, web, App, HttpServer};
use config_parser::parser::Config; use config::parser::Config;
use handlebars::Handlebars; use handlebars::Handlebars;
use handler::public_path_handler::handle_different_public_path; use handler::public_paths::public_path;
/// Runs the web server on the provided TCP listener and returns a `Server` instance. /// Runs the web server on the provided TCP listener and returns a `Server` instance.
/// ///
@ -32,7 +32,7 @@ use handler::public_path_handler::handle_different_public_path;
/// ///
/// ```rust /// ```rust
/// use std::net::TcpListener; /// use std::net::TcpListener;
/// use websurfx::{config_parser::parser::Config, run}; /// use websurfx::{config::parser::Config, run};
/// ///
/// let config = Config::parse().unwrap(); /// let config = Config::parse().unwrap();
/// let listener = TcpListener::bind("127.0.0.1:8080").expect("Failed to bind address"); /// let listener = TcpListener::bind("127.0.0.1:8080").expect("Failed to bind address");
@ -41,7 +41,7 @@ use handler::public_path_handler::handle_different_public_path;
pub fn run(listener: TcpListener, config: Config) -> std::io::Result<Server> { pub fn run(listener: TcpListener, config: Config) -> std::io::Result<Server> {
let mut handlebars: Handlebars = Handlebars::new(); let mut handlebars: Handlebars = Handlebars::new();
let public_folder_path: String = handle_different_public_path()?; let public_folder_path: String = public_path()?;
handlebars handlebars
.register_templates_directory(".html", format!("{}/templates", public_folder_path)) .register_templates_directory(".html", format!("{}/templates", public_folder_path))

View File

@ -3,7 +3,7 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{config_parser::parser_models::Style, engines::engine_models::EngineError}; use crate::{config::parser_models::Style, engines::engine_models::EngineError};
/// A named struct to store, serialize and deserializes the individual search result from all the /// A named struct to store, serialize and deserializes the individual search result from all the
/// scraped and aggregated search results from the upstream search engines. /// scraped and aggregated search results from the upstream search engines.

View File

@ -1,14 +1,14 @@
//! This module provides the functionality to handle different routes of the `websurfx` //! This module provides the functionality to handle different routes of the `websurfx`
//! meta search engine website and provide approriate response to each route/page //! meta search engine website and provide appropriate response to each route/page
//! when requested. //! when requested.
use std::fs::read_to_string; use std::fs::read_to_string;
use crate::{ use crate::{
cache::cacher::RedisCache, cache::cacher::RedisCache,
config_parser::parser::Config, config::parser::Config,
handler::public_path_handler::handle_different_public_path, handler::public_paths::public_path,
search_results_handler::{aggregation_models::SearchResults, aggregator::aggregate}, results::{aggregation_models::SearchResults, aggregator::aggregate},
}; };
use actix_web::{get, web, HttpRequest, HttpResponse}; use actix_web::{get, web, HttpRequest, HttpResponse};
use handlebars::Handlebars; use handlebars::Handlebars;
@ -87,86 +87,25 @@ pub async fn search(
config: web::Data<Config>, config: web::Data<Config>,
) -> Result<HttpResponse, Box<dyn std::error::Error>> { ) -> Result<HttpResponse, Box<dyn std::error::Error>> {
let params = web::Query::<SearchParams>::from_query(req.query_string())?; let params = web::Query::<SearchParams>::from_query(req.query_string())?;
//Initialize redis cache connection struct
let mut redis_cache = RedisCache::new(config.redis_connection_url.clone())?;
match &params.q { match &params.q {
Some(query) => { Some(query) => {
if query.trim().is_empty() { if query.trim().is_empty() {
Ok(HttpResponse::Found() return Ok(HttpResponse::Found()
.insert_header(("location", "/")) .insert_header(("location", "/"))
.finish()) .finish());
} else {
let page_url: String; // Declare the page_url variable without initializing it
// ...
let page = match params.page {
Some(page_number) => {
if page_number <= 1 {
page_url = format!(
"http://{}:{}/search?q={}&page={}",
config.binding_ip_addr, config.port, query, 1
);
1
} else {
page_url = format!(
"http://{}:{}/search?q={}&page={}",
config.binding_ip_addr, config.port, query, page_number
);
page_number
}
}
None => {
page_url = format!(
"http://{}:{}{}&page={}",
config.binding_ip_addr,
config.port,
req.uri(),
1
);
1
}
};
// fetch the cached results json.
let cached_results_json = redis_cache.cached_results_json(&page_url);
// check if fetched catch results was indeed fetched or it was an error and if so
// handle the data accordingly.
match cached_results_json {
Ok(results_json) => {
let new_results_json: SearchResults = serde_json::from_str(&results_json)?;
let page_content: String = hbs.render("search", &new_results_json)?;
Ok(HttpResponse::Ok().body(page_content))
}
Err(_) => {
// check if the cookie value is empty or not if it is empty then use the
// default selected upstream search engines from the config file otherwise
// parse the non-empty cookie and grab the user selected engines from the
// UI and use that.
let mut results_json: crate::search_results_handler::aggregation_models::SearchResults = match req.cookie("appCookie") {
Some(cookie_value) => {
let cookie_value:Cookie = serde_json::from_str(cookie_value.name_value().1)?;
aggregate(query.clone(), page, config.aggregator.random_delay, config.debug, cookie_value.engines).await?
},
None => aggregate(query.clone(), page, config.aggregator.random_delay, config.debug, config.upstream_search_engines.clone()).await?,
};
results_json.add_style(config.style.clone());
// check whether the results grabbed from the upstream engines are empty or
// not if they are empty then set the empty_result_set option to true in
// the result json.
if results_json.is_empty_result_set() {
results_json.set_empty_result_set();
}
redis_cache
.cache_results(serde_json::to_string(&results_json)?, &page_url)?;
let page_content: String = hbs.render("search", &results_json)?;
Ok(HttpResponse::Ok().body(page_content))
}
}
} }
let page = match &params.page {
Some(page) => *page,
None => 0,
};
let url = format!(
"http://{}:{}/search?q={}&page={}",
config.binding_ip, config.port, query, page
);
let results_json = results(url, &config, query.to_string(), page, req).await?;
let page_content: String = hbs.render("search", &results_json)?;
Ok(HttpResponse::Ok().body(page_content))
} }
None => Ok(HttpResponse::Found() None => Ok(HttpResponse::Found()
.insert_header(("location", "/")) .insert_header(("location", "/"))
@ -174,11 +113,70 @@ pub async fn search(
} }
} }
/// Fetches the results for a query and page.
/// First checks the redis cache, if that fails it gets proper results
async fn results(
url: String,
config: &Config,
query: String,
page: u32,
req: HttpRequest,
) -> Result<SearchResults, Box<dyn std::error::Error>> {
//Initialize redis cache connection struct
let mut redis_cache = RedisCache::new(config.redis_url.clone())?;
// fetch the cached results json.
let cached_results_json = redis_cache.cached_json(&url);
// check if fetched catch results was indeed fetched or it was an error and if so
// handle the data accordingly.
match cached_results_json {
Ok(results_json) => Ok(serde_json::from_str::<SearchResults>(&results_json).unwrap()),
Err(_) => {
// check if the cookie value is empty or not if it is empty then use the
// default selected upstream search engines from the config file otherwise
// parse the non-empty cookie and grab the user selected engines from the
// UI and use that.
let mut results_json: crate::results::aggregation_models::SearchResults = match req
.cookie("appCookie")
{
Some(cookie_value) => {
let cookie_value: Cookie = serde_json::from_str(cookie_value.name_value().1)?;
aggregate(
query,
page,
config.aggregator.random_delay,
config.debug,
cookie_value.engines,
)
.await?
}
None => {
aggregate(
query,
page,
config.aggregator.random_delay,
config.debug,
config.upstream_search_engines.clone(),
)
.await?
}
};
results_json.add_style(config.style.clone());
// check whether the results grabbed from the upstream engines are empty or
// not if they are empty then set the empty_result_set option to true in
// the result json.
if results_json.is_empty_result_set() {
results_json.set_empty_result_set();
}
redis_cache.cache_results(serde_json::to_string(&results_json)?, &url)?;
Ok(results_json)
}
}
}
/// Handles the route of robots.txt page of the `websurfx` meta search engine website. /// Handles the route of robots.txt page of the `websurfx` meta search engine website.
#[get("/robots.txt")] #[get("/robots.txt")]
pub async fn robots_data(_req: HttpRequest) -> Result<HttpResponse, Box<dyn std::error::Error>> { pub async fn robots_data(_req: HttpRequest) -> Result<HttpResponse, Box<dyn std::error::Error>> {
let page_content: String = let page_content: String = read_to_string(format!("{}/robots.txt", public_path()?))?;
read_to_string(format!("{}/robots.txt", handle_different_public_path()?))?;
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
.content_type("text/plain; charset=ascii") .content_type("text/plain; charset=ascii")
.body(page_content)) .body(page_content))

View File

@ -1,7 +1,7 @@
use std::net::TcpListener; use std::net::TcpListener;
use handlebars::Handlebars; use handlebars::Handlebars;
use websurfx::{config_parser::parser::Config, run}; use websurfx::{config::parser::Config, run};
// Starts a new instance of the HTTP server, bound to a random available port // Starts a new instance of the HTTP server, bound to a random available port
fn spawn_app() -> String { fn spawn_app() -> String {
@ -41,5 +41,5 @@ async fn test_index() {
assert_eq!(res.text().await.unwrap(), template); assert_eq!(res.text().await.unwrap(), template);
} }
// TODO: Write tests for tesing parameters for search function that if provided with something // TODO: Write tests for testing parameters for search function that if provided with something
// other than u32 like alphabets and special characters than it should panic // other than u32 like alphabets and special characters than it should panic

View File

@ -4,7 +4,7 @@ debug = false -- an option to enable or disable debug mode.
-- ### Server ### -- ### Server ###
port = "8080" -- port on which server should be launched port = "8080" -- port on which server should be launched
binding_ip_addr = "127.0.0.1" --ip address on the which server should be launched. binding_ip = "127.0.0.1" --ip address on the which server should be launched.
production_use = false -- whether to use production mode or not (in other words this option should be used if it is to be used to host it on the server to provide a service to a large number of users (more than one)) production_use = false -- whether to use production mode or not (in other words this option should be used if it is to be used to host it on the server to provide a service to a large number of users (more than one))
-- if production_use is set to true -- if production_use is set to true
-- There will be a random delay before sending the request to the search engines, this is to prevent DDoSing the upstream search engines from a large number of simultaneous requests. -- There will be a random delay before sending the request to the search engines, this is to prevent DDoSing the upstream search engines from a large number of simultaneous requests.
@ -25,7 +25,7 @@ colorscheme = "catppuccin-mocha" -- the colorscheme name which should be used fo
theme = "simple" -- the theme name which should be used for the website theme = "simple" -- the theme name which should be used for the website
-- ### Caching ### -- ### Caching ###
redis_connection_url = "redis://127.0.0.1:8082" -- redis connection url address on which the client should connect on. redis_url = "redis://127.0.0.1:8082" -- redis connection url address on which the client should connect on.
-- ### Search Engines ### -- ### Search Engines ###
upstream_search_engines = { DuckDuckGo = true, Searx = false } -- select the upstream search engines from which the results should be fetched. upstream_search_engines = { DuckDuckGo = true, Searx = false } -- select the upstream search engines from which the results should be fetched.